2010-07-08 11 views
10

Powiedzmy mam następujący obiekt:Jaki jest najlepszy sposób na zsumowanie wyniku funkcji składowej dla wszystkich elementów w kontenerze?

struct Foo 
{ 
    int size() { return 2; } 
}; 

Jaki jest najlepszy sposób (najbardziej utrzymaniu, czytelne, itd.), Aby uzyskać całkowitą size wszystkich obiektów w vector<Foo>? Opublikuję moje rozwiązanie, ale interesują mnie lepsze pomysły.

Aktualizacja:

tej pory mamy:

  • std :: gromadzić i funktor
  • std :: gromadzić i wyrażenie lambda
  • zwykły ol”dla pętli

Czy istnieją inne możliwe do zastosowania rozwiązania? Czy możesz stworzyć coś możliwego do utrzymania przy użyciu boost::bind lub std::bind1st/2nd?

+3

'std :: wektora vec; vec.size() * 2', ponieważ wiemy, że 'Foo :: size' zawsze zwraca 2. :) – jalf

Odpowiedz

23

Oprócz własnych sugestii, jeśli kompilator C++ 0x obsługuje wyrażeń lambda, można użyć tej krótszą wersję:

std::vector<Foo> vf; 

// do something to populate vf 


int totalSize = std::accumulate(vf.begin(), 
           vf.end(), 
           0, 
           [](int sum, const Foo& elem){ return sum + elem.size();}); 
+0

literówka: średnik brakuje na końcu ciała lambda (nie mogę edytować siebie). – rafak

7

Użyj std::accumulate i funktora.

#include <functional> 
#include <numeric> 

struct SumSizes : public std::binary_function<int, Foo, int> 
{ 
    int operator()(int total, const Foo& elem) const 
    { 
     return total + elem.size(); 
    } 
}; 

std::vector<Foo> vf; 

// do something to populate vf 

int totalSize = std::accumulate(vf.begin(), 
           vf.end(), 
           0, 
           SumSizes()); 
+0

Twoje rozwiązanie jest oczywiście najbardziej idiomatyczne, ale pętla iteratora może być łatwiejsza w takich prostych przypadkach. – Philipp

+0

+1 Zostałaby poprawiona przez szablon 'SumSizes' dla generyczności, ponieważ wszystkie standardowe kontenery mają funkcję składową' size() '. –

+0

@Jon, myślę, że prawdopodobnie źle zrozumiałeś pytanie. Nie chodziło o to, aby uzyskać rozmiar pojemnika, ale o sumę wyniku funkcji składowej wszystkich elementów. Być może "rozmiar" był kiepską nazwą takiej funkcji. –

4

Oto rozwiązanie down-to-earth:

+0

O wiele łatwiejsze do odczytania niż inne, funkcjonalne rozwiązania. – Jon

7

Znajduję wzmacniające iteratory elegants, chociaż mogą być nieco szczegółowe (algorytmy oparte na odległościach sprawiają, że jest to lepsze). W tym przypadku transform iterators może wykonać zadanie:

#include <boost/iterator/transform_iterator.hpp> 
//... 

int totalSize = std::accumulate(
    boost::make_transform_iterator(vf.begin(), std::mem_fn(&Foo::size)), 
    boost::make_transform_iterator(vf.end(), std::mem_fn(&Foo::size)),0); 

Edycja: zastąpiona "boost::bind(&Foo::size,_1)" przez "std::mem_fn(&Foo::size)"

Edit: Właśnie okazało się, że biblioteka Boost.Range został zaktualizowany do wprowadzenia algorytmów zasięgu! Oto nowa wersja tego samego roztworu:

#include <boost/range/distance.hpp> // numeric.hpp needs it (a bug?) 
#include <boost/range/numeric.hpp> // accumulate 
#include <boost/range/adaptor/transformed.hpp> // transformed 
//... 
int totalSize = boost::accumulate(
    vf | boost::adaptors::transformed(std::mem_fn(Foo::size)), 0); 

Uwaga: występy są w przybliżeniu takie same (patrz mój komentarz): wewnętrznie, transformed wykorzystuje transorm_iterator.

+1

Zrobiłem synchronizację porównując to rozwiązanie i bezpośrednie, i niestety ten jest wolniejszy (znalazłem czynnik między 2 a 5). Jednak nie może to być problemem. – rafak

+0

Myślę, że to najlepsza odpowiedź. Problem polega na ** tym, co ** gromadzić, które jest adresowane przez niestandardowy iterator, a nie ** jak ** gromadzić, które jest adresowane za pomocą funktora. Domyślne zachowanie akumulacji (plus) _jest_, co chcesz. Rozważ rozszerzenie tego problemu na wewnętrzny produkt: transformowany iterator jest wielokrotnego użytku, podczas gdy funktor nie jest. Nowy funktor dla każdego algorytmu byłby wymagany po prostu do przedefiniowania domyślnego zachowania pod względem rozmiaru członka(). –

4

przy użyciu C++ 11 (oraz poza) Zakres na bazie pętli

std::vector<Foo> vFoo; 
// populate vFoo with some values... 
int totalSize = 0; 
for (const auto& element: vFoo) { 
    totalSize += element.size(); 
} 
Powiązane problemy