PHP - 部分適用ってなんだに続くポエムですが、どうしてもPHPでarray_map
とかarray_reduce
とかを駆使して宣言的に書きたいんだ! って人向けです。
前回のポエムでは「foreach
があれば、別に部分適用とか要らないし」と結論を出しました。この記事は蛇足もいいところで、まったくおすすめしません。読まなくていいです。
これが前回のポエムの最終的な結論でした。
$a = [];
foreach (range(1, $n) as $m) {
$a[] = area_trapezoid($height, 3, 5);
}
$result = $a;
しかし一時変数を作って破壊的操作してくのは嫌だ! って感じかたもありそうです。
嫌なのは仕方ないので、foreach
はやめよう。そうです、array_map()
です。
array_map
array_map
— 指定した配列の要素にコールバック関数を適用するarray array_map ( callable $callback , array $array1 [, array $... ] )
array_map()
は、array1
の各要素にcallback
関数を適用した後、 その全ての要素を含む配列を返します。callback
関数が受け付けるパラメータの数は、array_map()
に渡される配列の数に一致している必要があります。
なるほど。callback
に渡すcallable
とは何かについては PHP: コールバック / Callable - Manual を読んでください。
適用って何だっけ
「関数を適用」とは耳馴染みがないかもしれませんが、例を示します。
/**
* 値を2倍にする
*/
function Nx2($n)
{
return $n * 2;
}
$data = [1, 2, 3, 4, 5];
$result = array_map("Nx2", $data);
//=> [2, 4, 6, 8, 10]
配列に「値を2倍にする」函数を適用することで、全ての値が2倍になりました。
では次に「値をm倍にする」函数を適用してやりたい。
/**
* 値をM倍にする
*/
function NxM($n, $m)
{
return $n * $m;
}
これをarray_map
を使って、全ての値を3倍に適用するにはどうすればいいか。
NxM
は引数を二つ要求するので、配列を二つ渡してやればいいですね。
$data1 = [1, 2, 3, 4, 5];
$data2 = [3, 3, 3, 3, 3];
$result = array_map("NxM", $data1, $data2);
//=> [3, 6, 9, 12, 15]
$data
の要素が$n
に、$data2
の要素が$m
に入ります。
これはちょっとめんどくさい… いや、array_fill(0, count($data), 3)
とかでdata
と同じ要素数の配列を作ることはできるけど、$data
が100万要素あったとしたら、100万個の3
だけが並んだ配列を生成するのは、いくらなんでも無駄すぎるだろ。
「array_map
からn個の引数を渡すにはn個の配列」が必要なので、わざわざ2個の配列を作りました。
しかし、「array_map
から1個の引数だけ渡されれば良い」ようにすれば、配列は1個で済みますね。前回のポエムで長々と部分適用の種類を紹介したのは何のためだったか。
$data = [1, 2, 3, 4, 5];
$Nx3 = function($a) { return NxM($a, 3); };
$result = array_map($Nx3, $data);
//=> [3, 6, 9, 12, 15]
やった、配列がひとつで済んだ! だらだらとしたポエムで、ようやく「foreach
」以外の部分適用を活用できましたね。
これで引数がいくつあっても怖くない。
話を戻す
$a = [];
foreach (range(1, $n) as $m) {
$a[] = area_trapezoid($height, 3, 5);
}
$result = $a;
$t3_5 = function ($height) {
return area_trapezoid($height, 3, 5);
};
$result = array_map($t3_5, range(1, $n));
短くなった…? わかりやすい…?
いや、どっちも意図は明確ではあるんだけど、foreach
がarray_map
になっても別に嬉しくはないだろ、ってのが前回の記事を強引にまとめた理由です。
しかし、「短くしたい」「コードを明確にしたい」 が大差ない以上、もっと宣言的に書きたくなるのが人情です。
そのためにPartialCallable.phpを用意したよ。
$t3_5 = new PartialCallable("area_trapezoid", [1 => 3, 2 => 5], 0);
$result = array_map($t3_5, range(1, 10));
new PartialCallable
の第一引数はcallable、第二引数は部分適用する引数を配列で指定、第三引数はarray_map
から渡された引数をどの位置に部分適用するかの指定です。
PHPの配列は0オリジンなのでわかりにくいですが、0
が第一引数、1
が第二引数、2
が第三引数… とずれます。
よかったよかった。これで人類に平和が訪れます。
結論
foreach
でいいじゃん。
完全な蛇足
日本語で「部分適用」で検索すると、たいていカレー料理の話が出てきて読者を混乱させます。 PHPにカリー化の出番は一切ありません。
どうしても、どうしてもカリー化とは何か気になる人は「カリー化ってなぁに? をRubyから。」と、「Rubyでのカリー化、をまじめに。」を読んで、 二度とPHPの話題でカリー化を持ち込まない方がいいです 。
得るものは特にないから読まなくていいし、何度でも言ふけどPHPに カリー化の出番は一切ない から忘れてください。