#21 C++で学ぶコレクション操作の応用(フィルタ・マップ的な考え方)の基本
はじめに
C++でstd::vectorなどのコレクション(データをまとめて扱う入れ物)を操作していると、条件に合う要素だけを取り出したい場面によく出会います。ほかにも、全要素を変換したい、合計を求めたい、といった場面もよくあります。Pythonにはリスト内包表記、JavaScriptにはfilterやmap、reduceという専用の書き方がありますが、C++にはそれに相当する構文が言語自体にはありません。代わりに<algorithm>や<numeric>ヘッダの関数群と、ラムダ式(その場で定義できる無名の関数)を組み合わせて実現します。基本パターンを紹介します。
フィルタ:std::copy_ifで条件に合う要素だけ抜き出す
「偶数だけ取り出す」処理はPythonなら内包表記1行で書けますが、C++ではstd::copy_ifを使います。
#include <vector>
#include <algorithm>
#include <iterator>
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
std::vector<int> evens;
std::copy_if(nums.begin(), nums.end(), std::back_inserter(evens),
[](int n) { return n % 2 == 0; });
// evens = {2, 4, 6}
第3引数のstd::back_inserter(evens)は、要素を追加するたびに自動でpush_backしてくれる特殊なイテレータ(コレクションの要素を1つずつ指し示す仕組み)です。これを使わずに普通のイテレータを渡すと、コピー先の要素数が足りず未定義動作(何が起きるか保証されない危険な状態)になるので注意しましょう。最後の引数がラムダ式で、「条件を判定する処理」をその場で書けます。
マップ:std::transformで全要素を変換する
全要素に同じ処理を適用して新しいコレクションを作るにはstd::transformを使います。JavaScriptのmapに近い役割です。
std::vector<int> nums = {1, 2, 3};
std::vector<int> squared(nums.size());
std::transform(nums.begin(), nums.end(), squared.begin(),
[](int n) { return n * n; });
// squared = {1, 4, 9}
出力先squaredはnums.size()個分の要素数で確保しています。back_inserterを使わない場合、出力先のサイズが足りないと同様に未定義動作になるため、サイズ確保かback_inserterのどちらかを必ず選んでください。
リデュース:std::accumulateで集計する
合計や積などの集計処理は<numeric>ヘッダのstd::accumulateで行います。第3引数が初期値、第4引数(省略可)がラムダで、JavaScriptのreduceと同じ発想です。
#include <numeric>
std::vector<int> nums = {1, 2, 3, 4};
int sum = std::accumulate(nums.begin(), nums.end(), 0);
// sum = 10
積を求めたい場合は初期値を1にし、第4引数に[](int acc, int n) { return acc * n; }のようなラムダを渡します。初期値を0のままにすると結果が常に0になるので注意しましょう。
C++らしい注意点
PythonやJavaScriptの同種の関数は「新しい配列を返す」のが基本ですが、C++の<algorithm>系関数は「出力先をどこにするか」を自分で用意する設計です。処理速度とメモリ効率を重視するC++らしい、無駄なコピーを避ける考え方です。C++20以降のRangesライブラリではviews::filterのような直感的な書き方もできますが、まずは基本形を押さえておくと現場のコードを読み解けます。
まとめ
C++にはフィルタ・マップ・リデュースのための専用構文はありませんが、<algorithm>や<numeric>の関数とラムダ式を組み合わせることで同じ発想の処理を実現できます。std::copy_ifでフィルタ、std::transformでマップ、std::accumulateでリデュースという対応関係を覚え、出力先の確保に注意しながら書いてみてください。
関連記事
本ページはプロモーションを含みます。
参考書籍(PR)
C++をもっと学びたい方には「新・明解C++入門編」がおすすめです。
PR
コードを書かずに自動化したい方へ
「学んだけど自分の作業に組み込む時間がない」という方向けに、ダブルクリックで動くWindows専用の自動化ツールをオーダーメイドで作成しています。気になる方はこちらをご覧ください。
コメント
コメントを投稿