std::accumulateを使う

#include とすれば、std::accumulate() が利用できるのか。これまたなんと便利な。。(涙)
これはコンテナの中の値の加算とか、統計をとったりするのに便利だ。っと、第4引数の二項演算子はどう使われるのだ?(以下、sgiのページより)

The function object binary_op is not required to be either commutative or associative: the order of all of accumulate's operations is specified. The result is first initialized to init. Then, for each iterator i in [first, last), in order from beginning to end, it is updated by result = result + *i (in the first version) or result = binary_op(result, *i) (in the second version).

なるほどな。std::accumulate が、その第4引数のbinary_opをどう使うかが問題なのだが、それは、二項演算子としてのbinary_op( x, y ) に対し、これがまた、
x = binary_op( x, y )
という具合に使われるということらしい。ここで気をつけてほしいのは、この一行は俺たちが書くのではなくて、accumulate()アルゴリズムの中では、俺達が書いたbinary_op がこのように使われるということだ。どうだいややこしく思えるかもしれないがそれほどではないよ。
つまり、binary_opは、第一引数がこれまでの総和、第二引数が「これから加算される値に関する情報を持っているオブジェクトつまり配列要素のオブジェクト(binary_opがこの配列要素数分と同じ回数だけ呼ばれるところの各要素のオブジェクト)」ということを前提として書き、この両者を使って更新される新しい総和を返すように書くのだ。
以上より、1から10の2乗の和の計算は以下の如くとなるか。

#include <iostream>
//#include <algorithm>
#include <vector>
//#include <iterator>
//#include <functional>
#include <numeric>

int main( void )
{
  using namespace std;
  vector< int > v;
  {
    for( int i=0; i<=10; ++i ){ v.push_back( i ); }
  }

  class MultiSum{
  public:
    int operator()( const int sum, const int val )
    { 
      return sum + val*val; // ★
    }
  };
  cout << std::accumulate( v.begin(), v.end(), 0, MultiSum() ) <<endl;
  return 0;
}


おー、ちゃんと「385」と計算されたか。よしよし。ようするに、★のval*valのところが、f(val)なのであると。MultiSumのところはラムダ式とかを使えばもう少しコンパクトに書けそうっぽい予感がするが今日のところは予感だけにとどめておくか。
ちなみに3番目の値は加算前の初期値。これと同じ型がaccumulateの返値となる。
で、実はこれはもっと簡単に書くことができて、そのためにはラムダ式を使う。ラムダ式は上記MultiSumのような関数オブジェクトをもっと簡潔に書くためのC++11の新しい文法だ。

  cout << std::accumulate( v.begin(), v.end(), 0,
                      []( int sum, int val ){ return sum + val*val; } ) <<endl;

この書き方のほうが、MultiSumをわざわざ定義する必要がなく、同じ内容をその場で手早く書けてしまうのではるかに便利でスマートだね。これで上記と同じ「385」が出力されるはずだ。
次に、構造体のvectorなり配列なりがあって、その中のあるメンバ値についての総和を計算したい場合はどうだろうか。

    // 生徒クラスの定義
    struct Student
    {
        Student( const int age ):_age(age){}
        int _age;// 年齢
    };
    // 例えば0歳児から10歳児の11人の生徒がいるとする
    vector< Student > svec;
    {
        for( int i=0; i<=10; ++i )
            svec.push_back( Student( i ) );
    }
    // 生徒の年齢の総和を計算、表示。
    int sum_age = std::accumulate( svec.begin(), svec.end(), 0,
                     []( int sum, Student& tak ){ return sum+tak._age; } );
    cout << "average_age: "<< sum_age <<endl;

という感じになるかな。答えは55と表示されたよ。
このようにaccumulateは4番目の引数の使い方を知ると超便利なんだね。みんなもどんどん使ってみてくれ。
チャオ!

C++ ポケットリファレンス

C++ ポケットリファレンス