【C++STL学習と応用まとめ】86:std::accumulateの使い方
このシリーズの文章の目次はここです:目次.カタログを通してSTLの全体的な理解を得ることができます
前言
本文はSTLアルゴリズムの中で、数値類アルゴリズム(numeric algorithm)の中の最初のアルゴリズム:std::accumulateの使用、および注意事項をまとめた.
基本的な使い方
まずaccumulateを使用する簡単な例を見てみましょう.
この例では、accumulateは3つのパラメータを受信、1対の反復器は開始区間と終了区間を識別するために用いられ、3番目のパラメータ0はaccumulate動作の初期値である.accumulateは[begin,end)という区間を遍歴し,各値を0という初期値の上に累積し,最終的に累積終了値(0+1+2+3)==6を返す.
一般的な使い方
最初の例はaccumulateの特例の場合にすぎませんが、実際には累積操作を完了するだけでなく、より一般的な意味を理解できると思います.
1つの区間と初期値initとオプションの操作関数opが与えられ、initと同じタイプの結果が返され、この結果は、与えられた区間内の各要素を1つずつ蓄積してop操作でinitに作用することによって得られる.
opは2元操作関数である、デフォルトのopは
次に、そのプロトタイプを示します.
可能な実装は次のとおりです.
従って、第1のプロトタイプと用法は第2の特例にすぎず、accumulateのより一般的な用法は操作関数opを指定することであると言える.
最初の例を書き換えることができます
結果は同じですが、2つ目の例はもっと一般的な使い方です.
accumulateの戻り値.(注意)
accumulateのプロトタイプから,initは値で伝達され,呼び出し完了後に局所変数initの値は変更されていないことがわかる.
値が累積値に等しいようにするには、accumulateの戻り値を受信する必要があります.
したがって、次の例のinitはaccumulateを呼び出した後も変更されません.
initは、最初の呼び出しが完了した後も0である.では、戻り値を受け入れずにinitを修正して、引用を伝えたいのではないでしょうか.
この問題は後で議論し、referenceを使用します.wrapperはinitを包装してみます.
その他の例
反復器区間は特に言う必要はなく,初期値の選択もまあまあだが,他の非数値型のアルゴリズムと同様にaccumulateの柔軟性用法の鍵はop操作関数の選択にあり,関数オブジェクト(functors)のようなものはすべてここに詰めることができ,以下はaccumulateのいくつかの実用的な用法を簡単から複雑に与えることができる.
関数、関数オブジェクト、lambda、bind関数の組合せなどを使用する
以上のテストは著者の環境テストに合格した.
さらに例を挙げると、この過程はfunctorの特別な場になり、op操作を組み合わせてaccumulateから遠ざかっていることがわかり、これで止めます.
最後にmapでaccumulateを使った例を示しますが、面白いと思います.
mapには様々な動物-数のマッピングがあり、accumulateを使用して動物の総数を統計します.
実はこの例は統計Accountの中のお金の数の例と本質的な違いはなく、バインドされたクラスメンバー変数secondが2層ネストされているだけだ.
accumulateのinitパラメータの変更の試み
2つのタイプの
ソースおよびリファレンスリンクソースコード:accumulate_test.cpp accumulate
作者のレベルは有限で、関连する知识の理解と総括に対してどうしても间违いがあって、また指摘することを望んで、とても感谢します!
githubブログへようこそ、当駅と同期して更新します
前言
本文はSTLアルゴリズムの中で、数値類アルゴリズム(numeric algorithm)の中の最初のアルゴリズム:std::accumulateの使用、および注意事項をまとめた.
基本的な使い方
まずaccumulateを使用する簡単な例を見てみましょう.
vector<int> vi{1, 2, 3};
cout << accumulate(vi.begin(), vi.end(), 0); // 6
この例では、accumulateは3つのパラメータを受信、1対の反復器は開始区間と終了区間を識別するために用いられ、3番目のパラメータ0はaccumulate動作の初期値である.accumulateは[begin,end)という区間を遍歴し,各値を0という初期値の上に累積し,最終的に累積終了値(0+1+2+3)==6を返す.
一般的な使い方
最初の例はaccumulateの特例の場合にすぎませんが、実際には累積操作を完了するだけでなく、より一般的な意味を理解できると思います.
1つの区間と初期値initとオプションの操作関数opが与えられ、initと同じタイプの結果が返され、この結果は、与えられた区間内の各要素を1つずつ蓄積してop操作でinitに作用することによって得られる.
opは2元操作関数である、デフォルトのopは
+
演算である、これが第1例が累積を実行する理由である.次に、そのプロトタイプを示します.
// 1. op
template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init);
// 2. op
template <class InputIterator, class T, class BinaryOperation>
T accumulate (InputIterator first, InputIterator last, T init,
BinaryOperation binary_op);
可能な実装は次のとおりです.
template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init)
{
while (first!=last) {
init = init + *first; // or: init=binary_op(init,*first) , op
++first;
}
return init;
}
従って、第1のプロトタイプと用法は第2の特例にすぎず、accumulateのより一般的な用法は操作関数opを指定することであると言える.
最初の例を書き換えることができます
vector<int> vi{ 1, 2, 3 };
// op : plus<int>()
cout << accumulate(vi.begin(), vi.end(), 0, plus<int>()); // 6
結果は同じですが、2つ目の例はもっと一般的な使い方です.
accumulateの戻り値.(注意)
accumulateのプロトタイプから,initは値で伝達され,呼び出し完了後に局所変数initの値は変更されていないことがわかる.
値が累積値に等しいようにするには、accumulateの戻り値を受信する必要があります.
したがって、次の例のinitはaccumulateを呼び出した後も変更されません.
vector<int> vi{ 1, 2, 3 };
int init(0);
accumulate(vi.begin(), vi.end(), init, plus<int>());
EXPECT_EQ(0, init); // test pass
init = accumulate(vi.begin(), vi.end(), init, plus<int>());
EXPECT_EQ(6, init); // test pass
initは、最初の呼び出しが完了した後も0である.では、戻り値を受け入れずにinitを修正して、引用を伝えたいのではないでしょうか.
この問題は後で議論し、referenceを使用します.wrapperはinitを包装してみます.
その他の例
反復器区間は特に言う必要はなく,初期値の選択もまあまあだが,他の非数値型のアルゴリズムと同様にaccumulateの柔軟性用法の鍵はop操作関数の選択にあり,関数オブジェクト(functors)のようなものはすべてここに詰めることができ,以下はaccumulateのいくつかの実用的な用法を簡単から複雑に与えることができる.
関数、関数オブジェクト、lambda、bind関数の組合せなどを使用する
int func(int i, int j)
{
return i + j;
}
struct Functor
{
int operator () (int i, int j)
{
return i + j;
}
};
RUN_GTEST(NumericAlgorithm, MoreExamples, @);
vector<int> vi{ 1, 2, 3 };
EXPECT_EQ(6, accumulate(vi.begin(), vi.end(), 0, func)); //
EXPECT_EQ(6, accumulate(vi.begin(), vi.end(), 0, Functor())); //
EXPECT_EQ(6, accumulate(vi.begin(), vi.end(), 0, [](int i, int j) ->int {return i + j;})); // lambda
// : init + v[i] * 2
int res = accumulate(vi.begin(), vi.end(),
0,
bind(plus<int>(), _1,
bind(multiplies<int>(), _2, 2)
)
);
EXPECT_EQ(12, res);
// or use lambda. lambda
res = accumulate(vi.begin(), vi.end(), 0, [](int i, int j) ->int { return i + 2*j; });
EXPECT_EQ(12, res);
//
struct Account
{
int money;
};
vector<Account> va = {Account{1}, Account{100}, Account{}};
int total = accumulate(va.begin(), va.end(),
0,
bind(plus<int>(), _1,
bind(&Account::money, _2)
)
);
EXPECT_EQ(101, total);
END_TEST;
以上のテストは著者の環境テストに合格した.
さらに例を挙げると、この過程はfunctorの特別な場になり、op操作を組み合わせてaccumulateから遠ざかっていることがわかり、これで止めます.
最後にmapでaccumulateを使った例を示しますが、面白いと思います.
mapには様々な動物-数のマッピングがあり、accumulateを使用して動物の総数を統計します.
RUN_GTEST(NumericAlgorithm, AdvancedUse, @);
map<string, int> m;
m.insert({ "dog", 3 });
m.insert({ "cat", 2 });
m.insert({ "fox", 1 });
m.insert({ "crow", 2 });
int animals(0);
animals = accumulate(m.begin(), m.end(),
0,
bind(plus<int>(), _1,
bind(&map<string, int>::value_type::second, _2)
)
);
EXPECT_EQ(8, animals); // animail totoal count is 8
END_TEST;
実はこの例は統計Accountの中のお金の数の例と本質的な違いはなく、バインドされたクラスメンバー変数secondが2層ネストされているだけだ.
accumulateのinitパラメータの変更の試み
RUN_GTEST(NumericAlgorithm, TryToChangeInit, @);
// try1. ref int init, , :accumulate `init = op(init, *first)`
// :reference_wrapper 。 ? ref ?
// class 。 Addable
vector<int> vi{ 1, 2, 3 };
int init = 0;
//accumulate(vi.begin(), vi.end(), ref(init)); // compile error
//EXPECT_NE(0, init);
// try2.
class Addable
{
public:
Addable(int i=0) :i_(i) {}
Addable operator + (const Addable& other) const
{
Addable a;
a.i_ = i_ + other.i_;
return a;
}
int i_{ 0 };
};
Addable inita;
EXPECT_EQ(0, inita.i_);
vector<Addable> aa = {Addable(1), Addable(2), Addable(3)};
// ref Addable , reference_wrapper 。
//accumulate(aa.begin(), aa.end(), ref(inita), plus<Addable>()); // also error.
//EXPECT_NE(0, inita.i_);
END_TEST;
2つのタイプの
reference_wrapper<int> reference_wrapper<Addable>
で試してもコンパイルできなかったので、reference_をさらに理解する必要があります.wrapper、しばらくaccumulate内部で参照によって変数initを修正することに成功しませんでした.ソースおよびリファレンスリンク
作者のレベルは有限で、関连する知识の理解と総括に対してどうしても间违いがあって、また指摘することを望んで、とても感谢します!
githubブログへようこそ、当駅と同期して更新します