PHP でも関数型っぽく書く

なんとなく表題の事をやってみた。 とりあえず、

  • 関数合成
  • 部分適用

があればなんとかなりそうなので、作ってみる。

// 関数合成
function compose()
{
    $fns = func_get_args();
    $revFns = array_reverse($fns);
    return function ($initial) use ($revFns) {
        return array_reduce(
            $revFns,
            function ($acc, $fn) {
                return call_user_func($fn, $acc);
            },
            $initial
        );
    };
}

// 部分適用
function partial()
{
    $args = func_get_args();
    $fn = array_shift($args);

    return function () use ($fn, $args) {
        $newArgs = func_get_args();

        $allArgs = array_merge($args, $newArgs);
        return call_user_func_array($fn, $allArgs);
    };
}

できたので、いつもの偶数二乗和を求める関数を作ってみる。

// 偶数二乗和を求める
function evenSquareSum($n)
{
    // 最後の引数が配列になるように引数の順番を合わせておく
    $filter = function ($callback, $array) {
        return array_filter($array, $callback);
    };
    $map = function ($callback, $array) {
        return array_map($callback, $array);
    };
    $reduce = function ($initial, $callback, $array) {
        return array_reduce($array, $callback, $initial);
    };

    // 配列を一つ受け取り、偶数のみ抽出、各要素を 2 乗する、全ての要素を合計する関数を作成
    $selectEven = partial($filter, function ($n) { return $n % 2 == 0; });
    $squareEach = partial($map, function ($n) { return $n * $n; });
    $sum = partial($reduce, 0, function ($acc, $n) { return $acc + $n; });

    // それぞれの関数を合成し、偶数二乗和を計算する関数を作る
    $evenSquareSum = compose(
        $sum,
        $squareEach,
        $selectEven
    );

    $numList = range(1, $n);
    return $evenSquareSum($numList);
}
$ret = evenSquareSum(10);
var_dump($ret); // => 220

動いてそう。 これのカリー化バージョン作って比べてみると 部分適用との違いとか分かりやすいかも。