私はいくつかのPHP 5.3.0
機能をチェックしていて、サイト上で非常におもしろそうに見えるいくつかのコードに出くわしました:
public function getTotal($tax)
{
$total = 0.00;
$callback =
/* This line here: */
function ($quantity, $product) use ($tax, &$total)
{
$pricePerItem = constant(__CLASS__ . "::PRICE_" .
strtoupper($product));
$total += ($pricePerItem * $quantity) * ($tax + 1.0);
};
array_walk($this->products, $callback);
return round($total, 2);
}
無名関数 の例の1つとして。
誰もがこれについて知っていますか?ドキュメントはありますか?そしてそれは悪そうに見えます、それは使われるべきですか?
これがPHPが クロージャ を表す方法です。これは決して悪ではありません、そして実際、それは非常に強力で有用です。
基本的にこれが意味するのは、無名関数がその範囲外のローカル変数(この場合は$tax
および$total
への参照)を「キャプチャー」し、その値を保持すること(または$total
の場合は$total
自体への参照)を許可することです。 )無名関数自体の中の状態として。
もっと簡単な答えです。
function ($quantity) use ($tax, &$total) { .. };
$tax
を変更しても、オブジェクトのようにポインタでない限り、外部からの影響はありません。&$total
の場合のように、変数をポインタとして渡すことができます。このようにして、$total
の値を変更すると外部効果があり、元の変数の値が変わります。@Mytskineとして 指摘された おそらく最も詳細な説明は クロージャのためのRFC です。 (彼にこれを支持しなさい)
閉鎖は美しいです!それらは無名関数に付随する多くの問題を解決し、(少なくともphpについて話す限りでは)本当に優雅なコードを可能にします。
javascriptプログラマは、束縛された変数が明示的に定義されていないため、クロージャを常に、時には知らなくても使用します - これがphpでの使用です。
上記の例よりも優れた実例があります。多次元配列をサブ値でソートする必要があるとしましょうが、キーが変わります。
<?php
function generateComparisonFunctionForKey($key) {
return function ($left, $right) use ($key) {
if ($left[$key] == $right[$key])
return 0;
else
return ($left[$key] < $right[$key]) ? -1 : 1;
};
}
$myArray = array(
array('name' => 'Alex', 'age' => 70),
array('name' => 'Enrico', 'age' => 25)
);
$sortByName = generateComparisonFunctionForKey('name');
$sortByAge = generateComparisonFunctionForKey('age');
usort($myArray, $sortByName);
usort($myArray, $sortByAge);
?>
警告:テストされていないコード(私はatmをインストールしたphp5.3を持っていません)が、それはそのようなもののように見えるはずです。
1つ欠点があります。多くのphp開発者は、クロージャと対決するのであれば少し無力かもしれません。
より良い閉鎖の理解を深めるために、私はあなたに別の例を挙げる - 今度はJavaScriptで。問題の1つは、スコープとブラウザ固有の非同期性です。特にwindow.setTimeout();
(または-interval)に関してはそうです。そのため、setTimeoutに関数を渡しますが、パラメータを指定するとコードが実行されるため、実際にはパラメータを指定することはできません。
function getFunctionTextInASecond(value) {
return function () {
document.getElementsByName('body')[0].innerHTML = value; // "value" is the bound variable!
}
}
var textToDisplay = Prompt('text to show in a second', 'foo bar');
// this returns a function that sets the bodys innerHTML to the prompted value
var myFunction = getFunctionTextInASecond(textToDisplay);
window.setTimeout(myFunction, 1000);
myFunctionはある種の定義済みパラメータを持つ関数を返します。
正直に言うと、私は5.3以降、PHPと匿名関数/クロージャーが大好きです。名前空間はより重要かもしれませんしかし、それらはずっとセクシーではありません。
function () use () {}
はPHPのクロージャのようなものです。
use
がないと、関数は親スコープ変数にアクセスできません
$s = "hello";
$f = function () {
echo $s;
};
$f(); // Notice: Undefined variable: s
$s = "hello";
$f = function () use ($s) {
echo $s;
};
$f(); // hello
use
変数の値は、呼び出されたときではなく、関数が定義されたときのものです。
$s = "hello";
$f = function () use ($s) {
echo $s;
};
$obj = "how are you?";
$f(); // hello
use
変数&
による参照による参照
$s = "hello";
$f = function () use (&$s) {
echo $s;
};
$s = "how are you?";
$f(); // how are you?
Zupaは、 'use'によるクロージャーとEarlyBindingと 'used'である変数の参照の違いを説明する素晴らしい仕事をしました。
それで、私は変数の早期束縛(=コピー)でコード例を作りました:
<?php
$a = 1;
$b = 2;
$closureExampleEarlyBinding = function() use ($a, $b){
$a++;
$b++;
echo "Inside \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Inside \$closureExampleEarlyBinding() \$b = ".$b."<br />";
};
echo "Before executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "Before executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";
$closureExampleEarlyBinding();
echo "After executing \$closureExampleEarlyBinding() \$a = ".$a."<br />";
echo "After executing \$closureExampleEarlyBinding() \$b = ".$b."<br />";
/* this will output:
Before executing $closureExampleEarlyBinding() $a = 1
Before executing $closureExampleEarlyBinding() $b = 2
Inside $closureExampleEarlyBinding() $a = 2
Inside $closureExampleEarlyBinding() $b = 3
After executing $closureExampleEarlyBinding() $a = 1
After executing $closureExampleEarlyBinding() $b = 2
*/
?>
変数を参照する例(変数の前の '&'文字に注意してください)。
<?php
$a = 1;
$b = 2;
$closureExampleReferencing = function() use (&$a, &$b){
$a++;
$b++;
echo "Inside \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Inside \$closureExampleReferencing() \$b = ".$b."<br />";
};
echo "Before executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "Before executing \$closureExampleReferencing() \$b = ".$b."<br />";
$closureExampleReferencing();
echo "After executing \$closureExampleReferencing() \$a = ".$a."<br />";
echo "After executing \$closureExampleReferencing() \$b = ".$b."<br />";
/* this will output:
Before executing $closureExampleReferencing() $a = 1
Before executing $closureExampleReferencing() $b = 2
Inside $closureExampleReferencing() $a = 2
Inside $closureExampleReferencing() $b = 3
After executing $closureExampleReferencing() $a = 2
After executing $closureExampleReferencing() $b = 3
*/
?>
ごく最近まで、PHPはASTを定義し、PHPインタープリターはパーサーを評価部分から分離していました。クロージャが導入されている間、PHPのパーサーは評価と高度に結びついています。
したがって、クロージャーが最初にPHPに導入されたとき、インタープリターには、どの変数がクロージャーで使用されるかを知る方法がありません。したがって、ユーザーは明示的なインポートによってzendエンジンを満足させ、zendが行うべき宿題をしなければなりません。
これは、PHPでいわゆるシンプルな方法です。