[英]Slice view for C++20 ranges
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
. Python 的
itertools
有islice(seq, start, stop, step)
过程,它接受一个序列并返回start
和stop
之间序列值的每step
值的迭代器。
Does the Ranges library of C++20 provide a similar functionality, eg 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
? C++20 的 Ranges 库是否提供了类似的功能,例如像
slice
这样的函数,它接受一个随机访问迭代器start
、一个标记stop
和一个步长值step
,并返回一个随机访问迭代器,该迭代器在每step
上迭代start
和stop
之间的值?
In case it does not, can such an iterator adapter be implemented using the primitives provided by the Ranges library?如果没有,可以使用 Ranges 库提供的原语来实现这样的迭代器适配器吗?
(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. C++20 将具有
view::iota
,它为您提供从起始值到哨兵的序列。 However, it does not have the stride functionality.但是,它没有 stride 功能。 It only increments (via
++
).它只会增加(通过
++
)。
However, you can combine it with range-v3's view::stride
to add in the steps.但是,您可以将它与range-v3 的
view::stride
结合起来添加到步骤中。 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.对于现有的范围,有
view::slice
,它也没有大步。 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 .不幸的是,如Barry'sanswer 中所示, Range-v3 的
slice
和stride
(尚)在C++20的Ranges 库中不可用。 However, you can replace slice
by combining std::views::drop_while
and std::views::take_while
.但是,您可以通过组合
std::views::drop_while
和std::views::take_while
来替换slice
。 To replace stride
, you can use the range adaptor std::views::filter
and pass a specific lambda expression to it.要替换
stride
,您可以使用范围适配器std::views::filter
并将特定的lambda 表达式传递给它。 To filter for every other element as in Barry's example, I would use a stateful lambda expression with an init capture .为了像 Barry 的示例中那样过滤所有其他元素,我将使用带有init capture的有状态 lambda 表达式。 You can put everything together to represent the range
[10, 12, 14, 16, 18]
as follows:您可以将所有内容放在一起来表示范围
[10, 12, 14, 16, 18]
如下:
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.对于更通用的跨步解决方案,您可以在 lambda 表达式中使用计数器和模运算符。 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:为了能够以可读的方式指定步幅大小
n
,我将使用以下 lambda 表达式,它提供了另一个跟踪步幅操作的 lambda 表达式:
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));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.