C++20 范围的切片视图
Slice view for C++20 ranges
Python 的 itertools
具有采用序列的 islice(seq, start, stop, step)
过程和 returns 序列值的每个 step
值的迭代器在 start
和 stop
之间。
C++20 的 Ranges 库是否提供类似的功能,例如像 slice
这样的函数,它接受一个随机访问迭代器 start
、一个标记 stop
和一个步长值 step
,并且 returns 一个随机访问迭代器遍历 start
和 stop
?
之间的每个第 step
个值
如果没有,是否可以使用 Ranges 库提供的原语来实现这样的迭代器适配器?
(我知道如何手动实现这样的适配器,所以这不是问题。)
不完全是。
C++20 将具有 view::iota
,它为您提供从起始值到哨兵的序列。但是,它没有跨步功能。它只会增加(通过 ++
)。
但是,您可以将其与range-v3's view::stride
结合起来添加到步骤中。即:
auto evens = view::iota(0, 100) | view::stride(2); // [0, 2, 4, 6, ... ]
对于现有范围,view::slice
也没有大步前进。但这些是正交的并且很好地分层:
auto even_teens = view::iota(0, 100)
| view::slice(10, 20)
| view::stride(2); // [10, 12, 14, 16, 18]
不幸的是,Range-v3, as presented in , are not (yet) available in the Ranges library of C++20 的 slice
和 stride
。
但是,您可以通过组合 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 来替换 slice
。您可以将所有内容放在一起来表示范围 [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; });
对于更通用的步幅解决方案,您可以在 lambda 表达式中使用计数器和模运算符。为了能够以可读的方式指定步幅大小 n
,我将使用以下 lambda 表达式,它提供了另一个跟踪步幅操作的 lambda 表达式:
auto stride = [](int n) {
return [s = -1, n](auto const&) mutable { s = (s + 1) % n; return !s; };
};
总而言之,最终的解决方案是这样的:
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));
Python 的 itertools
具有采用序列的 islice(seq, start, stop, step)
过程和 returns 序列值的每个 step
值的迭代器在 start
和 stop
之间。
C++20 的 Ranges 库是否提供类似的功能,例如像 slice
这样的函数,它接受一个随机访问迭代器 start
、一个标记 stop
和一个步长值 step
,并且 returns 一个随机访问迭代器遍历 start
和 stop
?
step
个值
如果没有,是否可以使用 Ranges 库提供的原语来实现这样的迭代器适配器?
(我知道如何手动实现这样的适配器,所以这不是问题。)
不完全是。
C++20 将具有 view::iota
,它为您提供从起始值到哨兵的序列。但是,它没有跨步功能。它只会增加(通过 ++
)。
但是,您可以将其与range-v3's view::stride
结合起来添加到步骤中。即:
auto evens = view::iota(0, 100) | view::stride(2); // [0, 2, 4, 6, ... ]
对于现有范围,view::slice
也没有大步前进。但这些是正交的并且很好地分层:
auto even_teens = view::iota(0, 100)
| view::slice(10, 20)
| view::stride(2); // [10, 12, 14, 16, 18]
不幸的是,Range-v3, as presented in slice
和 stride
。
但是,您可以通过组合 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 来替换 slice
。您可以将所有内容放在一起来表示范围 [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; });
对于更通用的步幅解决方案,您可以在 lambda 表达式中使用计数器和模运算符。为了能够以可读的方式指定步幅大小 n
,我将使用以下 lambda 表达式,它提供了另一个跟踪步幅操作的 lambda 表达式:
auto stride = [](int n) {
return [s = -1, n](auto const&) mutable { s = (s + 1) % n; return !s; };
};
总而言之,最终的解决方案是这样的:
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));