php inqude函数,优化函数式编程:向 PHP 移植 Clojure 函数

许多通用程序设计语言试图兼容大多数编程范式,PHP 就属于其中之一。不论你想要成熟的面向对象的程序设计,还是程序式或函数式编程,PHP 都可以做到。但我们不禁要问,PHP 擅长函数式编程吗?本文系国内 ITOM 管理平台 OneAPM工程师编译整理。

笔者在今年冬天开始时,在 Recurse Center致力于学习 Clojure,更加深入地了解了函数式编程,并重新拾起 PHP 的客户端工作。但笔者仍然希望运用一些高阶函数和概念,并对它们进行研究。

笔者已经在 PHP 中实施了模拟 LISP 语言,并看到了一些在 PHP 中通过使用 underscore 类库以兼容某些关键函数方法的尝试。但为了使 Clojure 在写入其它编程语言时仍然保有较高的速度,笔者特意镜像 Clojure 的标准库,以使自己能在编写真正的 PHP 代码时,以 Clojure 的方式思考。虽然在学习的过程中绕了一些弯路,笔者仍然愿意向各位展示自己是如何实现 interleave 函数的。

幸运地是,已经有人执行了 arraysome 和 arrayevery,并且非常地道(至少笔者这么认为)。

/**

* Returns true if the given predicate is true for all elements.

* credit: array_every and array_some.php

* https://gist.github.com/kid-icarus/8661319

*/

function every(callable $callback, array $arr) {

foreach ($arr as $element) {

if (!$callback($element)) {

return FALSE;

}

}

return TRUE;

}

/**

* Returns true if the given predicate is true for at least one element.

* credit: array_every and array_some.php

* https://gist.github.com/kid-icarus/8661319

*/

function some(callable $callback, array $arr) {

foreach ($arr as $element) {

if ($callback($element)) {

return TRUE;

}

}

return FALSE;

}

我们只要简单地取消调用 every 函数,就可以运用 not-every 函数插入一些容易实现的目标,同时仍然有相同 signature。

/**

* Returns true if the given predicate is not true for all elements.

*/

function not_every(callable $callback, array $arr) {

return !every($callable, $arr);

}

如你所见,笔者已经去掉了前缀 array。PHP 的不便之处在于强调序函数,通常使用前缀 array 来运行数列。笔者将此理解为这两种函数的作者是在相互模仿。虽然数列在 PHP 中已经形成事实数据结构,但标准数据库以此种方式被写入并不常见。

这一标准适用于基本高阶函数,你可以使用 arraymap、arrayreduce和 arrayfilter 结尾,而不是 map,recude 和 filter。如果这些还不够,那参数便不一致了。arrayreduce 和 arrayfilter 都以数列为第一个参数,然后以回调值作为第二个参数,首先调回 arraymap。在 Clojure 中,通常首先运行回调函数,所以让我们将这些函数重新命名,然后只需一步就能使这些签名变得正常:

/**

* Applies callable to each item in array, return new array.

*/

function map(callable $callback, array $arr) {

return array_map($callback, $arr);

}

/**

* Return a new array with elements for which predicate returns true.

*/

function filter(callable $callback, array $arr, $flag=0) {

return array_filter($arr, $callback, $flag);

}

/**

* Iteratively reduce the array to a single value using a callback function

*/

function reduce(callable $callback, array $arr, $initial=NULL) {

return array_reduce($arr, $callback, $initial);

}

我们目前没有其它方法,所以当 Clojure 中的 reduce 函数通过了初始值并作为第二个参数时,它便有了另一个签名。鉴于此,我们从现在开始就将 initial 作为最终值——毕竟相对于原函数来说,这仍然是一大进步。另外,我们也将在过滤函数中保留 $flag,它决定了是否全部通过键和值,还是只通过键。

在 Clojure 中,first 和 last 是十分有用的两个函数,相当于 PHP 中的 arrayshift 和 arraypop。它们的关键不同之处在于:PHP 中两个命令具有毁坏性。以 arrayshift 为例,它返回数列的第一项,同时又从原始数列中移除该项(当数列被引用通过时)。在函数式编程中,目标之一是减轻副作用。所以在后台用 first 和 last 函数将数列复制一份,这样原始数列就永远不会被更改了。与之相对应的是 rest 和 but-last 函数,我们可以继续使用 arrayslice 来返回该部分。

/**

* Returns the first item in an array.

*/

function first(array $arr) {

$copy = array_slice($arr, 0, 1, true);

return array_shift($copy);

}

/**

* Returns the last item in an array.

*/

function last(array $arr) {

$copy = array_slice($arr, 0, NULL, true);

return array_pop($copy);

}

/**

* Returns all but the first item in an array.

*/

