【问题标题】:std::transform with two std::vector and one constant as arguments以两个 std::vector 和一个常量作为参数的 std::transform
【发布时间】:2018-11-19 22:31:24
【问题描述】:

我想使用 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 应用于两个向量 x1 和 x2?我是否必须使标量常数 c 成为具有相同元素的向量,例如 (c,c,c...c)?

提前致谢。

【问题讨论】:

  • 你考虑过std::bind 吗?

标签: c++ c++11 lambda transform stdvector


【解决方案1】:

试试这个

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

是lambda函数; i 是来自 x1 的元素,j 是来自 x2 的元素。 [&] 发出信号,通过引用捕获在 lambda 之外定义的所有值。

【讨论】:

  • @skeller 为什么不呢?捕获所有和显式捕获之间的唯一区别是后者使使用外部范围的名称更加明显,这对于一个衬里来说并不是必需的。
  • @Cubic 因为这是一种不好的做法。您捕获的方式比您预期的要多,例如'this' 指针。
【解决方案2】:

你可以通过提供一个仿函数来做到这一点,即一个结构体,它有一个调用运算符来做你想做的事情。执行此操作的旧方法是执行以下操作:

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 添加了 lambda,让您无需在代码的其他部分定义结构即可执行此操作:

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

如果你不习惯它的语法可能看起来有点古怪,但这基本上和上面的一样。

lambda 中的[] 部分决定了外部范围的哪些部分(即不在 lambda 本身中的所有内容)应该是可见的。 [] 表示什么都没有,[=] 表示按值复制的所有内容,[&amp;] 表示通过引用可见的所有内容。您也可以直接命名,例如[someConstant, &amp;someOtherConstant] 将通过值捕获someConstant,通过引用捕获someOtherConstant

【讨论】:

  • 您已经解释了 lambda 捕获;但是,在您的第二个示例中,错过了将 someConstant 捕获到 lambda 中。 :)
  • @JeJo 这些示例是伪代码,因此显然您也可以在其中放置文字。话虽如此,无论如何您只需要捕获本地范围 - 如果someConstant 在全局范围内,则无需将其放入捕获列表中。
  • 在这种情况下,someConstant 属于 lambda 的本地范围,顺便说一句。
【解决方案3】:

您可以在 lambda 中捕获标量:

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

【讨论】:

    【解决方案4】:

    你甚至不需要multiply(double x, double y) 函数。使用一个简单的 lambda,你可以做所有你想做的乘法和加法。

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

    来自cppreference.com

    std::transform 不保证 unary_op 的有序应用 或 binary_op。将函数按顺序应用到序列或应用 修改序列元素的函数,使用std::for_each

    【讨论】:

    • 您的 for_each 示例比转换更复杂,而不是 更少 复杂(需要跟踪单独的索引并使用两种不同的方式来尝试同时访问事物地点)。此外,for_each 的所有实例都可以替换为 for-each 循环,这样更易​​于理解语法。
    • @Cubic 我已经在回答中提到了额外变量index。然而,这是对 OPs 问题的一个建议,即使他没有要求。
    【解决方案5】:

    你为什么不使用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);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-11
      相关资源
      最近更新 更多