3

Can this double loop be rewritten using ranges views split() ?

#include <vector>
#include <span>

struct MyPair
{
    int a;
    char b;
};


vector<MyPair> path = {{1,'a'},{1,'z'},{2,'b'},{2,'y'}};
vector<span<MyPair> > spans;

for (int i=0; i < path.size();)
{
    auto r = path | ranges::views::drop(i) | views::take_while([&](const MyPair& p){return p.a == path[i].a;});
    int size = ranges::distance(r);
    span<Range> ranges(&path[i], size);
    spans.push_back(ranges);
    i += size ;
}

I want a view of views looking like

{{{1,'a'},{1,'z'}},{{2,'b'},{2,'y'}}}
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
Ludovic Aubert
  • 9,534
  • 4
  • 16
  • 28

2 Answers2

2

Can this double loop be rewritten using ranges views split() ?

Since you're not using a range as a delimiter to split the original range, instead you're using a predicate to split the range, views::split doesn't actually solve the problem.

However, C++23 adopted views::chunk_by, and according to its description in [range.chunk.by.overview]:

chunk_by_view takes a view and a predicate, and splits the view into subranges between each pair of adjacent elements for which the predicate returns false.

The for-loop can be rewritten using views::chunk_by with the appropriate predicate:

vector<MyPair> path = ...
auto spans = path | std::views::chunk_by([](const auto& l, const auto& r) {
                                           return l.a == r.a; 
                                         });

But currently, no compiler implements this range adaptor, the alternative is to use range-v3's views::group_by.

Demo

It is worth noting that ranges::views::group_by is not functionally equivalent to std::views::chunk_by: the former always compares the first element in each subrange with each subsequent one, while the latter always compare consecutive elements.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
1

In the absence of views::chunk_by, here is my code, using a good old for loop:

#include <vector>
#include <span>

struct MyPair
{
    int a;
    char b;
};

vector<MyPair> path = {{1,'a'},{1,'z'},{2,'b'},{2,'y'}};

vector<span<MyPair> > spans;

int i_prev=0;
for (int i=0;i < path.size(); i++)
{
    if (path[i].a != path[i_prev].a)
    {
        spans.push_back(span<MyPair>(&path[i_prev], i - i_prev));
                i_prev=i ;
    }
}
spans.push_back(span<MyPair>(&path[i_prev], path.size() - i_prev));
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
Ludovic Aubert
  • 9,534
  • 4
  • 16
  • 28