ですから、私は素晴らしい、最新の、オブジェクト指向の方法でプログラミングしています。私はPHPが実装するOOPのさまざまな側面を定期的に利用していますが、クロージャをいつ使用する必要があるのか疑問に思っています。クロージャを実装することがいつ役立つかを明らかにすることができる専門家はいますか?
回答:
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
。
ただし、PHP5.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でDanUdeyの例のようなことをしたいときは、OO StrategyPatternを使用します。私の意見では、これは、実行時に動作が決定される新しいグローバル関数を導入するよりもはるかに優れています。
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を介してオブジェクトにアクセスして操作できます。実際には、そのオブジェクトのクラス定義ではなく、別の場所で定義されている場合でも、メソッドの1つにすぎません。
はっきりしていなくても心配いりません。使い始めるとはっきりします。
基本的に、Closureは、外部変数にアクセスできる内部関数であり、匿名関数(名前のない関数)へのコールバック関数として使用されます。
<?php
$param='ironman';
function sayhello(){
$param='captain';
$func=function () use ($param){
$param='spiderman';
};
$func();
echo $param;
}
sayhello();
?>
//output captain
//and if we pass variable as a reference as(&$param) then output would be spider man;
$param='captain'
funcsayhello()
は、funcのローカル変数ですsayhello()
。$param='ironman'
上記sayhello()
はグローバル変数です。あなたのスクリプトで唯一の$のparam変数を作成したい場合は、呼び出す必要があります:global $param;
内sayhello()
FUNC
これがphpのクロージャの例です
// Author: HishamDalal@gamil.com
// Publish on: 2017-08-28
class users
{
private $users = null;
private $i = 5;
function __construct(){
// Get users from database
$this->users = array('a', 'b', 'c', 'd', 'e', 'f');
}
function displayUsers($callback){
for($n=0; $n<=$this->i; $n++){
echo $callback($this->users[$n], $n);
}
}
function showUsers($callback){
return $callback($this->users);
}
function getUserByID($id, $callback){
$user = isset($this->users[$id]) ? $this->users[$id] : null;
return $callback($user);
}
}
$u = new users();
$u->displayUsers(function($username, $userID){
echo "$userID -> $username<br>";
});
$u->showUsers(function($users){
foreach($users as $user){
echo strtoupper($user).' ';
}
});
$x = $u->getUserByID(2, function($user){
return "<h1>$user</h1>";
});
echo ($x);
出力:
0 -> a
1 -> b
2 -> c
3 -> d
4 -> e
5 -> f
A B C D E F
c
閉鎖:
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