【问题标题】:Summing struct members inside a vector对向量内的结构成员求和
【发布时间】:2009-06-23 04:22:55
【问题描述】:

考虑以下几点:

typedef struct {
    int a;
    int b;
    int c;
    int d;
} ABCD;

typedef std::vector<ABCD> VecABCD;

假设我想将 VecABCD 类型向量中的每个“a”成员相加。简单的!我只是循环遍历向量,然后进行求和。

int CalcSumOfA(const VecABCD &vec)
{
    int sumOfA = 0;
    VecABCD::const_iterator it;
    for(it=vec.begin();it!=vec.end();it++)
        sumOfA += it->a;
    return sumOfA;
}

假设我想对“b”做同样的事情?简单的!我会写......基本上相同的功能,但只有微不足道的变化。与“c”和“d”相同。

那么,有没有更简洁、更少重复的方法呢?我想做类似的事情:

int sumOfA = SumOfMembers(myVec, a);

但我无法想象如何将这样的功能组合在一起。理想情况下,它是一个模板,我可以将它与任何结构的向量一起使用,而不是专门绑定到 VecABCD。有人有什么想法吗?

【问题讨论】:

    标签: c++ stl


    【解决方案1】:

    STL 求和可以用std::accumulate 完成

    #include <functional>
    
    accumulate(v.begin(), v.end(), 0, bind(plus<int>(), _1, bind(&ABCD::a, _2)))
    

    如果您希望它更通用,可以将 tr1::function 用于要绑定的成员:

    int sum_over_vec(const vector<ABCD>& v, const tr1::function<int (const ABCD&)>& member)
    {
      return accumulate(v.begin(), v.end(),
                        0,
                        bind(plus<int>(),
                             _1,
                             bind(member, _2)));
    };
    
    // ...
    
    int sum_a = sum_over_vec(vec, bind(&ABCD::a, _1));
    

    另一种方法是使用 boost::transform 迭代器,而不是将逻辑放入仿函数中,而是将逻辑放入迭代器中:

    tr1::function<int (const ABCD&)> member(bind(&ABCD::a, _1));
    accumulate(make_transform_iterator(v.begin(), member),
               make_transform_iterator(v.end(),   member),
               0);
    

    编辑添加:C++11 lambda 语法

    这在使用 C++11 lambda 时变得更加清晰(尽管不幸的是不更短):

    accumulate(v.begin(), v.end(), 0,
        [](int sum, const ABCD& curr) { return sum + curr.a });
    

    int sum_over_vec(const vector<ABCD>& v, const std::function<int (const ABCD&)>& member)
    {
      return accumulate(v.begin(), v.end(), 0,
          [&](int sum, const ABCD& curr) { return sum + member(curr}); });
    };
    

    用法:

    // Use a conversion from member function ptr to std::function.
    int sum_a = sum_over_vec(vec, &ABCD::a);
    // Or using a custom lambda sum the squares.
    int sum_a_squared = sum_over_vec(vec,
        [](const ABCD& curr) { return curr.a * curr.a; });
    

    【讨论】:

    • +1 表示累积。 int溢出呢?需要进行一些错误检查。
    • Int 溢出(或下溢)将取决于用例;在我有理由怀疑之前,我可能会使用鸵鸟算法。
    • bind() 是在 STL 中还是仅在 Boost 中?
    • Bind 在 boost 中,但也在 TR1 中,这是一组新的标准库(但不是 STL)。许多流行的编译器已经支持它:msdn.microsoft.com/en-us/library/bb982702.aspx
    【解决方案2】:

    另一种选择是使用指向成员的指针:

    int CalcSumOf(const VecABCD & vec, int ABCD::*member)
    {
        int sum = 0;
        for(VecABCD::const_iterator it = vec.begin(), end = vec.end(); it != end; ++it)
            sum += (*it).*member;
        return sum;
    }
    ...
    int sumA = CalcSumOf(myVec, &ABCD::a);  // find sum of .a members
    int sumB = CalcSumOf(myVec, &ABCD::b);  // find sum of .b members
    // etc.
    

    【讨论】:

      【解决方案3】:

      使用std::accumulate :)

      【讨论】:

        【解决方案4】:

        您可以使用for_each。它是一种选择。

        #include <iostream>
        #include <algorithm>
        #include <vector>
        
        using namespace std;
        typedef struct{
            int a;
        
        }ABCD;
        
        typedef vector<ABCD> vecABCD;
        
        struct sum  : public unary_function<ABCD, void>
        {
          sum(){count.a=count.b=count.c=count.d=0;}
          void operator() (ABCD x) {
               count.a+=x.a;
               count.b+=x.b;
               count.c+=x.c;
               count.d+=x.d;
           }
          ABCD count;
        };
        
        int main()
        {
        
          ABCD s1={1,2,3,4};
          ABCD s2={5,6,7,8};
        
          vecABCD v;
          v.push_back(s1);
          v.push_back(s2);
          sum s = for_each(v.begin(), v.end(), sum());
          cout<<s.count.a<<endl;
        
        }
        

        输出:

        4
        

        【讨论】:

        • ...for_each 将如何计算?
        • @彼得。添加了一个示例。类似于提供的 for_each 链接中的那个。
        • 你将如何计算所有 b 的总和??
        • @Naveen:我明白你的意思。编辑我的答案以总结所有 a、b、c、d。尽管如此,显然 for_each 并不是最好的选择。
        【解决方案5】:

        让我们再添加一个选项,不幸的是一个丑陋的选项。可以通过 offsetof -function 获取从 struct ABCD 开始到其成员的相对地址。将返回值传递给函数,它可以使用每个结构体开头的相对位置进行计数。如果您的类型可能与 int 不同,您可能还需要传递 sizeof 信息。

        【讨论】:

          【解决方案6】:

          这在未来可能会在 C++ 范围内实现。因为它们支持预测。
          投影允许对迭代类的成员变量执行算法。

          目前(2022 年)algorithm 库还没有accumulate 的范围版本。
          有一篇论文用于添加到 C++ 23:P2214: A Plan for C++23 Ranges

          但您已经可以使用它/尝试使用 range v3 库。

          #include <iostream>
          #include <range/v3/numeric/accumulate.hpp>
          
          struct Point
          {
             int x;
             int y;
          };
          
          int main()
          {
             const auto points = std::vector{ Point{1, 1}, Point{2, 4}, Point{3, 0} };
          
             const int xSum = ranges::accumulate(points, 0, std::plus(), &Point::x);
             const int ySum = ranges::accumulate(points, 0, std::plus(), &Point::y);
          
             std::cout << "X sum: " << xSum << " - Y sum: " << ySum << '\n';
             // output: X sum: 6 - Y sum: 5
          }
          

          【讨论】:

            猜你喜欢
            • 2022-01-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-06-23
            • 1970-01-01
            相关资源
            最近更新 更多