【C++】transform - map関数の実現【関数型プログラミング】

C++の標準ライブラリには関数プログラミングで言うところのmap関数やcollect関数が存在しませんが、代わりの機能で代替が可能です。また後半ではmap関数を自作する方法も紹介します。

transform関数

transformテンプレート関数は指定された要素範囲に対して関数オブジェクトを逐次適用してくれる機能です。これによってmap関数の代替処理が実現できます。

// #include <algorithm> // transform
std::vector<int> v = {1, 2};
std::transform(v.begin(), v.end(), v.begin(), [](int i) {
   return i * 2;
});

v; // {2, 4}

第三引数には出力先のオブジェクトを指定することが可能です。また第四引数にはラムダ式やファンクタ等の関数オブジェクトのほか、関数ポインタを渡すことが可能です。

// #include <ctype.h> // toupper
const std::vector<char> v = {'a', 'b'};
/* */ std::vector<char> w(v.size());
std::transform(v.begin(), v.end(), w.begin(), toupper);

v; // {'a', 'b'}
w; // {'A', 'B'}

次のような挿入イテレータ(back_inserter)を活用する方法もあります。

// #include <iterator> // back_inserter
std::vector<char> w, v = {'a', 'b'};
w.reserve(v.size());

std::transform(v.begin(), v.end(), std::back_inserter(w), toupper);
reserveメンバ関数で事前にキャパシティを指定しておくとより効率的な動作が期待できます。

map関数の実現

非破壊的なmap関数の作り方です。引数をイミュータブルなオブジェクトとして扱います。戻り値は新規のオブジェクトです。

template<class Container, class Transform>
Container map(const Container& c, Transform f) {
  using std::begin;
  using std::end;
  Container r;
  r.reserve(c.size());
  std::transform(begin(c), end(c), std::back_inserter(r), f);
  return r;
}
const auto v = std::vector<char>{'a', 'b'};
assert('B' == map(v, toupper).back());
assert('b' == map(v, [](char c) { return ++c; })[0]);
assert("HELLO" == map(std::string("hello"), toupper));
広告