ranges::views::group_by-подобная функция, применяющая предикат к последовательным элементам?

В следующем небольшом примере я пытался сгруппировать элементы по разнице между последовательными элементами, равной 1. Однако, как показывает вывод, group_byПредикат оценивается между текущим элементом и первым элементом обрабатываемой группы.

      #include <iostream>
#include <range/v3/view/group_by.hpp>

int main() {
    using ranges::views::group_by;
    std::vector<int> v{1,2,3,6,7,8,9,12,14,15};
    for (auto i : v | group_by([](auto x2,auto x1){ return x2 - x1 == 1; })) {
        std::cout << i << '\n';
    }
}

Предлагает ли Range-v3 способ оценки предиката между последовательными элементами группы?

Однажды я задал тот же вопрос для Haskell, и оказалось, что одноименная функция есть на Hackage.

2 ответа

group_byустарел в пользуchunk_by, который делает именно то, что вы хотите:

Учитывая исходный диапазон и двоичный предикат, вернуть диапазон диапазонов, где каждый диапазон содержит непрерывные элементы из исходного диапазона, так что выполняется следующее условие: для каждого элемента в диапазоне, кроме первого, когда этот элемент и предыдущий элемент передается бинарному предикату, результат истинен. По сути,views::chunk_byгруппирует смежные элементы вместе с бинарным предикатом.

      using ranges::views::chunk_by;
std::vector<int> v{1,2,3,6,7,8,9,12,14,15};
for (auto i : v | chunk_by([](auto l, auto r){ return r - l == 1; })) {
    std::cout << i << '\n';
}

Отпечатки

      [1,2,3]
[6,7,8,9]
[12]
[14,15]

Это уродливый результат моей попытки собрать то, что уже есть в Range-v3, чтобы получить то, что я хотел.

      #include <iostream>
#include <range/v3/view/group_by.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/iota.hpp>
#include <range/v3/view/concat.hpp>
#include <range/v3/view/zip_with.hpp>

constexpr auto elem_and_distance_from_previous
    = [](int x, int y){ return std::make_pair(x,x - y); };
constexpr auto second_is_1 = [](auto,auto x){ return x.second == 1; };
constexpr auto get_first = [](auto x){ return x.first; };

using namespace ranges::views;
int main() {
    std::vector<int> v{1,2,3,6,7,8,9,12,14,15};
    auto w = zip_with(elem_and_distance_from_previous,v,concat(iota(0,1),v))
           | group_by(second_is_1)
           | transform([](auto x){ return x | transform(get_first); });
    std::cout << w << '\n';
}
// outputs [[1,2,3],[6,7,8,9],[12],[14,15]]

Вероятно, как предложил @einpoklum в комментарии, копия-вставка-редактирование оригинала group_byбыло бы намного лучше. Однако я задал этот вопрос, потому что хотел знать, есть ли уже в Range-v3 способ сделать то, что я имел в виду.

Другие вопросы по тегам