匿名関数と無名関数 (PHP Advent Calendar 2010 16日目)

shin1x1 さんから、開始された PHP Advent Calendar jp 2010 16日目です。

書くTipsは、10分で考えて、5分で書ける内容で ok です。

とのことなので、さらっと。

PHP5.3から導入された無名関数って、5.2以前からある匿名関数とどうちがうの?ってお話。

PHP5.3から無名関数が利用できるようになりました。こんな感じで書けます。

$closure_echo = function( $s ){
	echo $s . "\n";
};
$closure_echo('Hello World!');

preg_replace_callback なんかの第2引数に使う一時的な関数を定義するのに便利ですね。

PHP5.2までは、同様のことを create_function を使って、実現してました。

$anonymous_echo = create_function( '$s', 'echo $s."\n";' );
$anonymous_echo('Hello World!');

第1引数にパラメータを、第2引数に関数のコードを、それぞれ文字列として渡す必要があります。
コーディングしづらいこと、この上ないですね。

さて実際には、この無名関数と匿名関数は、どう違うのでしょう?
それぞれに割り当てられた変数を var_dump() してみるとわかります。

無名関数

var_dump($closure_echo);
↓
object(Closure)#1 (1) {
  ["parameter"]=>
  array(1) {
    ["$s"]=>
    string(10) "<required>"
  }
}

匿名関数

var_dump($anonymous_echo);
↓
string(9) "lambda_1"

無名関数の実体は Closure オブジェクトですが、匿名関数の実体は "\0lambda_1" と言う名前の関数なのです。
ちなみに匿名関数を複数定義すると、lambda_1, lambda_2, lambda_3 という風に連番で関数名が付けられていきます。
なので、匿名関数は作成した後に function_exists() 関数で存在を確認することもできます。

$anonymous_echo = create_function('$s', 'echo $s."\n";');
if (function_exists("\0lambda_1")) {
	$anonymous_echo('Hello World!');
}

しかも、この匿名関数を割り当てた変数を unset しても、関数定義自体は残ってるので call_user_func で呼び出すことも可能です。

$anonymous_echo = create_function('$s', 'echo $s."\n";');
unset($anonymous_echo);
if (is_callable("\0lambda_1")) {
        call_user_func("\0lambda_1", ''Hello World!'');
}

もちろん、無名関数は実体が Closure オブジェクトなんで、こんなハレンチなことはできません。

他に違いはあるでしょうか?

無名関数では、use を使って親のスコープから変数を引き継ぐことができます。

$count = 0;
$closure = function() use ( &$count ) {
	echo $count++ . "\n";
};
$closure();
$closure();
$closure();

0
1
2

もちろん use を使って自分自身を渡せば再帰処理もできます。


$fib = function( $n ) use( &$fib ) {
if ( $n < 2 ) { return $n; } else { return $fib( $n -1 ) + $fib( $n -2 ); } }; echo $fib(10);[/php] kaila355-300x200再帰のサンプルと言えば、フィボナッチ。
The Lazy Rule of Thirds | Jake Garn Photography

ってことで PHP Advent Calendar jp 2010 16日目おしまい。
明日は koyhoge さんです。

コメントを残す

メールアドレスが公開されることはありません。