2011/07/28

PHPのクロージャ

PHPは書いたことありませんが、仕事で読む機会が出てきました。
なので、emacsにphp-mode入れたり、php自体を入れてphp -aしてREPLで遊んでみたり。

cygwinはsetup.exeからphpが入らないので爆発してください。

で、5.X系からはクロージャがあるらしいので試してみた。
ドキュメント読まずにschemeのノリで以下のようにやってみた。
以下REPL垂れ流し。
$ php -a
Interactive shell

php > $double = function ($x){ $ret = $x * $x; print $ret . PHP_EOL; return  $ret;};
php > array_map($double, range(1, 5));
1
4
9
16
25

php > $inc = 5;
php > $x5 = function($x){ $ret = $inc * $x; print $ret; return $ret;};
php > array_map ($x5, range(1, 5));
PHP Notice:  Undefined variable: inc in php shell code on line 1
0PHP Notice:  Undefined variable: inc in php shell code on line 1
0PHP Notice:  Undefined variable: inc in php shell code on line 1
0PHP Notice:  Undefined variable: inc in php shell code on line 1
0PHP Notice:  Undefined variable: inc in php shell code on line 1
0

php > print $inc;
5
php > var_dump($x5);
object(Closure)#2 (1) {
  ["parameter"]=>
  array(1) {
    ["$x"]=>
    string(10) ""
  }
}

php > function return_func (){ return function($x){ return $x * $x;};};
php > return_func();
php > var_dump(return_func());
object(Closure)#3 (1) {
  ["parameter"]=>
  array(1) {
    ["$x"]=>
    string(10) ""
  }
}
php > return_func()(5);
PHP Parse error:  syntax error, unexpected '(' in php shell code on line 1
php > $fun = return_func();
php > $fun(5);
php > print $fun(5);
25

php > function counter_maker ($seed){ return function (){ $seed =  $seed + 1; return $seed;};};
php > $counter = counter_maker(0);
php > $counter();
PHP Notice:  Undefined variable: seed in php shell code on line 1
php >
これのどこがクロージャなわけ?と、思ったら、クロージャは構文が微妙に違うんですね。

で、ちゃんとドキュメント読んだら、以下のようにしてできました、と。
php > $x5 = function($x) use ($inc) { $ret = $inc * $x; print $ret; return $ret;};
php > array_map ($x5, range(1, 5));
510152025
php > $x5 = function($x) use ($inc) { $ret = $inc * $x; print $ret . PHP_EOL; return $ret;};
php > array_map ($x5, range(1, 5));
5
10
15
20
25
おーできたできた。へー。

PHP 5.3にはクロージャがあるってんで、試してみたら「全然閉包じゃねーじゃん!レキシカルスコープどこいった!」だったんだけど、調べてみたらuseで明示的に環境(というか引きずる参照)を指定してあげる必要があるらしく、やってみたらできた。これは興味深い(笑)
よくよく考えたら、PHPのクロージャ構文はEmacs Lispのlexical-letとそっくりですね。


PHPは動的スコープなんですね!Emacs Lispと同じだと思えば怖くない!クロージャの実現方法も似てるしね!

PHPはクロージャを導入するにあたって、言語自体を改造する必要があったわけだけど、Emacs Lispでクロージャを実現するlexical-letは「ただのマクロ」という点がすごい。Lispすげー!(って言っとけばいい?)

そういえば、traitというのがあります。私はscalaか何かにあるってんで耳にしたことがあります。そのtrait(たぶんscalaのそれとまったく同じものというわけではないっぽい)がPHPの次期バージョンにも入るそうですね。
で、これ↑を読んで、どんなものかわかりました。mix-inしたいけど、interfaceの実装やabstractクラスの継承じゃできないので新しい概念を持ち込んだって感じですか。

Emacsテクニックバイブル ~作業効率をカイゼンする200の技~

0 件のコメント:

コメントを投稿