function rest(array $arr) {

return array_slice($arr, 1, NULL, true);

}

/**

* Returns all but the last item in an array.

*/

function but_last(array $arr) {

return array_slice($arr, 0, -1, true);

}

当然,这些都只是低阶函数,可能看起来并不那么让人兴奋,但它们迟早会有用。顺便问一下,大家知道 PHP 中与这些函数相对应的「应用」( https://en.wikipedia.org/wiki/Apply)吗?答案可能是否定的。因为它们的名字十分深奥,不像其它编程语言中那些概念相同但名称普通的命令。让我们继续将 calluserfunc_array 替换为 apply 函数吧。

/**

* Alias call_user_func_array to apply.

*/

function apply(callable $callback, array $args) {

return call_user_func_array($callback, $args);

}

这太让人兴奋了!当我们将函数名称变得地道,并创建出低级别的抽象名称,便有了一个能帮助我们创建更多有趣名称的平台。让我们用 apply 帮助我们创建 complement:

function complement(callable $f) {

return function() use ($f) {

$args = func_get_args();

return !apply($f, $args);

};

}

这里使用了 funcgetargs()函数,当所有值通过原始函数时,它就能够抓取一个数列,这一数列中所有的值都按照它们通过时的顺序排列。我们继续返回匿名函数,该函数能通过 use 获取原始函数 $f(因为所有的函数在PHP中都有新的域),然后在 $args 中调用 apply。

太好了,现在我们有了 complement 函数,它能让我们更加容易地实施与filter 函数相反的 remove 函数。通过返回回调的 complement 传递给filter,当所有数据与预设条件不相符时,返回所有数据。

/**

* Return a new array with elements for which predicate returns false.

*/

function remove(callable $callback, array $arr, $flag=0) {

return filter(complement($callback), $arr, $flag);

}

换个角度来说,array_merge 和 contact 是等效的。下面以 Cons 和 conj 为例,在 Clojure 中,它们是向集合的开始或末尾增加项的标准方式,

/**

* Alias array_merge to concat.

*/

function concat() {

$arrs = func_get_args();

return apply('array_merge', $arrs);

}

/**

* cons(truct)

* Returns a new array where x is the first element and $arr is the rest.

*/

function cons($x, array $arr) {

return concat(array($x), $arr);

}

/**

* conj(oin)

* Returns a new arr with the xs added.

* @param $arr

* @param & xs add'l args to be added to $arr.

*/

function conj() {

$args = func_get_args();

$arr = first($args);

return concat($arr, rest($args));

}

例如,现在调用这两个函数,会生成相同的结果:

cons(1, array(2, 3, 4));

conj(array(1), 2, 3, 4);

这些低阶工具足以让 interleave 的书写变得十分简单。首先,我们使用funcgetargs,取代在函数签名中使用声明参数,这样便能采用大量的数列作为函数参数。然后,我们将每个数列的第一项提出来组成一个新的数列,余下的每个数列作为每一个新数列。接着,检查每个数列是否都保留有元素,再使用 concat 函数连接交错数列的结果,如此反复。以可读的实施以及与 Clojure 版本几乎无差别的函数结果为结束,得到的结果就是证明 Clojure 生成了惰性序列。

/**

* Returns a sequence of the first item in each collection then the second, etc.

*/

function interleave() {

$arrs = func_get_args();

$firsts = map('first', $arrs);

$rests = map('rest', $arrs);

if (every(function($a) { return !empty($a); }, $rests)) {

return concat($firsts, apply('interleave', $rests));

}

return $firsts;

}

因此,当我们调用长度可变的数列来制作函数时:

interleave([1, 2, 3, 4], ["a", "b", "c", "d", "e"], ["w", "x", "y", "z"])

插入所有三个数列减去多余项,以其作为结果数列并以此结尾:

array (

0 => 1,

1 => 'a',

2 => 'w',

3 => 2,

4 => 'b',

5 => 'x',

6 => 3,

7 => 'c',

8 => 'y',

9 => 4,

10 => 'd',

11 => 'z',

)

当然,Clojure 有非常棒的功能性,在这里我们并没有提到,例如 interleave,它是返回惰性序列,而不是静态采集。此外,由于数列会像 PHP 中的映射一样加倍,那些类似于 assoc 的模拟方法就变得模棱两可。如果大家对这些感兴趣,并且想在下一个项目中使用它们,这些代码已放到 GitHub 上供您阅读参考。

本文系 OneAPM 工程师编译整理。OneAPM 是应用性能管理领域的新兴领军企业,能帮助企业用户和开发者轻松实现:缓慢的程序代码和 SQL 语句的实时抓取。想阅读更多技术文章,请访问 OneAPM 官方博客。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值