C++と色々

主にC++やプログラムに関する記事を投稿します。

部分適用された関数

ScalaにかこつけたC++の紹介

Scalaには部分適用された関数というものがあります。Scalaでは必要な引数を渡して関数を呼び出すことを、関数を引数に適用する(apply)と表現します。例えば:

def sum(a: Int, b: Int, c: Int) = a + b + c

という関数があったとして、

sum(1, 2, 3) // 6

は、1, 2, 3という引数にsumを適用しています。部分適用された関数とは関数が必要とする引数を全て渡していない関数呼び出し式です。必要な引数の一部または全てを渡していない状態です。すべての引数を適用していない部分適用された関数を作るには、関数の後ろにアンダースコアを書きます。

val s = sum _

こうすることで全ての引数を元の関数へ転送する、元の関数をラップした関数オブジェクトを作ることが出来ます。Scalaメソッドとローカル関数は変数に代入したり、他の関数の引数にすることは出来ませんが、このように部分適用することで関数オブジェクトとして扱うことが出来ます。

また、一部の引数に対する関数の適用は以下のように書くことが出来ます。

val s = sum(1, _: Int, 3)
s(2) // 6

これと同じことをC++std::bindstd::functionラムダ式で行うことが出来ます。C++で以下の様な関数があるとします

auto sum(int a, int b, int c) { return a + b + c; }

これは以下のように呼び出せます。

sum(1, 2, 3); // 6

これを関数オブジェクトにする方法を3通りを以下に示します。

#include <functional>
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;

auto sum(int a, int b, int c) { return a + b + c; }

int main()
{
    auto const a = std::bind(sum, _1, _2, _3); // (1
    auto const b = [](int a, int b, int c) { return sum(a, b, c); }; // (2
    auto const c = std::function<int (int, int, int)>(sum); // (3
}

std::bindは関数ポインタ、関数オブジェクトから、まさに部分適用された関数オブジェクトを作り出します。プレースホルダーがいくつまであるかは実装依存となります(少なくとも_2までは保証しています)。 std::bindScalaの最後の例と同じことが可能です。

auto const s = std::bind(sum, 1, _1, 3);
s(2); // 6