当前位置:  开发笔记 > 编程语言 > 正文

PHP5.3闭包特性及应用详解

09年7月发布的PHP5.3版本带来了很多新的特性,,其中比较惹眼的特性之一就是支持了闭包;那么以后,我们也可以和那帮写Ruby、Javascript等等高科技语言的家伙们一样,写出非常酷的代码吗?呃,其实大部分情况下是可以的,而有些方面还是令人非常的困扰">

09年7月发布的PHP 5.3 版本带来了很多新的特性,, 其中比较惹眼的特性之一就是支持了闭包;那么以后,我们也可以和那帮写 Ruby、Javascript 等等“高科技语言”的家伙们一样,写出非常酷的代码吗?呃,其实大部分情况下是可以的,而有些方面还是令人非常的困扰,下面慢慢道来。

很多语言的都提供了非常优雅和漂亮的操作数组的方法。在下面的例子中,会使用PHP5.3闭包特性以及其他语言提供的闭包功能,用于展示如何“客观的”操作迭代数组。

译注:原文作者比较火星,我不了解 Groovy 以及 Scala语言,所以这里我加上 Javascript 的实现。

在开始之前先说明下,本例子仅仅是阐明观点,并没有考虑性能等其他方面的因素。
 

“货比三家”

用个简单的例子开始,有下面个数组:

$nums = array(10, 20, 30, 40);

需要找出数组中大于 15 的项。那么,不考虑闭包的情况下,我们或许会这样写:

  1. $res = array();  
  2. foreach ($nums as $n) {   
  3.    if ($n > 15) {        $res[] = $n;      
  4. }  

如果语言本身有闭包支持的,那么或许会这样写(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) 都很漂亮得用一行就可以搞定。

当然,如果使用 PHP5.3 的闭包,也可以做到

$res = array_filter($nums, function($v) { return $v > 15; });

PHP 在这方面使用了比 Scala 更多的字符,但对比先前的例子,它更简短并且能更好得阅读。

顺便说下,上面的 PHP 代码实际上是使用了 Lambda 解析式,并不是个真正的闭包,这个 并不是我们目前关注的重点。详细阐述 PHP 闭包以及 Lambda 解析式的资料,可以参考这里。

目前看来感觉都还不错,那么我们再的题目增加点难度:找到所有大于 15 的项, 然后乘以 2 再加上作用域中的的某个变量值以后再返回。

Groovy 的实现:

  1. def x = 1def   
  2. res = nums .findAll { it > 15 } .collect { it * 2 + x } 

Scala 的实现:

  1. val x =   
  2. 1val res = nums filter (_ > 15) map (_ * 2 + x) 

PHP的实现:

  1. $x = 1;  
  2. $res = array_map(  
  3.     function($vuse ($x) {   
  4.         return $v * 2 + $x; },      
  5.      array_filter(        $nums,          
  6. function($v) { return $v > 15; })  
  7. );  

光从代码量方面,现在看起来 PHP 与其他语言有出入了。先抛开代码字面上本身 的审美不谈,上面的 PHP 代码还有个额外的问题。

例如,如果需要使用数组的键而非值作比较,怎么办?是的,上面的代码就办不到了。同时,从语法角度上说,上面的代码非常难以阅读。

返璞归真,这时还是得返回老土的思路去解决问题:

  1. $x = 1;  
  2. $res = array();  
  3. foreach ($nums as $n) {  
  4.     if ($n > 15) {  
  5.         $res[] = $n * 2 + $x;  
  6.     }  

这样看起来又很清楚了。但这个时候你或许又会迷惑了:“那还瞎折腾啥,这不就是个数组操作吗?”。

是的,好戏还在后头。这个时候该让 PHP 的某些高级特性出场,来搞定这看似有自残倾向 的“无聊问题”。

ArrayObject – 对数组的封装

PHP 有个称作 SPL 的标准库,其中包含了个叫做 ArrayObject 的类,它能提供“像数组一 样操作类”的功能,例如

  1. $res = new ArrayObject(array(10, 20, 30, 40));  
  2. foreach ($res as $v) {  
  3.     echo "$vn";  

ArrayObject 是个内置的类,所以你可以像其他类类操作一样封装它。

Arr - 包上糖衣

既然我们已经有了 ArrayObject 以及闭包这些特性,我们就可以开始尝试封装它:

  1. class Arr extends ArrayObject{      
  2. static function make($array)    {  
  3.         return new self($array);  
  4.     }    function map($func)  
  5.     {         
  6.  $res = new self();  
  7.         foreach ($this as $k => $v) {   
  8.            $res[$k] = $func($k$v);  
  9.         }        return $res;    }  
  10.     function filter($func)    {  
  11.         $res = new self();  
  12.         foreach ($this as $k => $v) {  
  13.             if ($func($k$v)) {   
  14.                $res[$k] = $v;  
  15.             }  
  16.         }  
  17.         return $res;  
  18.     }  

好了,万事俱备。下面重写的 PHP 代码就可以解决上面提到的问题,并且看起来语法上“差 不多”了:

$res = Arr::make($nums)    ->filter(function($k, $v)
{ return $v > 15; })    ->map(function($k, $v)
{ return $v * 2; });

上面的代码与传统方式有何不同呢?首先,它们可以递归并形成作用链式的调用,因此可以 添加更多的类似操作。

同时,可以通过回调的两个参数分别操作数组的键以及值其项 - $k 对应键以及 $v 对应值 。这使得我们可以在闭包中使用键值,这在传统的 PHP 函数 array_fliter 中是无法实现的。

另外个带来的额外好处就是更加一致 API 调用。使用传统的 PHP 函数操作,它们有可能第一个参数是个闭包,或者是个数组,抑或是多个数组…总之谁知道呢?

这里是 Arr 类的完整源代码,还包含了其他有用的函数(类似 reduce 以及 walk),其实它 们的实现其实方式和代码类似。

博弈

这个问题其实很难回答 - 这需要根据代码的上下文以及程序员自身等众多因素决定。其实 ,当我第一眼看见 PHP 的闭包实现时,我感觉似乎回到了那很久以前的 Java 时期,当时 我在开始使用匿名内置类(anonymous inner classes)来实现闭包。当然,这虽然可以做到, 但看起来实在是些画蛇添足。PHP 闭包本身是没错,只是它的实现以及语法让我感到非常的困惑。

其他具有闭包特性的语言,它们可以非常方便的调用闭包并同时具有优雅的语法。在上面的例子 中,在 Scala 中使用传统的循环也可以工作,但你会这样写吗?而从另个方面,那么有人 说上面这个题目使用 PHP 的闭包也可以实现,但一般情况下你会这样写吗?

可以确定,PHP 闭包在些情况下可以成为锐利的军刀(例如延时执行以及资源调用方面), 但在传统的迭代以及数组操作面前就显得有些为难。不要气馁不管怎么样, 返璞归真编写具有兼容性的、清爽的代码以及 API 是最重要的。

结束语

像所有后来加上的语法特性一样(记得当年 Java 的 Generics 特性不?以及前几年的 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服务器搭建的重要性及方法
    本文深入探讨了为什么在开发PHP应用之前需要搭建服务器环境,以及如何选择和搭建适合的PHP服务器。 ... [详细]
  • Python 异步编程:深入理解 asyncio 库(上)
    本文介绍了 Python 3.4 版本引入的标准库 asyncio,该库为异步 IO 提供了强大的支持。我们将探讨为什么需要 asyncio,以及它如何简化并发编程的复杂性,并详细介绍其核心概念和使用方法。 ... [详细]
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
  • Symfony是一个功能强大的PHP框架,以其依赖注入(DI)特性著称。许多流行的PHP框架如Drupal和Laravel的核心组件都基于Symfony构建。本文将详细介绍Symfony的安装方法及其基本使用。 ... [详细]
  • MongoDB的核心特性与架构解析
    本文深入探讨了MongoDB的核心特性,包括其强大的查询语言、灵活的文档模型以及高效的索引机制。此外,还详细介绍了MongoDB的体系结构,解释了其文档、集合和数据库的层次关系,并对比了MongoDB与传统关系型数据库(如MySQL)的逻辑结构。 ... [详细]
  • 在寻找轻量级Ruby Web框架的过程中,您可能会遇到Sinatra和Ramaze。两者都以简洁、轻便著称,但它们之间存在一些关键区别。本文将探讨这些差异,并提供详细的分析,帮助您做出最佳选择。 ... [详细]
  • 开发笔记:由数据库某字段存数组引发的json_encode/serialize思考
    开发笔记:由数据库某字段存数组引发的json_encode/serialize思考 ... [详细]
  • 深入分析十大PHP开发框架
    随着PHP技术的发展,各类开发框架层出不穷,成为了开发者们热议的话题。本文将详细介绍并对比十款主流的PHP开发框架,旨在帮助开发者根据自身需求选择最合适的工具。 ... [详细]
  • 我有这种ajax$.ajax({type:"POST",url:"bee_sesi_edit.php&# ... [详细]
  • 本文详细解析了 MySQL 中的 ibdata1 文件,探讨其存储内容、快速增长的原因及解决策略。 ... [详细]
  • 大数据时代的机器学习:人工特征工程与线性模型的局限
    本文探讨了在大数据背景下,人工特征工程与线性模型的应用及其局限性。随着数据量的激增和技术的进步,传统的特征工程方法面临挑战,文章提出了未来发展的可能方向。 ... [详细]
  • 本文详细探讨了Scala中单例模式的实现方式,通过关键字object来模拟其他语言中的静态成员功能,同时介绍了伴生对象的概念及其应用场景。 ... [详细]
  • 本文实例为大家分享了Python实现代码统计工具的具体代码,供大家参考,具体内容如下思路:首先获取所有文件,然后统计每个文 ... [详细]
  • 嗨,我正在为我的api调用使用axious,并且无法在nodejs中解析我的数据,不确定为 ... [详细]
author-avatar
高档的干果ieb
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有