簡體   English   中英

對向量內的結構成員求和

[英]Summing struct members inside a vector

考慮以下:

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。 有人有想法么?

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

#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 lambdas來說,這變得更加清晰(盡管不幸的是不會更短):

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

另一種選擇是使用指向成員的指針:

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.

你可以使用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

讓我們再添加一個選項,遺憾的是一個丑陋的選項。 可以通過offsetof -function從struct ABCD的開頭到它的成員獲取相對地址。 將返回值傳遞給函數,它可以使用每個結構開頭的相對位置進行計數。 如果您的類型可能與int不同,您可能還希望提供sizeof信息。

這可能在未來的 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
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM