John
John

Reputation: 329

std::transform with two std::vector and one constant as arguments

i want to use std::transform to do something similar to binary_op but with one additional constant, for example, to get a product of two vectors: x1 = (10,20,30,40,50) and x2 = (2,4,6,8,10), we can write:

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


double multiply(double x, double y){
    return x*y;
}

int main () {
  std::vector<int> x1;
  std::vector<int> x2;


  for (int i=1; i<6; i++)
    x1.push_back (i*10); //10,20,30,40,50

  for (int i=1; i<6; i++)
    x2.push_back (i*2);  //2,4,6,8,10



  std::transform (x1.begin(), x1.end(), x2.begin(), x1.begin(),multiply);


  for (std::vector<int>::iterator it=x1.begin(); it!=x1.end(); ++it)
    std::cout << ' ' << *it;
  //output: 20,80,180,320,500

  return 0;
}

The above code will multiply x1 and x2 elementwise and return (20,80,180,320,500).

However, if i want to calculate x1 $\dot$ x2 + c, where c is a scalar constant such as 1, like the function below:

double multiply(double x, double y, double c){
    return x*y + c;
}

In this case, how can i use std::transform to apply on two vectors x1 and x2? do i have to make the scalar constant c to become a vector with the same elements like (c,c,c...c)?

Thanks in advance.

Upvotes: 6

Views: 8436

Answers (5)

JeJo
JeJo

Reputation: 32847

You don't even need multiply(double x, double y) function. Using a simple lambda, you can do all the multiplication and addition you want to do.

int c = 2;
std::transform (x1.begin(), x1.end(), x2.begin(), x1.begin(),
[&c](const auto& x1_ele, const auto& x2_ele){ return (x1_ele*x2_ele) + c;  });

I am not sure, is this a perfect case to deal with std::transform, since you can achieve the same using much simpler std::for_each like follows. An extra varible (index) is necessary though.

int index = 0;
int c = 2;
std::for_each(x1.begin(), x1.end(), [&](auto& x1_ele){ x1_ele = (x1_ele*x2[index]) + c; ++index; });

From cppreference.com:

std::transform does not guarantee in-order application of unary_op or binary_op. To apply a function to a sequence in-order or to apply a function that modifies the elements of a sequence, use std::for_each

Upvotes: 1

artona
artona

Reputation: 1272

Try this

int c = 20;
std::transform (x1.begin(), x1.end(), x2.begin(), x1.begin(),[&c](auto i, auto j) { return  multiply(i,j) + c ; });

It is lambda function; i is the element from x1, j is element from x2. [&] signalizing capturing all values defined outside the lambda by reference.

Upvotes: 6

KostasRim
KostasRim

Reputation: 2053

Why don't you use the std::bind ?

For example:

#include <functional>

using namespace std;
using namespace std::placeholders;

double multiply(double x, double y, double c){
    return x*y + c;
}

//in main()
  //scallar is 10.0 in this case --> last parameter.
  auto multiplyScalar = bind(&multiply, _1, _2, 10.0);

  transform(x1.begin(), x1.end(), x2.begin(), x1.begin(), multiplyScalar);

Upvotes: 1

Joshua Ryan
Joshua Ryan

Reputation: 648

You can capture the scalar in a lambda:

double c = 1
std::transform (x1.begin(), x1.end(), x2.begin(), x1.begin(), [c]
    (double x, double y){
       return x*y + c;
    });

Upvotes: 1

Cubic
Cubic

Reputation: 15683

You do this by supplying a functor, i.e. a struct that has a call operator that does what you want. The old way to do this would be to do something like this:

struct MultWithConstantAdder {
   const double adder;
   MultWithConstantAdder(double adder): adder(adder) {}
   double operator()(double x, double y) { return add(x,y,adder); }
};
// ... rest of your code
std::transform(x1.begin(), x2.end(), x2.begin(), x1.begin(), MultWithConstantAdder(someConstantThatYouWant));

C++11 adds lambdas which let you do this in place without having to define a struct in some other part of the code:

std::transform(x1.begin(), x2.end(), x2.begin(), x1.begin(),
  [](double x, double y) { return add(x,y, someConstant); });

The syntax might look a bit funky if you're not used to it, but this basically does the same thing as the thing above.

The [] part in the lambda determines what parts of the outer scope (i.e. everything that's not in the lambda itself) should be visible. [] means nothing, [=] means everything copied by value, and [&] means everything visible by reference. You can also just name things directly, so for example [someConstant, &someOtherConstant] would capture someConstant by value and someOtherConstant by reference.

Upvotes: 1

Related Questions