Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a lazily evaluated function on two C++20 ranges?

There is a zip_with function provided by Eric Niebler.

But, now that C++20 have support for ranges I would like to build something similar.

The problem with filter and transform is that they iterate a range?

How would I go about doing this? I have been stuck with this for a while and would hate to use Expression Templates for the same.

Let's say for example I have two vectors M1{1,2,3} and M2{4,5,6}.

I would like to use the ranges library to overload a operator to return a view which contains matrix addition of these two - M1+M2 := {5,7,9}.

With ranges-v3, I can perform auto sum = zip_with(std::plus,M1,M2);

The above expression is evaluated lazily. How can I re-create this expression with C++20 Ranges?

like image 697
Aniket Chowdhury Avatar asked Mar 17 '20 17:03

Aniket Chowdhury


2 Answers

The principle is quite trivial. Create an iterator that stores an iterator for each vectors, that when incremented, increments the two stored iterators and does the addition only when it is dereferenced.

Here is a piece of code that shoes the principle:

template <class It1, class It2>
struct adder_iterator{
  It1 it1;
  It2 it2;

  decltype(auto)
  operator++(){
    ++it1; ++it2;
    return *this;
    }

  auto
  operator *()const{
    return *it1+*it2;
    }
  //....
  };

You will also need to implement a sentinel and a view (by deriving from std::view_interface).

The sentinel is the end iterator. You can use the adder_iterator class for that. But you can think about optimization: in your view constructor, you ensure that the shortest vector begin iterator is always it1 end then only use this iterator to test the end of the iteration. You should try to see.

like image 106
Oliv Avatar answered Nov 16 '22 08:11

Oliv


I don't know what is allowed in c++20, but the following works with range-v3's cpp20 namespace.

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

int main() {

  std::vector<int> m1 = {1, 2, 3};
  std::vector<int> m2 = {4, 5, 6};

  auto sum = ranges::cpp20::views::transform(m1, m2, std::plus{});

  for (auto i : sum)
      std::cout << i << " "; // 5 7 9
}
like image 1
cigien Avatar answered Nov 16 '22 08:11

cigien