Python's itertools
has the islice(seq, start, stop, step)
procedure that takes a sequence and returns an iterator of every step
th value of the sequence values between start
and stop
.
Does the Ranges library of C++20 provide a similar functionality, e.g. a function like slice
that takes a random access iterator start
, a sentinel stop
, and a step value step
, and that returns a random access iterator that iterates over every step
th value between start
and stop
?
In case it does not, can such an iterator adapter be implemented using the primitives provided by the Ranges library?
(I know how I can implement such an adapter by hand, so this is not the question.)
Not quite.
C++20 will have view::iota
which gives you a sequence from a starting value to a sentinel. However, it does not have the stride functionality. It only increments (via ++
).
However, you can combine it with range-v3's view::stride
to add in the steps. That is:
auto evens = view::iota(0, 100) | view::stride(2); // [0, 2, 4, 6, ... ]
For existing ranges, there's view::slice
, which also doesn't take a stride. But these are orthogonal and layer nicely:
auto even_teens = view::iota(0, 100)
| view::slice(10, 20)
| view::stride(2); // [10, 12, 14, 16, 18]
Unfortunaltely, slice
and stride
of Range-v3, as presented in Barry'sanswer, are not (yet) available in the Ranges library of C++20.
However, you can replace slice
by combining std::views::drop_while
and std::views::take_while
. To replace stride
, you can use the range adaptor std::views::filter
and pass a specific lambda expression to it. To filter for every other element as in Barry's example, I would use a stateful lambda expression with an init capture. You can put everything together to represent the range [10, 12, 14, 16, 18]
as follows:
auto even_teens = std::views::iota(0, 100)
| std::views::drop_while([](int i) { return i < 10; })
| std::views::take_while([](int i) { return i < 20; })
| std::views::filter([s = false](auto const&) mutable { return s = !s; });
For a more universal stride solution, you can use a counter along with the modulo operator in the lambda expression. To be able to specify the stride size n
in a readable way, I would use the following lambda expression, which provides another lambda expression that keeps track of the stride operation:
auto stride = [](int n) {
return [s = -1, n](auto const&) mutable { s = (s + 1) % n; return !s; };
};
All in all, the final solution looks like this:
auto even_teens = std::views::iota(0, 100)
| std::views::drop_while([](int i) { return i < 10; })
| std::views::take_while([](int i) { return i < 20; })
| std::views::filter(stride(2));
Code on Wandbox
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With