ラムダ式を使ってもう少しかっこよくstd::transformを使ってみる

先日の記事ではstd::transformの使い方を学んだ.しかし,既に用意された関数以外を利用する場合にはその都度関数を定義するのはダサい.どうやらC++ではラムダ式が使えるようになっていたらしいので使ってみたのだけれど,けっこうハマったのでメモ.

先日と同じようなコードをラムダ式を使って書いてみる.

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

#include <cmath>


int
main (int argc, char *argv[])
{

  std::vector<double> vec;
  vec.push_back( 1.0 );
  vec.push_back( 1.5 );
  vec.push_back( 2.0 );
  vec.push_back( 2.5 );

  std::transform(vec.begin(),
                 vec.end(),
                 vec.begin(),
                 std::ptr_fun<double, double>( std::exp ) );

  double sum = std::accumulate(vec.begin(), vec.end(), 0.0);

  //
  std::transform( vec.begin(), vec.end(),
                  vec.begin(),
                  ( [&sum] (double x) -> double { return (x / sum); } ) ); // sum変数を参照キャプチャ

  // 別の方法.こんな書き方もできる
  /*
  std::transform(vec.begin(), vec.end(),
                 vec.begin(),
                 std::bind2nd(
                              std::ptr_fun<double, double, double>( [] (double x, double s) -> double { return (x / s); } ),
                              sum ));
  */
                                         

  for (int i = 0; i < (int)vec.size(); i++) {
    std::cout << vec[ i ] << std::endl;
  }

  return 0;
}

ラムダ式 (<引数>) -> <返り値> {} で書くことができる.ただし,スコープ外部の変数を参照することができないため,最初のの中に参照したい変数を記述する.無印で記述すれば値渡し (すなわち,内部で変更しても外部の変数には影響がない),&を付与すれば参照渡しになる.

コンパイルする際には g++ std=c++0x とstdオプションをつける必要がある.これにけっこうハマった.


なお読み出し元のコンテナとは別のコンテナに値を確保する場合には少しだけ注意が必要.格納先のコンテナにも要素数の大きさのサイズが必要になるため,あらかじめ確保しておく必要がある.

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

#include <cmath>


int
main (int argc, char *argv[])
{

  std::vector<double> vec;
  vec.push_back( 1.0 );
  vec.push_back( 1.5 );
  vec.push_back( 2.0 );
  vec.push_back( 2.5 );


  std::vector<dobule> vec2;
  
  // エラー: vec2のサイズが不足
  std::transform(vec.begin(),
                 vec.end(),
                 vec2.begin(),
                 std::ptr_fun<double, double>( std::exp ) );

  std::vector<double> vec3( vec.size() );

  // OK
  std::transform(vec.begin(),
                 vec.end(),
                 vec3.begin(),
                 std::ptr_fun<double, double>( std::exp ) );