Суммирование элементов структуры внутри вектора - PullRequest
13 голосов
/ 23 июня 2009

Рассмотрим следующее:

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

typedef std::vector<ABCD> VecABCD;

Скажем, я хотел добавить каждый член 'a' в вектор типа VecABCD. Легко! Я просто перебираю вектор и суммирую по ходу.

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. У кого-нибудь есть идеи?

Ответы [ 5 ]

14 голосов
/ 23 июня 2009

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 лямбда-синтаксис

Это становится несколько понятнее с лямбдами C ++ 11 (хотя, к сожалению, не короче):

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; });
10 голосов
/ 23 июня 2009

Другим вариантом будет использование указателей на элементы:

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.
1 голос
/ 23 июня 2009

Использование std :: накопить :)

1 голос
/ 23 июня 2009

Вы можете использовать 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
0 голосов
/ 23 июня 2009

Давайте добавим еще одну опцию, к сожалению, некрасивую. Относительный адрес от начала struct ABCD до его члена можно передать с помощью offsetof -function. Доставьте возвращаемое значение в функцию, и он может делать подсчет, используя относительное местоположение с начала каждой структуры. Если ваши типы могут отличаться от int, вы можете также указать размер информации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...