I would like to understand when it is more practical to use std::transform and when an old fashioned for-loop is better.
This is my code with a for loop, I want to combine two vectors into a complex one:
vector<double> vAmplitude = this->amplitudeData(N);
vector<double> vPhase = this->phaseData(N);
vector<complex<double>,fftalloc<complex<double> > > vComplex(N);
for (size_t i = 0; i < N; ++i)
{
vComplex[i] = std::polar(vAmplitude[i], vPhase[i]);
}
This is my std::transform code
vector<double> vAmplitude = this->amplitudeData(N);
vector<double> vPhase = this->phaseData(N);
vector<complex<double>,fftalloc<complex<double> > > vComplex;
std::transform(
begin(vPhase), end(vPhase), begin(vAmplitude),
std::back_inserter(vComplex),
[](double p, double a) { return std::polar(a, p); });
Note that vComplex is allocated without size, so I wonder when the allocations happends. Also I do not understand why, in the lambda expression, p
and a
must be reversed to their usage.
One consideration in favor of the standard algorithms, is that it prepares your code (and you) for the c++17 alternative execution model versions.
To borrow from JoachimPileborg's answer, say you write your code as
vector<complex<double>,fftalloc<complex<double> > > vComplex(N);
std::transform(
begin(vAmplitude), end(vAmplitude), begin(vPhase),
std::begin(vComplex),
std::polar);
After some time, you realize that this is the bottleneck in your code, and you need to run it in parallel. So, in this case, all you'd need to do is add
std::execution::par{}
as the first parameter to std::transform
. In the hand-rolled version, your (standard-compliant) parallelism choices are gone.
Regarding the allocation, that's what std::back_inserter
does.
You could also set the size for the destination vector vComplex
and use std::begin
for it in the std::transform
call:
vector<complex<double>,fftalloc<complex<double> > > vComplex(N);
std::transform(
begin(vPhase), end(vPhase), begin(vAmplitude),
std::begin(vComplex),
[](double p, double a) { return std::polar(a, p); });
As for the reversal of the arguments in the lambda, it's because you use vPhase
as the first container in the std::transform
call. If you changed to use vAmplitude
instead you could have passed just a pointer to std::polar
instead:
std::transform(
begin(vAmplitude), end(vAmplitude), begin(vPhase),
std::begin(vComplex),
std::polar);
Lastly as for when to call std::transform
it's more of a personal matter in most cases. I personally prefers to use the standard algoritm functions before trying to do everything myself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With