使用for_each计算向量中每个元素的平方和

12

由于被 for_each 接受的函数仅接受一个参数(向量的元素),因此我必须在某个地方定义一个 static int sum = 0,以便在调用 for_each 后可以访问它。 我认为这很笨拙。 还有更好的方法吗?(仍然使用 for_each)?

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

using namespace std;

static int sum = 0;
void add_f(int i )
{
    sum += i * i;

}
void test_using_for_each()
{
    int arr[] = {1,2,3,4};
    vector<int> a (arr ,arr + sizeof(arr)/sizeof(arr[0]));

    for_each( a.begin(),a.end(), add_f);
    cout << "sum of the square of the element is  " << sum << endl;
}

在 Ruby 中,我们可以这样做:

sum = 0
[1,2,3,4].each { |i| sum += i*i}   #local variable can be used in the callback function
puts sum    #=> 30

能否展示更多实际编程中for_each的使用示例(而不仅是打印每个元素)?是否可以使用for_each模拟Ruby中的'map'和'inject'(或Haskell中的'map/fold')等编程模式。

#map in ruby 
>> [1,2,3,4].map  {|i| i*i} 
=> [1, 4, 9, 16]

#inject in ruby 
[1, 4, 9, 16].inject(0)  {|aac ,i| aac +=i}  #=> 30

编辑:感谢大家,我从你们的回复中学到了很多。在C++中,有很多种方法可以完成同一个任务,这使得学习变得有点困难。但是这很有趣 :)

6个回答

51

不要使用std::accumulate(),而要使用std::inner_product()。不需要函数对象。

#include <vector>
#include <numeric>

void main()
{
    std::vector <int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);

    int x = std::inner_product( v1.begin(), v1.end(), v1.begin(), 0 );
}

非常优雅...正是我所寻找的! - Jacob
7
为什么这不是最佳答案? - math

23
使用std::accumulate函数。
#include <vector>
#include <numeric>

// functor for getting sum of previous result and square of current element
template<typename T>
struct square
{
    T operator()(const T& Left, const T& Right) const
    {   
        return (Left + Right*Right);
    }
};

void main()
{
    std::vector <int> v1;
    v1.push_back(1);
    v1.push_back(2);
    v1.push_back(3);
    v1.push_back(4);

    int x = std::accumulate( v1.begin(), v1.end(), 0, square<int>() );
    // 0 stands here for initial value to which each element is in turn combined with
    // for our case must be 0.
}

你可以像GMan的优秀回答中所示,模拟std :: accumulate函数,但我认为使用std :: accumulate会使您的代码更易读,因为它是为此类目的而设计的。 您可以在这里找到更多标准算法。


1
我认为向量迭代器不能保证在std命名空间中。如果是这样的话,那么ADL在这里不能保证工作,并且提问者也没有指定编译器。 - Steve Jessop
2
你是对的。我刚刚检查了一下 - 标准并不保证迭代器是 std 命名空间的一部分。只有反向迭代器是 std 命名空间的一部分。 - Kirill V. Lyadvinsky
一一地说:哇,好发现。我查了标准,你绝对是对的。所以例如如果vector<T>::iterator被typedef为T*,ADL确实会失败。-1以引起OP的注意...(顺便说一句,这篇帖子非常棒。) - j_random_hacker
好的,速度很快,最后不需要减1! :) - j_random_hacker

7

for_each 返回使用的函数对象的副本。因此,代码可能如下:

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

template <typename T>
class square_accumulate
{
public:
    square_accumulate(void) :
      _sum(0)
      {
      }

      const T& result(void) const
      {
          return _sum;
      }

      void operator()(const T& val)
      {
          _sum += val * val;
      }

private:
    T _sum;
};

int main(void)
{
    int arr[] = {1,2,3,4};
    std::vector<int> a (arr ,arr + sizeof(arr)/sizeof(arr[0]));

    int sum = std::for_each(a.begin(), a.end(), square_accumulate<int>()).result();

    std::cout << "sum of the square of the element is " << sum << std::endl;
}

正如其他回答所示,std::accumulate 是最佳选择。


3
这是一个很好的std::accumulate仿真示例,非常适合教学用途。 - Kirill V. Lyadvinsky
给评论点个踩?如果不知道哪里出了问题,就无法改进。 - GManNickG

4
不要使用for_each(),而应该使用<numeric>头文件中的accumulate()函数:
#include <numeric>
#include <iostream>
using namespace std;

struct accum_sum_of_squares {
    // x contains the sum-of-squares so far, y is the next value.
    int operator()(int x, int y) const {
        return x + y * y;
    }
};

int main(int argc, char **argv) {
    int a[] = { 4, 5, 6, 7 };

    int ssq = accumulate(a, a + sizeof a / sizeof a[0], 0, accum_sum_of_squares());
    cout << ssq << endl;
    return 0;
}
accumulate() 的默认行为是对元素求和,但是您可以像我们在此处所做的那样提供自己的函数或函数对象,并且它执行的操作不一定是可结合的——第二个参数始终是要进行操作的下一个元素。在其他语言中,这种操作有时被称为 reduce
您可以使用普通函数代替 accum_sum_of_squares 函数对象,或者更加通用的是,您可以使 accum_sum_of_squares 成为一个接受任何数字类型的类模板。

3

std::for_each用于对每个元素执行某些操作。如果您想从所有元素的计算中获得结果,则使用std::accumulate。如果您想要Haskell的map行为,则使用std::transform

您可以滥用这三个函数中的任何一个来执行其他函数的相同操作,因为它们最终只是在迭代器上迭代(除了以两个迭代器作为输入的transform形式)。重点是for_each不能替代map/fold - 应该由transform/accumulate完成 - 尽管C++本地不具备像Haskell那样表达map/fold概念的东西 - 但gcc和VC++都支持OpenMP,后者在#pragma omp parallel for中有更好的类比。

Ruby中的Inject与使用完整functor调用for_each非常相似,就像GMan上面解释的那样。在C++0X中带有变量捕获的Lambda函数将使两种语言之间的行为更加相似:

int main(void)
{
    int arr[] = {1,2,3,4};
    std::vector<int> a (arr ,arr + sizeof(arr)/sizeof(arr[0]));

    int sum = 0;
    std::for_each(a.begin(), a.end(), [&](int i) { sum += i*i;} );

    std::cout << "sum of the square of the element is " << sum << std::endl;
}

3
作为解决STL中这种问题的通用方法,您可以传递一个functor而不是传递函数。例如,任何实现operator()的类的实例都可以作为functor。这比依赖全局变量要好得多,因为该实例可以保持和更新其自身状态!您可以将其视为一种“编译时鸭子类型”:泛型编程不会限制您在该位置传递“函数”,任何“像函数一样行事”的东西(即具有适当的operator())都可以!-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接