【问题标题】:sum of square of each elements in the vector using for_each使用 for_each 对向量中每个元素的平方和
【发布时间】:2009-08-25 04:50:51
【问题描述】:

由于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 中注入(或在 Haskell 中映射 /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++ 中做同样的事情,这使得学习有点困难。但这很有趣:)

【问题讨论】:

    标签: c++ vector stl-algorithm


    【解决方案1】:

    不,不要使用 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 );
    }
    

    【讨论】:

    • 非常优雅...正是我想要的!
    • 为什么这不是最佳答案?
    【解决方案2】:

    使用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.
    }
    

    你可以像nice GMan's answer 那样模拟std::accumulate,但我相信使用std::accumulate 会使你的代码更具可读性,因为它就是为这样的目的而设计的。你可以找到更多的标准算法here

    【讨论】:

    • 我认为向量迭代器不能保证在 std 命名空间中。如果这是正确的,那么 ADL 不能保证在这里工作,并且提问者没有指定编译器。
    • 你是对的。我刚刚检查过 - 标准并不能保证迭代器是 std 命名空间的一部分。只有反向迭代器是 std 命名空间的一部分。
    • onebyone:哇,好球。我检查了标准,你是绝对正确的。所以例如如果 vector::iterator 被定义为 T*,ADL 确实会失败。 -1 以引起 OP 的注意……(顺便说一句,这篇文章非常棒。)
    • 好吧,这很快,毕竟不需要-1! :)
    【解决方案3】:

    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 是最好的选择。

    【讨论】:

    • 这是一个很好的 std::accumulate 仿真示例。用于教学目的。
    • 投反对票?如果我不知道哪里出了问题,就无法改进。
    【解决方案4】:

    不要为此使用for_each(),请使用&lt;numeric&gt; 标头中的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 设为接受任何数字类型的类模板。

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

        std::for_each 用于使用 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 中注入更接近于使用成熟的仿函数调用 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;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-10-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多