简体   繁体   English

C++20 范围的切片视图

[英]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 的itertoolsislice(seq, start, stop, step)过程,它接受一个序列并返回startstop之间序列值的每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上迭代startstop之间的值?

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 的slicestride (尚)在C++20Ranges 库中不可用。 However, you can replace slice by combining std::views::drop_while and std::views::take_while .但是,您可以通过组合std::views::drop_whilestd::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));

Code on Wandbox Wandbox 上的代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM