Home > database >  Iteratively apply range-v3 views::filter on a container
Iteratively apply range-v3 views::filter on a container

Time:09-26

In the for loop in the following code snippet I'm trying to apply views::filter. But it results in type miss match and assignment error.

#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; });
    }
}

Error C2679 binary '=': no operator found which takes a right-hand operand of type 'ranges::filter_viewranges::filter_view<ranges::ref_view<std::vector<int,std::allocator<int>>,Arg>,main::<lambda_2>>' (or there is no acceptable conversion)`



So basically how can I apply views::filter on numbers ,assign the result in results , then apply views::filter on results and then again assign the result in 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; });

CodePudding user response:

In your example, the return type of results | ranges::views::filter is different from results, so you cannot assign it to results.

The alternative is to use ranges::to to convert each result into a vector and assign it to the previous result:

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.

CodePudding user response:

views::filter returns a different type than the range type passed in. You could apply ranges::to to create a vector after you filter each time, but this is going to be expensive. For this problem, i.e. generating primes using a sieve, it makes sense to use an eager algorithm.

The first filter you've applied is unnecessary; just start the iota at 2, since that's the first prime.

auto results = numbers;

Then while there are still primes to sieve

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));

The above loop is doing a filter as well, but using remove, so the condition has to be negated. Also, since the prime itself is skipped, the lambda doesn't need to check if n == i.

demo

  • Related