1. ホーム
  2. c++

[解決済み] std::accumulateを理解する

2022-03-07 14:37:14

質問

なぜか知りたい std::accumulate (aka reduce) 3番目のパラメータが必要です。をご存じない方のために accumulate は、このように使います。

vector<int> V{1,2,3};  
int sum = accumulate(V.begin(), V.end(), 0);
// sum == 6

への呼び出し accumulate とは同等である。

sum = 0;  // 0 - value of 3rd param
for (auto x : V)  sum += x;

また、オプションで第4パラメータがあり、加算を他の操作に置き換えることができます。

その理由は、例えばベクトルの要素を加算するのではなく、乗算する必要がある場合、他の(ゼロでない)初期値が必要だからだと聞いたことがあります。

vector<int> V{1,2,3};
int product = accumulate(V.begin(), V.end(), 1, multiplies<int>());

しかし、Pythonのように、初期値を V.begin() から始まる範囲を使用します。 V.begin()+1 . このような感じです。

int sum = accumulate(V.begin()+1, V.end(), V.begin());

これは、どのようなオペでも動作します。なぜ第3パラメータが必要なのでしょうか?

解決方法は?

このままでは、ある範囲が空でないことを確実に知っていて、その範囲の最初の要素から累積を開始したいコードにとって迷惑な話です。積算に使用する演算によっては、使用すべき「ゼロ」値が必ずしも明らかではありません。

一方、空でない範囲を必要とするバージョンしか提供しない場合、呼び出し側にとって、自分の範囲が空でないことを確実に知らないのは迷惑な話です。追加の負担がかかるのです。

一つの視点として、両方の機能を提供することがベストであることは言うまでもありません。例として、Haskellは foldl1foldr1 (空でないリストを必要とする) と共に foldlfoldr (これは std::transform ).

もうひとつの視点は、一方は些細な変換で他方から実装できるので(あなたが実証してくれたように。 std::transform(std::next(b), e, *b, f) -- std::next はC++11ですが、要点は同じです)、表現力を失うことなく、できる限りインターフェースを小さくすることが望ましいのです。