そのため、私はオブジェクト指向の最新の方法でプログラミングを行っています。私は、PHP が実装する OOP のさまざまな側面を定期的に利用していますが、いつクロージャーを使用する必要があるのか疑問に思っています。閉鎖を実装することが役立つ場合に光を当てることができる専門家はいますか?
8 に答える
PHP は 5.3 でネイティブにクロージャーをサポートします。クロージャーは、小さな特定の目的にのみ使用されるローカル関数が必要な場合に適しています。クロージャのRFCは良い例です:
function replace_spaces ($text) {
$replacement = function ($matches) {
return str_replace ($matches[1], ' ', ' ').' ';
};
return preg_replace_callback ('/( +) /', $replacement, $text);
}
これにより、replacement
関数を 内でローカルに定義できるようになるため、次replace_spaces()
のようなことはありません:
1)グローバル名前空間が乱雑になる
2) 3 年後には、他の 1 つの関数内でのみ使用されるグローバルに定義された関数があるのはなぜかと疑問に思うようになります。
物事を整理します。関数自体には名前がなく、単に定義され、 への参照として割り当てられていることに注意してください$replacement
。
ただし、PHP 5.3 を待つ必要があることを忘れないでください :)
キーワードを使用して、スコープ外の変数にクロージャにアクセスすることもできますuse
。この例を考えてみましょう。
// Set a multiplier
$multiplier = 3;
// Create a list of numbers
$numbers = array(1,2,3,4);
// Use array_walk to iterate
// through the list and multiply
array_walk($numbers, function($number) use($multiplier){
echo $number * $multiplier;
});
優れた説明がここにあります phpラムダとクロージャーとは何ですか
現在決定したタスクを実行する関数が将来必要になる場合。
たとえば、設定ファイルを読んで、パラメーターの 1 つが、hash_method
アルゴリズムの がmultiply
ではなく でsquare
あると示している場合、何かをハッシュする必要がある場合に使用されるクロージャーを作成できます。
クロージャは (たとえば) で作成できますconfig_parser()
。(構成ファイルから)do_hash_method()
ローカル変数を使用して呼び出される関数を作成します。config_parser()
が呼び出されるたびに、そのスコープで呼び出されていなくてもdo_hash_method()
、ローカル スコープ内の変数にアクセスできます。config_parser()
うまくいけば良い仮説の例:
function config_parser()
{
// Do some code here
// $hash_method is in config_parser() local scope
$hash_method = 'multiply';
if ($hashing_enabled)
{
function do_hash_method($var)
{
// $hash_method is from the parent's local scope
if ($hash_method == 'multiply')
return $var * $var;
else
return $var ^ $var;
}
}
}
function hashme($val)
{
// do_hash_method still knows about $hash_method
// even though it's not in the local scope anymore
$val = do_hash_method($val)
}
技術的な詳細は別として、クロージャーは、関数指向プログラミングとして知られるプログラミング スタイルの基本的な前提条件です。クロージャーは、オブジェクト指向プログラミングでオブジェクトを使用するのとほぼ同じ目的で使用されます。データ (変数) をいくつかのコード (関数) と一緒にバインドし、それを別の場所に渡すことができます。そのため、プログラムの作成方法に影響を与えるか、プログラムの作成方法を変更しない場合は、まったく影響がありません。
PHP のコンテキストでは、PHP はすでに古い手続き型のパラダイムだけでなく、クラス ベースのオブジェクト指向パラダイムにも重きを置いているため、これらは少し奇妙です。通常、クロージャーを持つ言語には、完全なレキシカル スコープがあります。下位互換性を維持するために、PHP はこれを取得しないため、クロージャは他の言語とは少し異なります。それらがどのように使用されるかはまだ正確にはわからないと思います。
troelskn の投稿が提供するコンテキストが気に入っています。PHP で Dan Udey の例のようなことをしたいときは、OO Strategy Pattern を使用します。私の意見では、これは実行時に動作が決定される新しいグローバル関数を導入するよりもはるかに優れています。
http://en.wikipedia.org/wiki/Strategy_pattern
PHP でメソッド名を保持する変数を使用して、関数とメソッドを呼び出すこともできます。これはすばらしいことです。したがって、ダンの例を別の例にすると、次のようになります。
class ConfigurableEncoder{
private $algorithm = 'multiply'; //default is multiply
public function encode($x){
return call_user_func(array($this,$this->algorithm),$x);
}
public function multiply($x){
return $x * 5;
}
public function add($x){
return $x + 5;
}
public function setAlgorithm($algName){
switch(strtolower($algName)){
case 'add':
$this->algorithm = 'add';
break;
case 'multiply': //fall through
default: //default is multiply
$this->algorithm = 'multiply';
break;
}
}
}
$raw = 5;
$encoder = new ConfigurableEncoder(); // set to multiply
echo "raw: $raw\n"; // 5
echo "multiply: " . $encoder->encode($raw) . "\n"; // 25
$encoder->setAlgorithm('add');
echo "add: " . $encoder->encode($raw) . "\n"; // 10
もちろん、どこでも利用できるようにしたい場合は、すべてを静的にすることができます...
クロージャは基本的に、あるコンテキストで定義を記述し、別のコンテキストで実行する関数です。Javascript は JavaScript のいたるところで使用されているため、これらを理解するのに大いに役立ちました。
PHP では、関数内からの「グローバル」(または「外部」) 変数のスコープとアクセス可能性が異なるため、JavaScript よりも効果的ではありません。しかし、PHP 5.4 以降では、オブジェクト内で実行されたときにクロージャーが $this オブジェクトにアクセスできるようになったため、クロージャーはより効果的になりました。
これがクロージャーの目的であり、上記の内容を理解するだけで十分です。
つまり、関数定義をどこかに記述し、関数定義内で $this 変数を使用してから、関数定義を変数に割り当て (他の人は構文の例を示しています)、この変数をオブジェクトに渡します。オブジェクトコンテキストでそれを呼び出すと、実際にはそのオブジェクトのクラス定義ではなく、別の場所で定義されている場合でも、関数は $this を介してオブジェクトにアクセスして操作できます。
あまりはっきりしていなくても心配はいりません。使い始めるとはっきりします。
閉鎖:
MDN には、IMO に関する最良の説明があります。
クロージャは、一緒にバンドルされた (囲まれた) 関数と、その周囲の状態 (レキシカル環境) への参照の組み合わせです。言い換えれば、クロージャーは、内部関数から外部関数のスコープへのアクセスを提供します。
つまり、クロージャーは、親スコープにある変数にアクセスできる関数です。クロージャーを使用すると、状況によっては関数が 1 つの場所 (コールバック、呼び出し可能な引数) でのみ必要になるため、その場で便利に関数を作成できます。
例:
$arr = [1,2,3,3];
$outersScopeNr = 2;
// The second arg in array_filter is a closure
// It would be inconvenient to have this function in global namespace
// The use keyword lets us access a variable in an outer scope
$newArr = array_filter($arr, function ($el) use ($outersScopeNr) {
return $el === 3 || $el === $outersScopeNr;
});
var_dump($newArr);
// array (size=3)
// 1 => int 2
// 2 => int 3
// 3 => int 3