首页 / 教程资源

PHP 中的函数式编程:高阶函数

发布时间:2023-03-08 14:44:46

如果检查多个框架和大型应用程序,你肯定会在某个时候看到高阶函数。许多语言都支持高阶函数的概念,包括 JavaScript、Java、.NET、Python 甚至 PHP 等等。

但是什么是高阶函数,我们为什么要使用它呢?它给我们带来了什么好处,我们可以用它来简化我们的代码吗?在本文中,我们将专门讨论 PHP 中的高阶函数,但将展示它们如何在其他语言中使用以进行比较。

一流的功能

在我们了解什么是高阶函数之前,我们必须首先了解我们必须有一种语言可以支持称为一等函数(也称为一阶函数)的特性。这意味着该语言将函数视为一等公民。换句话说,该语言对待函数就像对待变量一样。你可以将函数存储在变量中,将其作为变量传递给其他函数,从函数返回它们,例如变量,甚至将它们存储在数据结构中,例如数组或对象属性,就像你可以使用变量一样。现在大多数现代语言都默认具有此功能。真正需要知道的是函数可以像变量一样传递和使用。

出于我们的目的,我们将主要关注将函数作为参数传递和将函数作为结果返回,并简要介绍非局部变量和闭包的概念。

什么是高阶函数?

识别高阶函数有两个主要特征。高阶函数可以实现以下思想中的一个或两个:将一个或多个函数作为输入或返回一个函数作为输出的函数。在 PHP 中,有一个关键字可以明确表明函数是高阶的:关键字callable。虽然不一定要出现此关键字,但关键字可以很容易地识别它们。如果你看到具有可调用参数的函数或方法,则表示它采用函数作为输入。另一个简单的迹象是,如果你看到一个函数使用它的return语句返回一个函数。return 语句可能只是函数的名称,甚至可以是匿名/内联函数。以下是每种类型的一些示例。

// Simple user-defined function we'll pass to our higher-order functionfunction echoHelloWorld() {  echo "Hello World!";}

// Higher-order function that takes a function as an input and calls itfunction 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 functionfunction trimMessage2() { return function($text) { return trim($text); };}

// Returns the 'trim' function$trim1 = trimMessage1();

// Call it with our string to trimecho $trim1(' hello world ');

// Returns the anonymous function that internally calls 'trim'$trim2 = trimMessage1();

// Call it again with our string to trimecho $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 中工作的示例,并且我们已经创建了一些自定义函数来演示它们的各种用途。但是,让我们通过一个我们称之为calc() 的新函数来进一步展示灵活性。这个函数将接受两个参数和一个函数,该函数将对这两个参数进行操作以给我们一个结果:

function add($a, $b) {  return $a + $b;}

function subtract($a, $b) { return $a - $b;}

function multiply($a, $b) { return $a * $b;}

// Higher-order function that passes along $n1 and $n2function calc($n1, $n2, $math_func) { return $math_func($n1, $n2);}

$addedNums = calc(1, 5, 'add');$subtractedNums = calc(1, 5, 'subtract');$multipliedNums = calc(1, 5, 'multiply');

// Prints 6echo "Added numbers: $addedNums\n";

// Prints -4echo "Subtracted numbers: $subtractedNums\n";

// Prints 5echo "Multiplied numbers: $multipliedNums\n";

请注意,我们的calc()函数编写得非常通用,可以接收一组其他函数并使用参数$n1和$n2调用它们。在这种情况下,我们的计算函数不关心它接收的函数是做什么的。它只是将参数传递给函数并返回结果。

但是:我们的老板刚刚要求我们在现有系统中添加一个将两个字符串相加的功能。但是连接字符串并不是典型的数学运算。通过此处的设置,我们只需要添加字符串连接并将其通过管道传递calc():

function addTwoStrings($a, $b) {  return $a . " " . $b;}

// Prints "Hello World!"$concatenatedStrings = calc('Hello', 'World!', 'addTwoStrings');

虽然此示例非常人为,但它演示了如何在更大的项目中使用该概念。也许高阶函数决定了事件的处理方式。也许,根据触发的事件,你可以通过传入的处理函数来路由它。可能性是无限的。

这与其他具有高阶函数的语言相比如何?让我们以 Python 中的一个简单示例装饰器为例,将其与 JavaScript 进行比较,然后将其与 PHP 进行比较。这样,如果你懂 Python 或 JS,就可以看出它们是如何等效的。

与 Python 和 JavaScript 的并排比较

def say_message(func):    def wrapper():        print('Print before function is called')        func()        print('Print after function is called')    return wrapper

def say_hello_world(): print("Hello World!")

# Pass our hello world function to the say_message function. # It returns a function that we can then call# Note: This line could have been replaced with the @say_message (pie syntax) on the say_hello_world methodsay_hello_world = say_message(say_hello_world)

# Call the created functionsay_hello_world()

# Output...# Print before function is called# Hello World!# Print after function is calle

现在让我们看看如何在 JavaScript 高阶函数中实现这一点:

// Takes a function, returns a function.function say_message(func) {  return function() {      console.log("Print before function is called");      func();      console.log("Print after function is called");  }}



function say_hello_world() { console.log("Hello World!");}



// Again pass our function to say_message as an input parameter// say_message returns a function that is wrapped around the say_hello_world functionsay_hello = say_message(say_hello_world);



// Call the functionsay_hello();



// Output...// Print before function is called// Hello World!// Print after function is called

最后,让我们将其与 PHP 进行比较:

// Takes a function, returns a function.function say_message($func) {  // We use 'use' so that our anonymous function can see $func coming in  return function() use ($func) {      echo "Print before function is called";      $func();      echo "Print after function is called";  };}



function say_hello_world() { echo "Hello World!";}



// Again pass our function to say_message as an input parameter// say_message returns a function that is wrapped around the say_hello_world function$say_hello = say_message('say_hello_world');



// Call the function$say_hello();



// Output...// Print before function is called// Hello World!// Print after function is called

从并排比较可以看出,PHP 版本与 JavaScript 语法非常相似。但是,如果你对 Python 和装饰器有所了解,你就会明白装饰器是如何被翻译成其他两种编程语言之一的。

快速了解 Lambdas/匿名函数

很多时候,当使用高阶函数时,你可能会看到内联匿名函数(也称为 lambda)的使用。在我们上面看到的一些示例中,我使用了这种格式。除此之外,我还使用了更明确的版本,即首先定义函数,然后将其传递给我们的高阶函数。这是为了让你习惯看到这两个版本。通常情况下,你会在大量使用高阶函数的框架中看到内联 lambda 版本。不要让这种格式丢给你。他们只是创建一个简单的函数,没有名称,并使用它来代替你给出独立函数名称的地方。

结论

在本文中,我们介绍了使函数成为高阶函数的基本定义。任何接受一个函数或返回一个函数(或两者)的函数都可以被认为是高阶函数。我们还讨论了为什么你可能想要创建这些类型的函数以及它们的优势是什么。

然后,我们展示了 PHP 中现有高阶函数的一些示例,例如array_map和array_filter,然后继续创建我们自己的高阶函数,称为calc(),它接受多个输入函数并处理几个参数。然后我们进行了并排比较,展示了 PHP 的解决方案与 Python 装饰器和 JavaScript 实现相比如何。

相关推荐