C ++ 20范围的切片视图

时间:2019-06-17 20:27:45

标签: c++ stl c++20

Python的itertools具有islice(seq, start, stop, step)过程,该过程采用一个序列并返回stepstart之间序列值的每个第stop个值的迭代器

C ++ 20的Ranges库是否提供类似的功能,例如像slice这样的函数,它需要一个随机访问迭代器start,一个哨兵stop和一个步进值step,并返回一个对每个{在stepstart之间的第{1}}个值?

如果没有,可以使用Ranges库提供的原语来实现这种迭代器适配器。

(我知道如何手动实现这种适配器,所以这不是问题。)

2 个答案:

答案 0 :(得分:6)

不太。

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]

答案 1 :(得分:2)

不幸的是,Range-v3 中的 slicestride(如 Barry'sanswer 中所示)(尚)在 {{3} 的 Ranges library 中不可用}. 但是,您可以通过组合 C++20std::views::drop_while 来替换 slice。要替换 stride,您可以使用范围适配器 std::views::take_while 并将特定的 std::views::filter 传递给它。为了像 Barry 的示例中那样过滤所有其他元素,我将使用带有 lambda expression 的有状态 lambda 表达式。您可以将所有内容放在一起来表示范围 [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));

init capture