2015/08/08
無名関数からの外部変数参照について
-
-
PHP 5.3 以降では「無名関数」が使えていろいろ便利になった。
この無名関数の実装で、外部変数の参照は use(...) を利用して行う方式を、またその際の変数のスコープは「レキシカルスコープ」を採用している。そして、use() で外部参照を行なう際には外部変数のコピーを渡す。
use() を使用することで、無名関数内での外部変数参照が値渡しなのか参照渡しなのかを明確にすることができるのはなかなか良いと思う。
ただし、参照渡しの場合「オリジナルの変数への参照」ではなく、「オリジナル変数のコピーへの参照」となっているようだ。このため、2つの無名関数間で外部変数を共用することができない。例えば下記のコードを参照されたい。
06 | return function () use (& $local_var ) { $local_var ++; echo '1 => ' , $local_var , "\n" ; return $local_var ; }; |
08 | return function () use (& $local_var ) { $local_var ++; echo '2 => ' , $local_var , "\n" ; return $local_var ; }; |
14 | call_user_func( $test1 ); |
15 | call_user_func( $test1 ); |
16 | call_user_func( $test1 ); |
19 | call_user_func( $test2 ); |
20 | call_user_func( $test2 ); |
21 | call_user_func( $test2 ); |
24 | call_user_func( $test1 ); |
25 | call_user_func( $test1 ); |
26 | call_user_func( $test1 ); |
このコードを実行すると、結果は下記のようになる。
1 => 2
1 => 3
1 => 4
2 => 2
2 => 3
2 => 4
1 => 5
1 => 6
1 => 7
つまり、それぞれの無名関数定義時に use() の処理で外部変数 $local_var をコピーし、そのコピーへの参照を渡しているため無名関数ごとにコピーが生成され、各無名関数では自分用のコピーしかアクセスできず、値を共有することができない。
この方法による実装だと、参照される側の変数のエクステントと参照するクロージャ側の変数のエクステントを分離できる (= 参照される側の処理が終わったら参照される側の変数領域は破棄できる) ので実装は容易だが、ちょっと不便とも言える。
この辺りの議論を始めて目にしたのは John McArthur の「Anatomy of Lisp」 だったが、こちらではコピーをせず「環境」を無限エクステントに配置して共用できる実装を紹介していたように思う。