C++ Learning

第60回 述語・集計系アルゴリズム

述語(bool を返す関数)で判定する all_of / any_of / none_of、各要素に処理をかける for_each、最値を取る min_element / max_element / minmax_element。日常的にもっともよく使う部類。

1. 一覧

関数意味
std::all_of全要素が述語を満たすか
std::any_of一つでも満たすか
std::none_of一つも満たさないか
std::count / count_if個数
std::for_each各要素に関数を適用(戻り値なし)
std::min_element / max_element最小・最大のイテレータ
std::minmax_element最小と最大のペア
std::equal2 つの範囲が同じか
std::mismatch最初に一致しない位置

2. all_of / any_of / none_of

三兄弟述語判定
std::vector<int> v = {2, 4, 6, 8}; bool all_even = std::all_of(v.begin(), v.end(), [](int x){ return x % 2 == 0; }); // true bool any_neg = std::any_of(v.begin(), v.end(), [](int x){ return x < 0; }); // false bool no_zero = std::none_of(v.begin(), v.end(), [](int x){ return x == 0; }); // true // 空のコンテナでは: // all_of → true (空虚な真) // any_of → false // none_of → true

3. for_each ― range-for の代替

for_each関数適用
std::vector<int> v = {1,2,3,4,5}; // 各要素を出力 std::for_each(v.begin(), v.end(), [](int x){ std::cout << x << " "; }); // 部分範囲に適用 std::for_each(v.begin() + 1, v.begin() + 4, [](int& x){ x *= 10; }); // v[1..3] だけ 10 倍

全体を回すだけなら範囲 for(for (int x : v))の方が読みやすい。for_each が勝る場面は「部分範囲に適用したい」ケース。

4. 最値の取得

min/max_element最値
std::vector<int> v = {3, 1, 4, 1, 5, 9}; auto mn = std::min_element(v.begin(), v.end()); auto mx = std::max_element(v.begin(), v.end()); std::cout << *mn; // 1 std::cout << *mx; // 9 // カスタム比較 auto shortest = std::min_element( words.begin(), words.end(), [](auto& a, auto& b){ return a.size() < b.size(); });
minmax_element両方同時
auto [mn, mx] = std::minmax_element( v.begin(), v.end()); // 1 回の走査で両方取得 // min_element + max_element より速い

5. equal / mismatch ― 範囲比較

equal / mismatch比較
std::vector<int> a = {1,2,3,4}; std::vector<int> b = {1,2,3,5}; bool same = std::equal(a.begin(), a.end(), b.begin()); // false auto [it1, it2] = std::mismatch( a.begin(), a.end(), b.begin()); // 最初に違う位置のペア // *it1 = 4, *it2 = 5

確認クイズ

Q1. 空の vector に all_of を適用すると?

false
true(空虚な真)
未定義動作
例外
空集合に対する全称命題は常に真(vacuous truth)。all_of は true、any_of は false、none_of は true。

Q2. min と max を同時に取りたいとき一番効率が良いのは?

sort して先頭と末尾
min_element と max_element を 2 回
minmax_element(1 回の走査で両方)
手書きループ
minmax_element は約 1.5n 回の比較で両方取れます。2 回 走査より効率的。

Q3. 範囲 for と for_each の使い分けとして正しいのは?

常に for_each が速い
全体なら範囲 for、部分範囲なら for_each
範囲 for は C++17 以降限定
for_each はコピーを作る
全体を回すだけなら範囲 for が簡潔。for_each(v.begin()+1, v.begin()+5, ...) のように部分範囲に適用したいときは for_each が便利。

Q4. vector が特定の値を含むかチェックする最もシンプルな方法は?

std::count > 0
std::find != end() または std::any_of(..., == val)
手書きループ
std::sort + binary_search
find != end() が最短で O(n)。count だと全走査が強制されるので「存在するか」だけなら find が効率的。C++20 以降は std::ranges::contains もあり。
← 前の講座
第59回 順列・シャッフル