2006/10/01

std::accumulate

accumulate "laskee yhteen" elementit, jotka sille annetaan.

Funktiosta on kaksi versiota, joista jälkimmäinen käyttää funktiota operator+ __binary_op:na. Laskeminen aloitetaan alkuarvosta __init.
_Tp std::accumulate(_InputIterator __first,
_InputIterator __last,
_Tp __init,
_BinaryOperation __binary_op
)

_Tp std::accumulate(_InputIterator __first,
_InputIterator __last,
_Tp __init
)
Algoritmin käyttäminen on helppoa, kun säiliössä on tavallisia numeroita:
#include <iostream>
#include <numeric>
#include <list>

int main()
{
std::list<float> lista;
lista.push_back(3);
lista.push_back(4);
lista.push_back(4);

std::cout << std::accumulate(lista.begin(), lista.end(), 1) << std::endl;
}

Ohjelma antaa kääntyessään varoituksen:
In function ‘_Tp std::accumulate(_InputIterator, _InputIterator, _Tp) [with _InputIterator = std::_List_iterator<float>, _Tp = int]
warning: converting to ‘int’ from ‘float’


Se korjaantuukin vaihtamalla 1:n tilalle static_cast<float>(1).

Ohjelma tulostaa 12 (3+4+4 = 11 ja alkuarvona 1).

Accumulaten käyttö oman luokan kanssa

Jos yhteen halutaan laskea muutakin kuin perustietotyyppejä täytyy määritellä operator+. Jos laskettavana on luokkia X, täytyy operator+ määritellä seuraavasti int operator+(int, const X &).

Tässä luokka, jonka sisältöä lasketaan:

class X {
public:
X(int i):m_i(i) { }
int get_i() const { return m_i; }
private:
int m_i;
};

operator+

Esimerkki:

int operator+(int n, const X &x) { return n + x.get_i(); }

std::vector<X> y;
y.push_back(1);
y.push_back(2);
int n = std::accumulate(y.begin(), y.end(), 0); // palauttaa 3

Tämä ei kuitenkaan toimi, jos säiliössä on osoittimia. Katso kaksi seuraavaa.

Muu funktio

Määritetään funktio, joka ottaa kaksi parametriä. Tässä on vikana, että se sotkee nimiavaruutta ja jos se määritellään väärässä paikassa niin sitä ei välttämättä osaa yhdistää oikeaan tarkoitukseen.

int plus(int i, X *x) {
return i + x->get_i();
}
Siirretäänkin se X-luokan funktioksi. Tällöin päästääm myös käsiksi m_i-muuttujaan ilman getteriä.

class X {
public:
static int plus(int i, X *x) {
return i + x->m_i;
}
};
std::vector<X*> x;
int n = std::accumulate(x.begin(), x.end(), 0, X::plus);

TR1

Laskeminen voidaan myös hoitaa C++:n seuraavaan standardiin lisättävien komponenttien avulla, jotka löytyvät GCC 4:stä. (TR1)

Boost-kirjastoon tutustuneille koodi näyttää varmasti heti selvältä. Tämän ratkaisun huonoja puolia ovat verrattain pitkä kääntymisaika johtuen massiivisesta mallien (template) sekä ylikuormitusten käytön takia.

int n = std::accumulate(x.begin(), x.end(), 3,
std::tr1::bind(std::plus<int>(),
std::tr1::placeholders::_1,
std::tr1::bind(&X::get_i,
std::tr1::placeholders::_2)));

Esimerkkiohjelman voit ladata täältä: http://pastebin.ca/raw/186941

Ei kommentteja: