Как заархивировать вектор вектора с помощью range-v3

(Это продолжение Суммировать вектор с диапазоном-v3)

Если у меня есть два (или более) вектора, я могу zip их вместе с range-v3 вот так:

std::vector< int > v1{1,1,1};
std::vector< int > v2{2,2,2};

auto v = ranges::views::zip( v1, v2 )
  | ranges::views::transform( ... );

Это хорошо работает, но на практике у меня нет явных векторов, но есть вектор векторов. Я хотел бы сделать следующее, но это не дает того же результата. (На самом деле, я не уверен, каков результат, и я не знаю, как определить, каков результат!)


std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = ranges::views::zip( V )
  | ranges::views::transform( ... );

Что я могу сделать, чтобы заархивировать vector< vector > так же, как я заархивировал несколько явных векторов? Я пытался использовать join вместе с stride, chunk и т. д., но не нашел волшебной комбинации.


person jlconlin    schedule 15.05.2020    source источник
comment
Кстати, если вы хотите заархивировать поля внешнего vector, вероятно, это должен быть не вектор, а array или tuple   -  person bartop    schedule 15.05.2020
comment
Проблема в том, что я не знаю, сколько подвекторов будет во время компиляции. Я создаю свой вектор (возможно, неэффективно), используя push_back каждый раз, когда создается новый подвектор.   -  person jlconlin    schedule 15.05.2020
comment
тогда нет простого ответа на вашу проблему, так как zip должен знать внешний размер vector при компиляции   -  person bartop    schedule 15.05.2020


Ответы (3)


Я полагаю, что если вы не знаете размер внешнего vector во время компиляции, единственное разумное решение, которое остается, - это обойти его. Вместо того, чтобы пытаться использовать zip, я бы предложил использовать accumulate вдобавок к этому, так как в этой ситуации он кажется более универсальным.

std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = ranges::accumulate(
    V,
    std::vector<ResultType>(V[0].size()),
    [](const auto& acc, const auto& next) {
        auto range = ranges::views::zip(acc, next) | ranges::views::transform(...);
        return std::vector<int>(range.begin(), range.end());
    }
)

РЕДАКТИРОВАТЬ: я забыл, что диапазон должен быть скопирован.

person bartop    schedule 15.05.2020
comment
Спасибо за это предложение. Мне нравится эта идея, но я изо всех сил пытаюсь реализовать ее. Я не так хорошо знаком с accumulate, поэтому не знаю, в чем проблема. - person jlconlin; 15.05.2020
comment
@jlconlin Кстати, взгляните также на views::transpose, возможно, он подойдет вам даже больше. - person bartop; 15.05.2020
comment
О, хороший момент. Я не думал об этом. Мне нравится библиотека range-v3, но там столько всего, что я просто даже не знаю, что спросить. Вошло ли транспонирование в стандарт C++20? - person jlconlin; 15.05.2020
comment
@jlconlin, честно говоря, не знаю, вам нужно просто проверить последний черновик - person bartop; 15.05.2020
comment
@bartop Я не думаю, что в диапазоне-v3 есть views::transpose. Не могли бы вы указать мне на это? - person cigien; 15.05.2020

ranges::views::zip( V ) заархивирует только один вектор, а не его содержимое. (аналогично std::make_tuple(v), который делает std::tuple<vector<int>>).

Проблема в том, что вектор имеет размер времени выполнения, поэтому для создания tuple из его содержимого требуется некоторая помощь:

template <std::size_t ...Is, typename T>
auto zip_vector(std::index_sequence<Is...>, std::vector<std::vector<T>>& v)
{
    assert(N <= v.size());
    return ranges::views::zip(v[Is]...);
}

template <std::size_t N, typename T>
auto zip_vector(std::vector<std::vector<T>>& v)
{
    return zip_vector(std::make_index_sequence<N>(), v);
}

А потом:

std::vector< std::vector< int > > V{{1,1,1},{2,2,2}};

auto vV = zip_vector<2>( V )
  | ranges::views::transform( ... );
person Jarod42    schedule 15.05.2020

Основываясь на вопросе, на который вы ссылались, я думаю, что этот конкретный вопрос представляет собой проблему XY, т. Е. Нет причин привлекать zip для решения этой проблемы, кроме как опираться на предыдущее решение. Хотя zip мог бы быть разумным подходом, когда количество диапазонов было известно во время компиляции, он просто мешает, когда число известно только во время выполнения.

Итак, учитывая, что у вас есть vector<vector<int>>, где, как и в предыдущем вопросе, все внутренние векторы имеют одинаковый размер, вот как я бы написал это в диапазоне-v3:

namespace rv = ranges::views;  

std::vector<std::vector<int>> v{{1,2,3},{4,5,6}};

int n = v.size();
int k = v[0].size();

auto vs = v | rv::join;

auto s = rv::iota(0, n + 1) 
       | rv::transform([=](int i){
           return ranges::accumulate(
                    vs | rv::drop(i) | rv::stride(k), 0);
         });

Вот демонстрация.

person cigien    schedule 15.05.2020