диапазоны диапазонов в вектор векторов

Предположим, у меня есть диапазон T, называемый rng. я могу сделать

auto groups = ranges::view::group_by(rng, bin_op);

группы теперь представляют собой диапазон диапазонов Т.

я тоже могу это сделать

auto groups = ranges::view::group_by(rng, bin_op) | ranges::to_vector;

чтобы получить вектор диапазонов Т. Однако это

auto groups = ranges::view::group_by(rng, bin_op)
            | ranges::to_vector
            | ranges::action::transform([] (auto r) { return r | ranges::to_vector; };

так же как

auto groups = ranges::view::group_by(rng, bin_op)
            | ranges::to_vector
            | ranges::action::transform([] (auto r) { return std::vector<T>{} | ranges::action::push_back; };

не будет работать, поскольку очевидно, что ranges::action::transform() возвращает void в этом случае, и «Тип результата функции, переданной action::transform, должен быть доступен для записи обратно в исходный диапазон».

Итак, как мне превратить мои диапазоны диапазонов в вектор векторов?

Примечание. Извините за неверные теги, но я не смог найти тег ranges/ranges-ts/ranges-v3, мне не разрешено его создавать и я не мог использовать его в заголовке.


person Richard Vock    schedule 20.07.2017    source источник


Ответы (2)


Предполагая, что вы используете Rangesv3, мое чтение документации дает мне что-то вроде этого:

auto groups = ranges::view::group_by(rng, bin_op)
        | ranges::view::transform( ranges::to_vector )
        | ranges::to_vector;

или, может быть

auto groups = ranges::view::group_by(rng, bin_op)
        | ranges::view::transform( [] (auto r) { return r | ranges::to_vector; } )
        | ranges::to_vector;

(Я помню, что ranges::to_vector можно было бы использовать в стиле функций, но я мог ошибаться, или что-то могло измениться. Первый предполагает, что это возможно, второй — нет.)

Что это делает, так это сначала преобразует ленивый диапазон диапазонов в ленивый диапазон векторов.

Затем он преобразует ленивый диапазон векторов в вектор векторов.

Это работает лучше (наизнанку), потому что промежуточные продукты ленивы «снаружи». Может быть способ сделать это извне внутрь, но вектор ленивых диапазонов должен реально существовать так, как ленивый диапазон векторов не существует.

person Yakk - Adam Nevraumont    schedule 20.07.2017
comment
Действительно это работает. Таким образом, идея состоит в том, чтобы работать изнутри наружу, а не снаружи внутрь. Любая идея, почему другое направление не будет работать? - person Richard Vock; 20.07.2017
comment
@rich, может быть, и мог бы, но это было бы неэффективно, потому что векторы существуют, а диапазоны ленивы. У вас может быть промежуточный ленивый диапазон векторов с нулевой стоимостью, но промежуточный вектор ленивых диапазонов должен существовать и иметь стоимость. - person Yakk - Adam Nevraumont; 20.07.2017
comment
Это имеет смысл. Немного смущает, что мой мозг на Haskell этого не заметил... Спасибо за помощь. - person Richard Vock; 20.07.2017

Вы можете использовать ranges::to для преобразования диапазона диапазонов в вектор векторов. Например:

#include <vector>
#include <iostream>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/all.hpp>
#include <range/v3/view/group_by.hpp>
#include <range/v3/view/transform.hpp>

int main() {
    std::vector<int> rng {0,1,2,3,4,5,6,7,8,9};
    auto groups = ranges::view::group_by(rng, [](int i, int j){
        return j/3 == i/3;
    });

    auto vs = groups | ranges::to<std::vector<std::vector<int>>>;

    // Display the result: [[0,1,2],[3,4,5],[6,7,8],[9]]
    std::cout << ranges::view::transform(vs, ranges::view::all) << std::endl;
}

10 июня 2020 г .: Ранее в этом ответе рекомендовалось просто назначать переменную groups переменной vector<vector<int>>, поскольку range-v3 использовался для поддержки неявных преобразований из представлений в контейнеры. Неявные преобразования были проблематичными, поэтому от них отказались. ranges::to теперь идиоматический способ сделать это.

person Eric Niebler    schedule 20.07.2017
comment
Ух ты, это намного проще, чем ожидалось ;) Я бы очень хотел, чтобы документация была более подробной - так много вещей, которые я не понимаю... (ожидайте вопрос о std::multimap в ближайшем будущем) - person Richard Vock; 21.07.2017
comment
Я больше не могу преобразовать вид в контейнер с помощью присваивания. Каков на данный момент идиоматический способ создания нового объекта-контейнера из представления? Скажем, для более простого примера, просто std::vector<int> vi{1, 2}; преобразуется в представление long int, vi | views::transform(convert_to<long int>{}), из которого я хотел бы создать объект std::vector<long int>. - person dfrib; 10.06.2020
comment
Неявные преобразования представлений в контейнеры были удалены, поскольку они были проблематичными. ranges::to это правильный способ сделать это. Я обновил ответ. Спасибо. - person Eric Niebler; 10.06.2020