for_eachで平均・分散を求める
配列内のデータの平均・分散を算出するには通常以下のようなプログラムを書く
double avg = 0.0; for(size_t i=0; i<data.size(); i++){ avg += data[i];} // 合計を計算 avg /= data.size(); // 総数で割って平均値 double var = 0.0; for(size_t i=0; i<data.size(); i++) { var += (data[i] - avg)*(data[i] - avg);} // 平均との差分を2乗 var /= data.size(); // 総数で割って分散値
これで読みやすい人はそれで良いけども、何も知らない人にはちょっと読みにくい。
そこで以下のような書き方を推奨。
まず
class calc_var{ const double avg; double sum; public: calc_var(const double& a):avg(a),sum(0.0){} inline void operator()(const double& dat){ double diff = dat-avg; sum+=diff*diff; } const double& get()const{ return sum;} };
このように平均値をセットして差分の二乗を合計する関数オブジェクトを用意しておいて
#include <numeric> #include <algorithm> // 平均値はnumeric内のaccumulateを使う const double avg = std::accumulate(data.begin(),data.end(),0.0) / data.size(); // 分散値はaccumulateでは出来ないのでfor_eachを使う const double var = std::for_each(data.begin(), data.end(), calc_var(avg)).get() / data.size();
for_eachで関数オブジェクトを平均値付きで渡す。
for_eachは「範囲内の要素全てに関数を適用する」という意味では直感的だけど、そこから戻り値を獲得する為にはひと工夫必要。
for_eachの戻り値は「範囲内の要素全てを適用したあとの関数オブジェクト」なので、その戻り値自体をクラスとしてメンバ関数を呼び出せる。
ここではfor_eachで合計値を算出させ、合計結果を獲得するメンバ関数getを用意している。
述語として渡す関数はステートレスな物が推奨されているけれど、この場合は例外?
(ネタがだんだんtrivialになっていく