2009-08-25 17 views
10

Como la función aceptado por for_each tomar sólo un parámetro (el elemento del vector), tengo que definir un static int sum = 0 en algún lugar de modo que se puede acceder después de llamar al for_each . Creo que esto es incomodo ¿Alguna mejor manera de hacer esto (aún usar for_each)?suma de cuadrados de cada uno de los elementos en el vector usando 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; 
} 

En Ruby, podemos hacerlo de esta manera:

sum = 0 
[1,2,3,4].each { |i| sum += i*i} #local variable can be used in the callback function 
puts sum #=> 30 

podría por favor mostrar más ejemplos de cómo for_each se utiliza normalmente en la programación práctica (no sólo imprimir cada elemento)? ¿Es posible usar for_each simular 'patrón de programación' como mapa e inyectar en Ruby (o mapa/plegar en Haskell).

#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 

EDIT: Gracias a todos. He aprendido mucho de tus respuestas. Tenemos muchas maneras de hacer lo mismo en C++, lo que hace que sea un poco difícil de aprender. Pero es interesante :)

Respuesta

18

Uso 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. 
} 

Se podría emular std :: acumulan como en nice GMan's answer, pero yo creo que el uso de std :: acumulan hará que su código sea más legible, ya que fue diseñado para tales propósitos. Puede encontrar más algoritmos estándar here.

+1

No creo iteradores de vectores están garantizados para estar en el espacio de nombres std. Si eso es correcto, entonces no se garantiza que ADL funcione aquí, y el que pregunta no ha especificado un compilador. –

+2

Tienes razón. Acabo de comprobar: el estándar no garantiza que el iterador sea parte del espacio de nombres estándar. Solo los iteradores inversos son parte del espacio de nombres 'std'. –

+0

onebyone: Guau, buena captura. Revisé el estándar y tienes toda la razón. Por ejemplo, si vector :: iterator fue typedefed a T *, ADL fallaría. -1ing para obtener la atención del OP ... (La publicación es por lo demás excelente BTW.) –

7

for_each devuelve (una copia) del functor que estaba usando. Por lo tanto, algo como esto:

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

Como se ha demostrado por otras respuestas, sin embargo, std::accumulate es el mejor camino a seguir.

+0

+1, no pensé en eso. –

+3

Esta es una buena muestra de std :: accumulate emulation. Útil para propósitos de enseñanza. –

+0

Abajo voto comentario? No puedo mejorar si no sé lo que está mal. – GManNickG

3

Como solución general a este problema con STL: en lugar de pasar una función, puede pasar un functor - por ejemplo, una instancia de cualquier clase que implemente operator(). Esto es mucho mejor que depender de variables globales, ¡ya que dicha instancia puede mantener y actualizar su propio estado! Podrías pensar que es una especie de "compilación en tiempo de pato": la programación genérica no te obliga a pasar una "función" en ese lugar, cualquier cosa que "se comporte como una función" (es decir, tenga un operator() apropiado) hará así -)

3

no utilice for_each() para ello, utilice accumulate() de la cabecera <numeric>:

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

el comportamiento predeterminado de accumulate() es sumar elementos, pero puede proporcionar su propia función o funtor como lo hacemos aquí, y la operación que realiza no necesita ser asociativa: el segundo argumento es siempre el siguiente elemento para operar. Esta operación a veces se llama reduce en otros idiomas.

Puede usar una función simple en lugar del accum_sum_of_squares, o para obtener una mayor generalidad, puede hacer que accum_sum_of_squares sea una plantilla de clase que acepte cualquier tipo numérico.

32

No, no use std :: accumulate() use std :: inner_product(). No se requiere un functor.

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

Muy elegante ... ¡exactamente lo que estaba buscando! – Jacob

+3

¿Por qué esta no es la mejor respuesta? – math

3

std::for_each es para hacer algo con cada elemento. Si desea obtener un resultado de un cálculo en todos los elementos, hay std::accumulate. Si quiere el comportamiento de Haskell map, use std::transform.

Puede abusar de cualquiera de estos tres para hacer lo mismo que cualquiera de los otros, ya que en última instancia solo están iterando sobre un iterador (excepto en la forma transform que toma dos iteradores como entrada.) El punto es que for_each no es un reemplazo para map/fold - eso debe hacerse mediante transform/accumulate - aunque C++ no posee de forma nativa algo que exprese el concepto map/fold tan bien como Haskell lo hace - pero tanto gcc como VC++ admiten OpenMP que tiene una mucho mejor análogo en #pragma omp parallel for.

Inyectar en Ruby es una coincidencia mucho más cercana a llamar al for_each con un funtor completo, como GMan explicado anteriormente. Las funciones lambda con captura variable en C++ 0X harán que el comportamiento entre los dos idiomas, incluso más similares:

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

debe ser suma + = i * i; – mskfisher

Cuestiones relacionadas