JavaScriptではよく使う即時関数(function(){ /*...*/ })()
ですが、PHPもバージョン5.3からクロージャがサポートされ、原理的には書けるはずなので試してみました。
current(array(function(){ /* * このブロックは擬似的にブロックスコープを持つ * */ }))->__invoke(); echo current(array(function($a, $b){ return $a + $b; }))->__invoke(1, 2);// 3
…すっげー見づらいですが、一応解説。
function(){ }をリファレンスにする
PHPの関数や配列、オブジェクトといったものは、一度変数に代入しないとうまく起動してくれません。しかし関数の戻り値はメソッドをつなげることができます。anatooさんのHackが有名ですね。
function ref($o) { return $o; } echo ref(new DateTime)->format('Y');
このref()
を、PHPの組み込み関数だけで実現しようとするとcurrent(array())
となります。ひとまずこれでリファレンスになりました。
->__invoke()で起動する
しかし関数を起動する()
をそのまま続けても文法エラーになってしまいます。ここで思い出したいのはPHPの無名関数はClosureクラスのインスタンスであるということです。実際は__invoke()というマジックメソッドを呼んでいるのを、シンタックスシュガーとして短く書けるようになっているだけです。
なので、省略せずに直接書けば期待通りに動きます。
current(array(function(){ /* * このブロックは擬似的にブロックスコープを持つ * */ }))->__invoke();
しかし読みにくいですね。。。
読みやすくする
call_user_func()を使うと、もう少しすっきり書けます。
call_user_func(function(){ /* * このブロックは擬似的にブロックスコープを持つ * */ }); echo call_user_func(function($a, $b){ return $a + $b; }, 1, 2); //3
これぐらいなら、ややこしくなりがちな不動点コンビネータだってそこそこの読みやすさで書けますよ!!
/** * 不動点コンビネータ(Yコンビネータ) */ function Y($f) { return call_user_func(function($x) use($f){ return function($y) use($f, $x){ return call_user_func($f($x($x)), $y); }; }, function($x) use($f){ return function($y) use($f, $x){ return call_user_func($f($x($x)), $y); }; }); } /** * 階乗計算を無名関数で行う */ echo call_user_func(Y(function($f){ return function($n) use($f){ return $n ? $n * $f($n - 1) : 1; }; }), 5); // 120
(Wikibooksをほとんどそのままパクリました。。)
PHPの文化では「クラスで名前もしっかり付けましょう」な感じなのであまり馴染まないかもですが、書き方のバリエーションが広がるのはいいことだと思います。テスティングフレームワークとかDSL作るときに面白くなりそう。