ホームページ >バックエンド開発 >PHPチュートリアル >実践的なアプリケーションにおける PHP クロージャ機能の問題点についての簡単な説明_PHP チュートリアル
実際、ほとんどの場合は可能ですが、まだ非常に厄介な点もありますので、ゆっくりお話しましょう。
多くの言語は、配列を操作するための非常にエレガントで美しいメソッドを提供します。次の例では、PHP5.3 などの言語で提供されているクロージャー関数を使用して、反復配列を「客観的に」操作する方法を示します。
翻訳注釈: オリジナルの作成者は Groovy 言語と Scala 言語をあまり理解していないため、ここに Javascript 実装を追加します。
始める前に、この例は要点を説明するためだけのものであり、パフォーマンスなどの他の要素は考慮されていないことを説明させてください。
「買い物をする」
次の配列を使用した簡単な例から始めます:
$nums = array(10, 20, 30, 40); 配列内で 15 より大きい項目を見つける必要があります。 。次に、クロージャを考慮せずに、次のように記述します:
$res = array();foreach ($nums as $n) { if ($n > 15) { $res[] = $n;言語自体はクロージャをサポートしているため、次のように記述することができます (Groovy 言語)
def res = nums.findAll { it > 15 } または Scala 言語を使用します
val res = nums filter (_ > 15) Javascript 1.6 を使用すると以下のようになります
var res = nums.filter(function(c){return c > 15}); ループ操作が抽象化されているので、Groovy と Scala (および Javascript) がわかることがわかります。どれも美しいです 1行で完了します。
もちろん、PHP5.3のクロージャを使えばそれも可能です
$res = array_filter($nums, function($v) { return $v > 15; });PHPは、PHP5.3よりも高度なメソッドを使用しています。この点では Scala 文字数は増えていますが、前の例よりも短くて読みやすくなっています。
ところで、上記の PHP コードは実際には Lambda 分析を使用しており、実際のクロージャではありません。これは私たちが現在注目している点ではありません。 PHP クロージャと Lambda 解析の詳細については、ここを参照してください。
ここまではかなりうまくできているようですので、問題の難易度を上げてみましょう。15 より大きい項目をすべて見つけて、2 を掛けて、スコープ内の変数の値を加算してから返します。
Groovy 実装:
def x = 1def res = nums .findAll { it > 15 } .collect { it * 2 + x }Scala 実装:
val x = 1val res = nums filter (_ > 15) _ * 2 + x) 翻訳アノテーション、JavaScript 実装:
var i = 1;var res = nums.filter(function(c){return c > 15}).map(function(c){return c * 2 + i}); および PHP:
$x = 1;$res = array_map( function($v) use ($x) { return $v * 2 + $x; }, array_filter( $nums, function($ v) { return $v > 15 })); コードサイズの点で、PHP は他の言語とは異なっているようです。コードの文字通りの美しさはさておき、上記の PHP コードにはさらに問題があります。
たとえば、比較に値の代わりに配列キーを使用する必要がある場合はどうすればよいでしょうか?はい、上記のコードは実行できません。また、構文的に言えば、上記のコードは非常に読みにくいです。
自然に立ち返って、問題を解決するにはやはり昔ながらの考え方に戻る必要があります:
$x = 1;$res = array();foreach ($nums as $n) { if ($n > 15) { $res [] = $n * 2 + $x; ふぅ、これでまた明らかですね。しかしこのとき、「なぜわざわざいじる必要があるの?これはただの配列演算ではないの?」と再び混乱するかもしれません。
はい、最高のものはまだ来ません。現時点では、自傷行為をする傾向があるように見えるこの「退屈な問題」を解決するために、PHP のいくつかの高度な機能を活用する時期が来ています。
ArrayObject - 配列のカプセル化
PHP には SPL と呼ばれる標準ライブラリがあり、これには ArrayObject と呼ばれるクラスが含まれており、「クラスを配列のように操作する」機能を提供できます。たとえば、
$res = new ArrayObject(array (10, 20, 30, 40));foreach ($res as $v) { echo "$vn";}ArrayObject は組み込みクラスであるため、他のクラス操作と同様にカプセル化できます。
Arr - シュガー コーティング
ArrayObject とクロージャの機能ができたので、それをカプセル化してみましょう:
class Arr extends ArrayObject{ static function make($array) { return new self($ array) ; } 関数マップ($func) { $res = new self(); ($this as $k => $v) { $res[$k] = $func($k, $v); $res; } 関数 filter($func) { $res = new self(); ($this as $k => $v) { if ($func($k, $v)) { $res [$ k] = $v; } } return $res }}準備は完了です。以下の書き換えられた PHP コードは、上記の問題を解決でき、構文的には「ほぼ」次のようになります。
$res = Arr::make($nums) ->filter(function($k, $v) ) { return $v > 15; }) ->map(function($k, $v) { return $v * 2; });上記のコードは従来の方法とどう違うのでしょうか?まず、再帰的呼び出しや連鎖呼び出しが可能であるため、より類似した操作を追加できます。
同時に、配列のキーと値は、コールバックの 2 つのパラメーターを通じてそれぞれ操作できます - $k はキーに対応し、$v は値に対応します。これにより、従来の PHP 関数 array_filter では不可能であった、クロージャでキー値を使用できるようになります。
もう 1 つの追加の利点は、より一貫性のある API 呼び出しです。従来の PHP 関数操作を使用すると、その最初のパラメータはクロージャ、配列、または複数の配列になる可能性があります... とにかく、誰にもわかりません。
これは Arr クラスの完全なソース コードです。これには他の便利な関数 (reduce や walk と同様) も含まれています。実際、それらの実装はコードと似ています。
ゲーム
この質問に答えるのは実際には難しいです。コードのコンテキストやプログラマー自身など、多くの要因に依存します。実際、PHP のクロージャ実装を初めて見たとき、匿名内部クラスを使用してクロージャを実装し始めた、はるか昔の Java の時代に戻ったような気がしました。もちろん、これを実行することもできますが、それは不必要に思えます。 PHP クロージャーには何の問題もありませんが、その実装と構文が私を混乱させます。
クロージャ機能を備えた他の言語では、クロージャを非常に便利に呼び出すことができ、エレガントな構文を備えています。上の例では、Scala の従来のループを使用することもできますが、このように記述しますか?一方で、上記の問題は PHP クロージャでも実現できるという人もいますが、一般的にはこのように書くのでしょうか?
PHP クロージャーは、状況によっては (実行の遅延やリソース呼び出しなど) 非常に強力であることは確かですが、従来の反復操作や配列操作に直面すると少し困難です。何があっても落胆しないでください。最も重要なことは、基本に立ち返り、互換性のあるクリーンなコードと API を作成することです。
結論
後から追加されたすべての構文機能 (当時の Java のジェネリック機能を覚えていますか? そしてその前の PHP OOP 機能) と同様に、それらはすべて実行されて最終的に安定するまでに時間がかかります。今後、PHP5.3、さらには PHP6 の人気が高まるにつれ、近い将来、より多くのテクニックや機能が賢明なプログラマーによって徐々に発見されるようになると思います。
元の記事の冒頭のタイトルに戻って比較してください
$res = Arr::make($nums) ->filter(function($k, $v) { return $v > 15; }) ->map( function($k, $v) { return $v * 2; }); と
val res = nums filter (_ > 15) map (_ * 2) の違い。結局のところ、これらは本質的には単なる構文であり、同じ問題を異なるアプローチで解決します。プログラミング言語はアプリケーションの特性が異なるため、どちらが優れていてどちらが劣っているかを比較することはできません。
最後に、この記事のコード例を紹介します。(もちろん、これだけではありませんが) PHP を使用して関数を反復する方法についてさらに理解できると思います。
信頼性の低いブロガーの体験談
正直に言うと、提案されている新しいクロージャやその他の関数については PHP5.0 よりも前から知っていましたが、PHP5.3 で提供されるクロージャや Lambda 関数を見てから、まだ内容との齟齬があり、もともと予想していました。
馴染みのある JavaScript と比較しても、PHP のクロージャは、「他の言語にもクロージャがあるから、私もクロージャを持たなければならない」という考え方の産物であるように私には思えます。
しかし、上で述べたように、JavaScript などの他の動的言語と比較すると、PHP は独自のアプリケーションと実装の哲学により他の開発言語とは異なります。
そのため、特定の機能の呼び出し方法と実装方法が異なるため、同様の機能を持つ他の言語に慣れている人は必然的に不快感を感じることになります。
PHP5.3は公開されてまだ半年も経っていないため、すでにクロージャなどの機能を備えているJavaScriptなどの動的言語と比べると、当然ながら非常に未熟なように見えます。
同時に、大多数の開発者は、クロージャを含む、PHP5.3 によって提供される新機能に対してまだ様子見の姿勢を保っています。 PHP のクロージャ機能はまだ研究室に存在しており、実際の開発にそれを適用するには、言語機能の画期的な進歩だけでなく、効率やセキュリティなどの観点からのテストも必要です。
しかし、原著者が言ったように、PHPのバージョンが進むにつれて、PHPのクロージャアプリケーションはますます頻繁になると思います。 PHP4 から PHP5 への変換と同様に、言語の新機能に適応することは、実際には苦しみながらも楽しいプロセスです。