2009-08-25 6 views
10

Comme la fonction acceptée par for_each prendre un seul paramètre (l'élément du vecteur), je dois définir un static int sum = 0 quelque part de sorte qu'il est possible d'accéder après avoir appelé le for_each . Je pense que c'est gênant. Une meilleure façon de faire cela (toujours utiliser for_each)?somme des carrés de chaque élément dans le vecteur en utilisant 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; 
} 

Dans Ruby, nous pouvons le faire de cette façon:

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

Voulez-vous s'il vous plaît montrer plus d'exemples comment for_each est généralement utilisé dans la programmation pratique (non seulement imprimer chaque élément)? Est-il possible d'utiliser for_each simuler 'schéma de programmation' comme carte et injecter dans Ruby (ou carte/plier dans 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: Merci à tous. J'ai tellement appris de vos réponses. Nous avons tellement de façons de faire la même chose en C++, ce qui le rend un peu difficile à apprendre. Mais il est intéressant :)

Répondre

18

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

Vous pouvez imiter std :: accumuler comme dans nice GMan's answer, mais je crois que l'utilisation std :: Accumulez rendra votre code plus lisible, car il a été conçu pour ces fins. Vous pouvez trouver plus d'algorithmes standard here.

+1

Je ne pense pas que les itérateurs vectoriels soient dans l'espace de noms std. Si c'est correct, ADL n'est pas garanti pour fonctionner ici, et l'interrogateur n'a pas spécifié de compilateur. –

+2

Vous avez raison. Je viens de vérifier - standard ne garantit pas que itérateur fait partie de l'espace de noms std. Seuls les itérateurs inverses font partie de l'espace de noms 'std'. –

+0

onebyone: Wow, bonne prise. J'ai vérifié la norme et vous avez absolument raison. Ainsi, par exemple Si le vecteur :: iterator était typedefed à T *, ADL échouerait en effet. -1ing pour obtenir l'attention de l'OP ... (Le poste est par ailleurs excellent BTW.) –

7

for_each renvoie (une copie de) le foncteur qu'il utilisait. Donc, quelque chose comme ceci:

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

Comme il est démontré par d'autres réponses, cependant, std::accumulate est la meilleure façon d'aller.

+0

+1, je n'ai pas pensé à ça. –

+3

Ceci est un bon exemple d'émulation std :: accumulate. Utile à des fins d'enseignement. –

+0

Diminuer le commentaire? Je ne peux pas améliorer si je ne sais pas ce qui ne va pas. – GManNickG

3

En tant que solution générale à ce problème avec STL: au lieu de passer une fonction, vous pouvez passer un functor - par exemple, une instance de n'importe quelle classe implémentant operator(). C'est beaucoup mieux que de se fier aux variables globales, puisque cette instance peut garder et mettre à jour son propre état! Vous pourriez le considérer comme une sorte de "compilation de dard de temps": la programmation générique ne vous contraint pas à passer une "fonction" à cet endroit, tout ce qui "se comporte comme une fonction" (c'est-à-dire un bon operator()) ainsi -)

3

ne pas utiliser for_each() pour cela, utilisez accumulate() de l'en-tête <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; 
} 

le comportement par défaut de accumulate() est de résumer les éléments, mais vous pouvez fournir votre propre fonction ou foncteur comme nous le faisons ici, et l'opération qu'il effectue n'a pas besoin d'être associative - le 2ème argument est toujours l'élément suivant à opérer. Cette opération est parfois appelée reduce dans d'autres langues.

Vous pouvez utiliser une fonction simple à la place du foncteur accum_sum_of_squares, ou pour encore plus de généricité, vous pouvez créer accum_sum_of_squares un modèle de classe qui accepte n'importe quel type numérique.

32

Non, n'utilisez pas std :: accumulate() utilisez std :: inner_product(). Aucun foncteur requis.

#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

Très élégant ... exactement ce que je cherchais! – Jacob

+3

Pourquoi ce n'est pas la meilleure réponse? – math

3

std::for_each est de faire quelque chose avec chaque élément. Si vous voulez obtenir un résultat d'un calcul sur tous les les éléments, il y a std::accumulate. Si vous voulez le comportement map de Haskell, utilisez std::transform.

Vous pouvez abuser l'un ou l'autre de ces trois pour faire la même chose que les autres, puisqu'enfin ils ne font que répéter sur un itérateur (excepté le formulaire transform qui prend deux itérateurs en entrée.) Le fait est que for_each ne remplace pas map/fold - cela devrait être fait par transform/accumulate - bien que C++ ne possède pas nativement quelque chose qui exprime le concept map/fold aussi bien que Haskell - mais gcc et VC++ supportent OpenMP qui a un beaucoup mieux analogique dans #pragma omp parallel for. Injecter en Ruby est beaucoup plus proche d'appeler for_each avec un foncteur à part entière, comme GMan expliqué ci-dessus. Les fonctions Lambda avec capture de variables en C++ 0X rendront le comportement entre les deux langues encore plus similaire:

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

devrait être sum + = i * i; – mskfisher

Questions connexes