在容器上迭代地应用 range-v3 views::filter

Posted

技术标签:

【中文标题】在容器上迭代地应用 range-v3 views::filter【英文标题】:Iteratively apply range-v3 views::filter on a container 【发布时间】:2021-11-18 12:06:22 【问题描述】:

在以下代码 sn-p 的for 循环中,我正在尝试应用views::filter。但是会导致类型不匹配和赋值错误。

#include <iostream>
#include <vector>
#include <numeric>
#include <range/v3/all.hpp>

int main()

    int limit =100;
    std::vector<int> numbers(100);
    std::iota(numbers.begin(), numbers.end(), 1);
    auto results = numbers | ranges::views::filter([](int n) return n != 1; );
                           
    int sqrt_limit = std::sqrt(limit);
    for (int i = 2; i <= sqrt_limit; i++)
    
        results = results | ranges::views::filter([i](int n) return n == i
            || n % i != 0; );
    

错误 C2679 二进制“=”:未找到采用“ranges::filter_viewranges::filter_view<:ref_view> 类型的右侧操作数的运算符>,Arg>,main::>' (或没有可接受的转换)`

所以基本上我怎样才能在numbers上应用views::filter,在results上分配结果,然后在results上应用views::filter,然后再次在results上分配结果?

std::vector<int> numbers(100);
std::iota(numbers.begin(), numbers.end(), 1);
auto results = numbers | ranges::views::filter([](int n) return n != 1; );
results = results | ranges::views::filter([i](int n) return n == 2
        || n % 2 != 0; );

【问题讨论】:

【参考方案1】:

在您的示例中,results | ranges::views::filter 的返回类型与results 不同,因此您不能将其分配给results

另一种方法是使用ranges::to 将每个结果转换为vector 并将其分配给之前的结果:

std::vector<int> numbers(100);
std::iota(numbers.begin(), numbers.end(), 1);
auto results = numbers
             | ranges::views::filter([](int n) return n != 1; )
             | ranges::to_vector;
results = results
        | ranges::views::filter([](int n) return n == 2|| n % 2 != 0; )
        | ranges::to_vector;

Demo.

【讨论】:

谢谢。我不知道 range::to .【参考方案2】:

views::filter 返回的类型与传入的范围类型不同。您可以在每次过滤后应用ranges::to 创建一个向量,但这会很昂贵。对于这个问题,即使用筛子生成素数,使用 Eager 算法是有意义的。

您应用的第一个过滤器是不必要的;只需在 2 处开始 iota,因为那是第一个素数。

auto results = numbers;

那么当还有素数要筛选的时候

auto end = std::end(results);
for (auto begin = std::begin(results); begin != end; begin++)
    end = std::remove_if(begin + 1, end,        // *begin is prime by definition, so skip it
                         [i = *begin](int n) 
                            return n % i == 0;  // remove multiples of the prime 
                            );

// erase all the composite numbers
results.erase(end, std::end(results));

上面的循环也在做一个过滤器,但是使用remove,所以这个条件必须被否定。此外,由于跳过了素数本身,因此 lambda 不需要检查是否为 n == i

demo

【讨论】:

以上是关于在容器上迭代地应用 range-v3 views::filter的主要内容,如果未能解决你的问题,请参考以下文章

用于 STL 容器的 std::string_view

如何在 R 中的单个数据帧上迭代地应用函数?

如何确定`range :: view`对象和`std :: vector`之间的等价?

C++ STL 基础及应用 迭代器

设计模式与Android迭代器模式——容器遍历,细节隐藏

C++ 引用在函数式编程中的应用