第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::equal | 2 つの範囲が同じか |
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 もあり。