如果你研究几个框架和大规模的应用程序,你肯定会在某些地方看到高阶函数。许多语言都支持高阶函数的概念,包括JavaScript、Java、.NET、Python甚至PHP,仅举几例。
但什么是高阶函数,我们为什么要使用高阶函数?它能给我们带来什么好处,我们能用它来简化我们的代码吗?在这篇文章中,我们将专门讨论PHP中的高阶函数,但也将展示它们在其他语言中的使用情况,以便进行比较。
第一类函数
在我们讨论什么是高阶函数之前,我们必须首先明白,我们必须有一种语言能够支持一种叫做第一类函数(又称一阶函数)的特性。这意味着该语言将函数视为一流的公民。换句话说,该语言对待函数就像对待变量一样。你可以将一个函数存储在一个变量中,将其作为一个变量传递给其他函数,像变量一样从函数中返回,甚至像对待变量一样将其存储在数组或对象属性等数据结构中。现在的大多数现代语言都默认有这个功能。你真正需要知道的是,一个函数可以像变量那样被传递和使用。
为了我们的目的,我们将主要关注作为参数传递的函数和作为结果返回的函数,并简要地谈一谈非局部变量和闭包的概念。(你可以在上一段所链接的维基百科文章的1.1、1.4和1.3节中阅读更多关于这些概念的内容)。
什么是高阶函数?
有两个主要特征可以识别高阶函数。一个高阶函数可以只实现以下一个或两个想法:一个以一个或多个函数作为输入的函数,或以一个函数作为输出的函数。在PHP中,有一个关键词可以清楚地说明一个函数是高阶函数:关键词callable 。虽然这个关键字不一定要出现,但这个关键字使我们很容易识别它们。如果你看到一个函数或方法有一个可调用的参数,这意味着它需要一个函数作为输入。另一个简单的标志是如果你看到一个函数使用其return 语句返回一个函数。返回语句可能只是函数的名称,甚至可能是一个匿名/内联函数。下面是每种类型的一些例子。
// Simple user-defined function we'll pass to our higher-order function
function echoHelloWorld() {
echo "Hello World!";
}
// Higher-order function that takes a function as an input and calls it
function higherOrderFunction(callable $func) {
$func();
}
// Call our higher-order function and give it our echoHelloWorld() function.
// Notice we pass just the name as a string and no parenthesis.
// This echos "Hello World!"
higherOrderFunction('echoHelloWorld');
下面是一些简单的高阶函数的例子,它返回一个函数。
// Returns an existing function in PHP called trim(). Notice it's a simple string with no parentheses.
function trimMessage1() {
return 'trim';
}
// Here's an example using an anonymous inline function
function trimMessage2() {
return function($text) {
return trim($text);
};
}
// Returns the 'trim' function
$trim1 = trimMessage1();
// Call it with our string to trim
echo $trim1(' hello world ');
// Returns the anonymous function that internally calls 'trim'
$trim2 = trimMessage1();
// Call it again with our string to trim
echo $trim2(' hello world ');
正如你所想象的,你可以接收一个函数,也可以用它来生成另一个返回的函数。很整洁的技巧,对吗?但你为什么要做这些事呢?这听起来像是让事情变得更复杂的东西。
为什么要使用或创建一个高阶函数?
你可能想在你的代码中创建高阶函数,有几个原因。从代码的灵活性,代码的重用,代码的扩展,到模仿你在另一个程序中看到的代码解决方案。虽然原因很多,但我们将在这里介绍其中的几个。
增加代码的灵活性
高阶函数增加了大量的灵活性。根据前面的例子,你可能可以看到这样的东西有一些用途。你可以写一个单一的函数来接收一整套不同的函数并使用它们,而不必写代码来单独执行它们。
也许高阶函数本身并不知道它将接收什么类型的函数。谁说这个函数必须知道它所接收的函数的任何情况?前一分钟,输入的函数可能是一个add() ,而下一分钟,它可能是一个divide() 。不管是哪种情况,它都是有效的。
轻松地扩展你的代码
这种功能也使得以后添加额外的输入函数变得更加容易。假设你有add() 和divide() ,但接下来你需要添加一个sum() 函数。你可以编写sum() 函数,并通过高阶函数进行管道连接,而不需要改变它。在后面的例子中,我们将演示如何使用这个功能来创建我们自己的calc() 函数。
模仿其他语言的功能
在PHP中使用高阶函数的另一个原因是为了模拟Python中的装饰器之类的行为。将一个函数 "包裹 "在另一个函数中以修改该函数的行为方式。例如,你可以写一个函数为其他函数计时。你写一个高阶函数,它接收一个函数,启动一个定时器,调用该函数,然后结束定时器,看看已经过了多少时间。
PHP中现有的高阶函数的例子
要在PHP中找到高阶函数的例子其实很简单。看一下PHP文档,找到一个接受callable 输入参数的函数/方法,你就找到了一个。下面是几个常用的高阶函数的例子。你可能甚至已经使用过它们而不知道它们是高阶函数。
高阶动态二人组:array_map 和array_filter
$arrayOfNums = [1,2,3,4,5];
// array_map: example of taking an inline anonymous function
$doubledNums = array_map(function($num) {
return $num * 2;
}, $arrayOfNums);
var_dump($doubledNums); // Prints out array containing [2, 4, 6, 8, 10];
让我们看看如何使用另一个,这次是用我们单独定义的一个过滤函数。
$arrayOfNums = [1,2,3,4,5];
// Example of creating a function and giving it to a higher-order function array_filter()
function isEven($num) {
return ($num % 2) === 0;
}
// array_filter is a higher-order function
$evenNums = array_filter($arrayOfNums, 'isEven');
var_dump($evenNums); // Prints out array containing [2, 4]
array_filter 和 是两个非常流行的高阶函数,你会在很多代码项目中发现它们。另一个你可能会用到的是 ,它接收一个函数和一个参数列表并调用该函数。这本质上是一个定制的高阶函数。它的全部目的是调用用户定义的其他函数。array_map call_user_func
function printCustomMessage($message) {
echo "$message";
}
call_user_func('printCustomMessage', 'Called custom message through the use of call_user_func!');
继续阅读PHP中的功能编程。高阶函数在SitePoint上