Multiply vector elements by a scalar value using STL
Asked Answered
M

6

52

Hi I want to (multiply,add,etc) vector by scalar value for example myv1 * 3 , I know I can do a function with a forloop , but is there a way of doing this using STL function? Something like the {Algorithm.h :: transform function }?

Martinet answered 7/10, 2010 at 19:18 Comment(0)
X
97

Yes, using std::transform:

std::transform(myv1.begin(), myv1.end(), myv1.begin(),
               std::bind(std::multiplies<T>(), std::placeholders::_1, 3));

Before C++17 you could use std::bind1st(), which was deprecated in C++11.

std::transform(myv1.begin(), myv1.end(), myv1.begin(),
               std::bind1st(std::multiplies<T>(), 3));

For the placeholders;

#include <functional> 
Xantho answered 7/10, 2010 at 19:22 Comment(2)
Any illumination on what std::bind() is doing in the first example?Glenoid
Is this expected to be faster than a loop?Nightstick
D
32

If you can use a valarray instead of a vector, it has builtin operators for doing a scalar multiplication.

v *= 3;

If you have to use a vector, you can indeed use transform to do the job:

transform(v.begin(), v.end(), v.begin(), _1 * 3);

(assuming you have something similar to Boost.Lambda that allows you to easily create anonymous function objects like _1 * 3 :-P)

Dermot answered 7/10, 2010 at 19:22 Comment(2)
Valarrays are the correct solution IMO. With luck, your implementation uses SSE instructions to implement the procedure, making it significantly faster. See pixelglow.com/macstl/valarray for one such implementation of valarray. Unfortunately, its not very widespread, so if you want the advantages of SSE instructions you'll probably have to use compiler intrinsics...Shortcut
@Dragon: valarray is the best STL solution, but it's not very good for high-performance computing because it tends to copy data in memory a lot, and produces sequences of small loops containing single operations which have poor memory access patterns. It is easier to upgrade from valarray to a proper expression template system, though.Appaloosa
W
29

Modern C++ solution for your question.

#include <algorithm>
#include <vector>

std::vector<double> myarray;
double myconstant{3.3};
std::transform(myarray.begin(), myarray.end(), myarray.begin(), [&myconstant](auto& c){return c*myconstant;});
Weakfish answered 20/1, 2019 at 18:54 Comment(1)
I'd make it simpler just an empty capture lambda, and get ride of the const object. After all, this id supposed to be a Kickstarter.Kloster
P
2

I think for_each is very apt when you want to traverse a vector and manipulate each element according to some pattern, in this case a simple lambda would suffice:

std::for_each(myv1.begin(), mtv1.end(), [](int &el){el *= 3; });

note that any variable you want to capture for the lambda function to use (say that you e.g. wanted to multiply with some predetermined scalar), goes into the bracket as a reference.

Polivy answered 3/1, 2018 at 10:43 Comment(1)
This is not the purpose of std::for_each. std::for_each applies some (probably stateful) function object to a range and then returns this function object. If you want to transform a range use std::transform to make your intent more clear.Schutz
S
1

If you had to store the results in a new vector, then you could use the std::transform() from the <algorithm> header:

#include <algorithm>
#include <vector>

int main() {
    const double scale = 2;
    std::vector<double> vec_input{1, 2, 3};
    std::vector<double> vec_output(3); // a vector of 3 elements, Initialized to zero
    // ~~~
    std::transform(vec_input.begin(), vec_input.end(), vec_output.begin(),
                   [&scale](double element) { return element *= scale; });
    // ~~~
    return 0;
}

So, what we are saying here is,

  • take the values (elements) of vec_input starting from the beginning (vec_input.begin()) to the end (vec_input.begin()),
    • essentially, with the first two arguments, you specify a range of elements ([beginning, end)) to transform, range
  • pass each element to the last argument, lambda expression,
  • take the output of lambda expression and put it in the vec_output starting from the beginning (vec_output.begin()).
    • the third argument is to specify the beginning of the destination vector.

The lambda expression

  • captures the value of scale factor ([&scale]) from outside by reference,
  • takes as its input a vector element of type double (passed to it by std::transform())
  • in the body of the function, it returns the final result,
    • which, as I mentioned above, will be consequently stored in the vec_input.

Final note: Although unnecessary, you could pass lambda expression per below:

[&scale](double element) -> double { return element *= scale; }

It explicitly states that the output of the lambda expression is a double. However, we can omit that, because the compiler, in this case, can deduce the return type by itself.

Syntactics answered 16/11, 2021 at 22:18 Comment(0)
L
-1

I know this not STL as you want, but it is something you can adapt as different needs arise.

Below is a template you can use to calculate; 'func' would be the function you want to do: multiply, add, and so on; 'parm' is the second parameter to the 'func'. You can easily extend this to take different func's with more parms of varied types.

template<typename _ITStart, typename _ITEnd, typename _Func , typename _Value >
_ITStart xform(_ITStart its, _ITEnd ite, _Func func, _Value parm)
{
    while (its != ite) { *its = func(*its, parm); its++; }
    return its;
}
...

int mul(int a, int b) { return a*b; }

vector< int > v;

xform(v.begin(), v.end(), mul, 3); /* will multiply each element of v by 3 */

Also, this is not a 'safe' function, you must do type/value-checking etc. before you use it.

Ledeen answered 7/10, 2010 at 20:54 Comment(1)
This works, but is essentially reinventing the wheel, as std::transform already does this for you.Xantho

© 2022 - 2024 — McMap. All rights reserved.