std :: transform с двумя std :: vector и одной константой в качестве аргументов - PullRequest
0 голосов
/ 10 июня 2018

Я хочу использовать std :: transform, чтобы сделать что-то похожее на binary_op, но с одной дополнительной константой, например, чтобы получить произведение двух векторов: x1 = (10,20,30,40,50) и x2 =(2,4,6,8,10), мы можем написать:

#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;
}

Приведенный выше код будет умножать x1 и x2 поэлементно и возвращать (20,80,180,320,500).

Однако,если я хочу вычислить x1 $ \ dot $ x2 + c, где c - скалярная константа, такая как 1, как функция ниже:

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

В этом случае, как я могу использовать std :: transformприменить на двух векторах х1 и х2?Должен ли я сделать скалярную константу c, чтобы она стала вектором с такими же элементами, как (c, c, c ... c)?

Заранее спасибо.

Ответы [ 5 ]

0 голосов
/ 10 июня 2018

Почему бы вам не использовать std::bind?

Например:

#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);
0 голосов
/ 10 июня 2018

Вам даже не нужна функция multiply(double x, double y).Используя простую лямбду, вы можете выполнять все операции умножения и сложения, которые вы хотите сделать.

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;  });

Я не уверен, что это идеальный случай для работы с std::transform, поскольку вы можетедобиться того же, используя гораздо более простой std::for_each, как показано ниже.Однако требуется дополнительная переменная (index).

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 не гарантирует порядокприменение unary_op или binary_op.Чтобы применить функцию к порядку последовательности или применить функцию, которая изменяет элементы последовательности, используйте std::for_each

0 голосов
/ 10 июня 2018

Вы делаете это, предоставляя функтор, то есть структуру, которая имеет оператор вызова, который делает то, что вы хотите.Старый способ сделать это - сделать что-то вроде этого:

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 добавляет лямбда-выражения, которые позволяют вам делать это на месте без необходимости определять структуру в какой-либо другой части кода:

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

Синтаксис может показаться немного странным, если вы к нему не привыкли, но это в основном то же самое, что и выше.

Часть [] вЛямбда определяет, какие части внешнего объема (т. е. все, что не находится в самой лямбде) должны быть видимы.[] ничего не значит, [=] означает все, что скопировано по значению, а [&] означает все, что видно по ссылке.Вы также можете просто назвать вещи напрямую, например, [someConstant, &someOtherConstant] будет захватывать someConstant по значению и someOtherConstant по ссылке.

0 голосов
/ 10 июня 2018

Вы можете захватить скаляр в лямбду:

double c = 1
std::transform (x1.begin(), x1.end(), x2.begin(), x1.begin(), [c]
    (double x, double y){
       return x*y + c;
    });
0 голосов
/ 10 июня 2018

Попробуйте это

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

Это лямбда-функция;i это элемент из x1, j это элемент из x2.[&] сигнализирует захват всех значений, определенных за пределами лямбда-ссылки.

...