PHP5.3 の新しいバージョンには多くの新機能が追加されていますが、最も目を引く機能の 1 つはクロージャーのサポートです。では、将来的には、Ruby、JavaScript、その他の「ハイテク言語」を書いている人たちのように、非常にクールなコードを書くことができるのでしょうか?実際、ほとんどの場合は可能ですが、それでも非常に面倒な点もありますので、ゆっくりお話しましょう。
多くの言語は、配列を操作するための非常にエレガントで美しいメソッドを提供します。次の例では、PHP5.3 などの言語で提供されているクロージャー関数を使用して、反復配列を「客観的に」操作する方法を示します。
翻訳注釈: オリジナルの作者は比較的無知で、Groovy 言語と Scala 言語を理解していないため、ここに Javascript 実装を追加します。
始める前に説明させてください。この例は要点を説明するためのものであり、パフォーマンスなどの他の要素は考慮されていません。
次の配列を使用した簡単な例から始めましょう:
リーリー配列内で 15 を超える項目を見つける必要があります。次に、クロージャを考慮せずに、次のように書くことができます:
リーリー言語自体がクロージャをサポートしている場合は、おそらく次のように記述されるでしょう(Groovy言語)
リーリーまたはScala言語を使用してください
リーリー注釈: Javascript 1.6を使用した場合は次のようになります
リーリーループ操作が抽象化されているため、Groovy、Scala (および Javascript) はすべて美しく、1 行で実行できることがわかります。
もちろん、PHP5.3クロージャを使用する場合も可能です
リーリーPHP は Scala よりも多くの文字を使用しますが、前の例と比較すると短くて読みやすいです。
ちなみに、上記の PHP コードは実際にはラムダ分析を使用しており、実際のクロージャではありません。これは現在注目している点ではありません。 PHP クロージャと Lambda 解析の詳細については、ここを参照してください。
ここまではかなりうまくできているようですので、問題の難易度を上げてみましょう。15 より大きい項目をすべて見つけて、2 を掛けて、スコープ内の変数の値を加算してから返します。
素晴らしい実装:
リーリーScala の実装:
リーリー翻訳アノテーション、JavaScript実装:
リーリーそしてPHP:
リーリーコードサイズの点で、PHP は他の言語とは異なるようです。コードの文字通りの美しさはさておき、上記の PHP コードにはさらに問題があります。
たとえば、比較に値の代わりに配列キーを使用する必要がある場合はどうすればよいでしょうか?はい、上記のコードは実行できません。また、構文的に言えば、上記のコードは非常に読みにくいです。
自然に立ち返って、問題を解決するにはやはり昔ながらの考え方に戻る必要があります:
リーリーふぅ、これはまた非常に明確に見えます。しかしこのとき、「なぜわざわざいじる必要があるの?これはただの配列演算ではないの?」と再び混乱するかもしれません。
はい、最高のものはまだ来ません。現時点では、自傷行為をする傾向があるように見えるこの「退屈な問題」を解決するために、PHP のいくつかの高度な機能を活用する時期が来ています。
PHPにはSPLと呼ばれる標準ライブラリがあり、このライブラリにはArrayObjectと呼ばれるクラスが含まれており、
などの「クラスを配列のように操作する」機能を提供できます。 リーリーArrayObject は組み込みクラスであるため、他のクラス操作と同様にカプセル化できます。
ArrayObject とクロージャーの機能ができたので、それをカプセル化してみましょう:
リーリーさて、準備は完了です。以下の書き換えられた PHP コードは上記の問題を解決でき、構文的には「ほぼ」次のようになります。 リーリー
上記のコードは従来の方法とどう違うのですか?まず、再帰的呼び出しや連鎖呼び出しが可能であるため、より類似した操作を追加できます。同時に、配列のキーと値は、コールバックの 2 つのパラメーターを通じてそれぞれ操作できます。$k はキーに対応し、$v は値に対応します。これにより、従来の PHP 関数 array_filter では不可能であった、クロージャでキー値を使用できるようになります。
もう 1 つの追加の利点は、より一貫性のある API 呼び出しです。従来の PHP 関数操作を使用すると、その最初のパラメータはクロージャ、配列、または複数の配列になる可能性があります... とにかく、誰にもわかりません。
これは Arr クラスの完全なソース コードです。これには他の便利な関数 (reduce や walk と同様) も含まれています。実際、それらの実装はコードと似ています。
ゲーム
其他具有闭包特性的语言,它们可以非常方便的调用闭包并同时具有优雅的语法。在上面的例子 中,在 Scala 中使用传统的循环也可以工作,但你会这样写吗?而从另个方面,那么有人 说上面这个题目使用 PHP 的闭包也可以实现,但一般情况下你会这样写吗?
可以确定,PHP 闭包在些情况下可以成为锐利的军刀(例如延时执行以及资源调用方面), 但在传统的迭代以及数组操作面前就显得有些为难。不要气馁不管怎么样, 返璞归真编写具有兼容性的、清爽的代码以及 API 是最重要的。
像所有后来加上的语法特性一样(记得当年 Java 的 Generics 特性不?以及前几年的 PHP OOP 特性),它们都需要时间磨合以及最终稳定下来。随着 PHP5.3 甚至将来的 PHP6 逐渐普及,越来越多的技巧和特性相信在不远的将来逐渐的被聪明的程序员挖掘出来。
回到最初文章开头那个题目,对比
$res = Arr::make($nums)<br> ->filter(function($k, $v) { return $v > 15; })<br> ->map(function($k, $v) { return $v * 2; });
以及
val res = nums filter (_ > 15) map (_ * 2)
两者之间的区别。归根结底它们仅是语法而已,本质上都是殊途同归解决了同个问题。程序 语言的应用特性不同,自然孰优孰劣也就无从比较。
最后,这里有此篇文章的代码示例, 相信可以找到更多如何使用 PHP 进行函数式迭代(当然不仅仅是这些)的心得。
-- Split --
坦白讲,虽然在 PHP5.0 之前就了解过提出的新增闭包等功能,但在看到 PHP5.3 提供的闭 包以及 Lambda 功能后,与原本心理期待的还是有些出入。
甚至相对于熟悉的 JavaScript,PHP 的闭包在我看来,像是“别的语言都有了,所以我也要有” 的这种心态下的产物。
但正如上文中所言,相比 JavaScript 等其他动态语言,PHP 出于自身的应用以及实现的哲学 出发,与其他开发语言不尽相同。
因此在某些特性的调用方式、实现方法也会不一样,这难免会让熟悉另外具有类似功能的语言 的人感到的不适应。
从 PHP5.3 推出至今,还不到半年的时间,相比 JavaScript 等这些早已具有闭包等特性的 动态语言相比,自然是显得非常稚嫩。
同时,广大的开发者对于 PHP5.3 提供的包括闭包在内的新特性还在持观望态度。PHP 的闭包特性目前还是存在于实验室中,其应用于实际开发如要突破的不仅仅是语言特性 ,还要经过效率、安全性等方面的考验。
但相信,如原文作者所言,随着 PHP 版本的推进,PHP 的闭包应用场合会越来越频繁。像 当年 PHP4 转换到 PHP5 一样,对语言新特性的适应,其实是种痛并快乐着的过程。
文章来源: www.2cto.com