2009-06-23 18 views
12

en cuenta lo siguiente:miembros de la estructura dentro de un vector

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

typedef std::vector<ABCD> VecABCD; 

dicho que quiera sumar todos los 'a' miembro de un vector de tipo VecABCD. ¡Fácil! Recorro el vector y sumar a medida que avanzo.

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

Dije que quería hacer lo mismo con 'b'? ¡Fácil! Escribiría ... esencialmente la misma función, pero con solo cambios triviales. Lo mismo con 'c' y 'd'.

Entonces, ¿hay alguna forma más concisa y menos repetitiva de hacer esto? Me gustaría hacer algo como:

int sumOfA = SumOfMembers(myVec, a); 

pero no puedo concebir cómo podría poner juntas una función de este tipo. Idealmente, sería una plantilla, y podría usarlo con un vector de cualquier tipo de estructura, no específicamente vinculado a VecABCD. ¿Alguien tiene alguna idea?

Respuesta

14

sumas STL se puede hacer con std::accumulate

#include <functional> 

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

Si quería que esto sea más genérico, se puede tomar una función tr1 :: para el miembro que desea vincular:

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

Otra manera de hacerlo, en lugar de poner su lógica en el funtor, sería poner la lógica del iterador, utilizando un impulso :: transformar iterador:

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

editar para agregar: C++ 11 sintaxis lambda

Esto se convierte en algo más clara con los C++ 11 lambdas (aunque por desgracia no son más cortos):

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

y

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

Uso:

// 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; }); 
+0

+1 para acumular. ¿Qué ocurre con el desbordamiento de int? Alguna verificación de error es necesaria. –

+1

El desbordamiento interno (o subdesbordamiento) dependería del caso de uso para esto; Probablemente emplearía el algoritmo de avestruz hasta que tuviera motivos para sospechar lo contrario. –

+0

¿Está bind() en STL o solo en Boost? – Ari

1

Puede usar for_each. Es una opción.

#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; 

} 

de salida:

4 
+0

...¿Cómo va cada uno a darle un conteo? – Peter

+0

@Peter. Se agregó un ejemplo. Análogo al que está en el enlace for_each provisto. – Tom

+1

¿Cómo harías esto para calcular la suma de todas las b? – Naveen

0

Vamos a añadir una opción más, por desgracia uno feo. Es posible tomar la dirección relativa desde el inicio de la estructura ABCD a su miembro mediante offsetof -function. Entregar valor de retorno a la función y puede hacer el recuento utilizando la ubicación relativa desde el inicio de cada estructura. Si sus tipos pueden diferir de int, es posible que desee entregar también el tamaño de la información.

9

Otra opción sería utilizar puntero-a-miembros:

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. 
+0

+1. Para mí, esto parece una mejor solución. –

+0

+1. ¡No lo sabía! –

Cuestiones relacionadas