Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::transform for more than two vectors

Tags:

c++

std

vector

Assumed I would like to add two vectors a and b into a vector c

std::vector<double> a, b, c;
//Fill/Initialize vectors

I can either use std::transform() or a simple for-loop:

//std::transform
std::transform(a.begin(),
               a.end(),
               b.begin(),
               c.begin(),
               [](const auto &a_val, const auto &b_val){
                  return a_val + b_val;
               });
//Loop
for(size_t i = 0; i < a.size(); ++i){
    c[i] = a[i] + b[i];
}

Usually, using std::transform() is recommended compaired to a for-loop. Unfortunately, as far as I know, I can not do a similar approach using std::transform() for the case of adding up more vectors than two, i.e. if I would like to add up the vectors a, b and c into d. Then I'm back to the for-loop, or I have to use two rounds of std::transform(). Or is there a way of using a single std::transform()-operation (or something similar) for operating on more than two input vectors at the same time?

like image 692
arc_lupus Avatar asked Apr 22 '20 08:04

arc_lupus


2 Answers

It's true, in standard library this is impossible to std::transform more than 2 vectors. But using range-v3 you can do it with arbitrary number of vectors using zip.

#include <include/range/v3/view/zip.hpp>

int main()
{
    std::vector<int> a{1,2,3};
    std::vector<int> b{2,3,4};
    std::vector<int> c{2,3,4};
    std::vector<int> d{2,3,4};

    std::vector<int> res;
    auto r = ranges::views::zip(a, b, c, d);
    std::transform(
        r.begin(),
        r.end(),
        std::back_inserter(res),
        [](const auto& tup) { return std::get<0>(tup); }
    );
}
like image 58
bartop Avatar answered Sep 29 '22 06:09

bartop


If you are fine with creating another vector you can do this:

#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm>

int main() {
    std::vector<int> a{1,2,3,4};
    std::vector<int> b{1,2,3,4};
    std::vector<int> c(a.size());

    std::vector<int> v(a.size());
    std::iota(v.begin(), v.end(),0);

    std::transform(v.begin(),
               v.end(),
               c.begin(),
               [&](const auto &i){
                  return a[i] + b[i];
               });

    for (const auto& e : c) std::cout << e << ' ';
    return 0;
}

You could turn the lambda into something more generic, eg a functor that works on variadic number of vectors and adds their elements.

However, I would prefer the loop. Main advantage of algorithms is clarity, but if you need to resort to a workaround that clarity is lost. Maybe someone can proove me wrong and find an algorithm that can do what you want out of the box ;).

PS: on a second thought the above is really silly and a misuse of the algorithm. With the help of std::iota any loop can trivially be transformed to using an algorithm, but the true meaning of std::transform "transform one range to another" is completely lost which defeats the purpose of using an algorithm in the first place.

like image 26
463035818_is_not_a_number Avatar answered Sep 29 '22 05:09

463035818_is_not_a_number