许多程序员喜欢谈论函数式编程,但如果你问他们是否实际运用过,大多数的回答都会是“没有”。原因很简单:我们初学编程时,就被教导以命令式思维方式思考,即程序流程图和步骤。因此,本文将解释函数式编程的一些重要概念以及如何在 PHP 中编写函数式代码。
关键要点
函数式编程的重要概念
维基百科将函数式编程定义为“一种将计算视为数学函数的求值并避免状态和可变数据的编程范例”。在函数式编程中,函数被视为一等公民,而在命令式编程中,我们主要关注数据以及改变数据以达到预期结果的步骤。当我们说函数是一等公民时,这意味着我们可以像在命令式编程中使用值一样使用函数。它们可以作为参数传递给函数,在另一个函数内定义,甚至可以作为结果返回。换句话说,“函数就是值”。我们稍后将再次讨论这一点,但函数式编程还有许多其他重要概念。仅举几例:
不变性是指变量的值一旦定义就不能更改的行为。不同的语言有不同的实现方式;例如,在 PHP 中,使变量不变的唯一方法是将其定义为常量。
递归在函数式编程中也很突出。在命令式编程中,当我们需要操作集合或数组时,可以使用 for 和 foreach 等循环结构,遍历每个元素并使用临时变量来保存当前值。但是,由于不变性,这种方法在函数式编程中是不可能的。递归是答案,因为这种簿记是通过调用堆栈隐式完成的。假设我们想编写一个函数来查找数组中所有元素的总和(暂时忘记 array_sum() 的存在)。以函数式风格,我们将编写:
<?php function sum($array) { if (empty($array)) return 0; else return $array[0] + sum(array_slice($array, 1)); } $total = sum(array(1, 2, 3)); // 6 ?>
空列表将返回 0,这是我们的基本条件。对于包含多个值的数组,它将返回第一个元素与所有其他元素的递归总和的相加结果。
如果一个函数不改变自身外部对象的(例如全局变量或静态变量)的值,并且没有任何 I/O 效应(例如写入文件、数据库等),则称该函数没有副作用。此类函数也称为纯函数。对于给定的参数集,纯函数的输出将始终相同,这导致了另一个称为引用透明性的属性。当函数是引用透明的时,我们可以用它的值替换该函数,而不会影响程序的行为。所有数学函数都是纯函数,而日期函数、rand() 等则是非纯函数。
上述概念几乎可以在任何编程语言中实现,但一等公民函数和高阶函数是函数式编程的两个最显著特征。我已经解释了一等公民函数意味着函数可以被视为值。高阶函数是可以将函数作为参数并可以返回函数作为结果的函数。最近添加的两个重要功能使我们能够在 PHP 中编写高阶函数:lambda 表达式和闭包。
lambda 函数(也称为匿名函数)只是一个没有名称的函数。当我们定义匿名函数时,会返回对该函数的引用,该引用存储在一个变量中以供以后使用。我们使用此变量在需要时调用该函数。许多不同的语言都采用了这个概念。事实上,你可能在日常的 JavaScript 编程中使用 lambda 函数,将它们作为不同用户交互和 Ajax 调用的回调函数。
$("#myButton").click(function () { // do something });
这段代码非常简单易懂,这可能会让我们忘记它的函数式方面。PHP 在 5.3 版本中引入了这个强大的功能,它允许我们以类似的方式编写 PHP 代码:
<?php $square = function ($arg) { return $arg * $arg; }; $value = $square(2); // 4 ?>
在谈论函数,特别是匿名函数时,了解如何处理变量作用域非常重要。例如,JavaScript 允许你在 lambda 内部访问外部作用域的变量,而 PHP 则不允许。lambda 内部有它自己的作用域,就像常规 PHP 函数一样。
有时,你可能希望在函数内部引用父作用域中的变量。闭包类似于 lambda 函数,但略有不同,你可以访问外部作用域的变量。我们可以使用“reach out”并使用 PHP 的 use 关键字绑定外部变量,该关键字也在 PHP 5.3 中引入。
<?php function sum($array) { if (empty($array)) return 0; else return $array[0] + sum(array_slice($array, 1)); } $total = sum(array(1, 2, 3)); // 6 ?>
在这种情况下,我们不会在每次调用函数时都传递利率。相反,我们将其定义在外部,并使用 use 关键字使其在函数内部可用。
简单来说,部分函数是从现有函数创建的函数,通过部分应用其参数。你只需要在调用创建的函数时传递剩余的参数。我们可以使用闭包在 PHP 中创建部分函数。这是一个示例,用于根据其长度、宽度和高度查找盒子的体积。所有参数都是可选的;如果你没有提供所有参数,该函数将返回另一个函数以接受剩余的必要值。
$("#myButton").click(function () { // do something });
所有参数都是可选的。首先检查调用者是否传递了所有参数。在这种情况下,我们可以通过将长度、宽度和高度相乘直接返回体积。如果参数数量少于参数,则返回一个新函数以查找预先设置了给定参数的体积。现在假设我们大多数时候都在查找长度固定的盒子(例如 10)的体积。这可以通过将 10 作为第一个参数轻松完成,或者我们可以通过将 10 作为第一个参数来创建部分函数,然后只请求剩余的值。
<?php $square = function ($arg) { return $arg * $arg; }; $value = $square(2); // 4 ?>
柯里化是部分函数的一种特殊情况,你将一个接受多个参数的函数转换为多个每个都接受单个参数的函数。例如,类似于 f(x,y,z) 到 f(x)(y)(z)(尽管 PHP 语法不允许像这样嵌套函数调用)。如果你有兴趣了解更多信息,Timothy Boronczyk 撰写了一篇关于使用实际示例进行柯里化的优秀文章。
优点和缺点
函数式编程的功能在 PHP 中有很多实际用途。例如,lambda 函数在使用回调函数时被广泛使用。例如,使用 Slim 框架,你可以定义如下路由:
<?php function sum($array) { if (empty($array)) return 0; else return $array[0] + sum(array_slice($array, 1)); } $total = sum(array(1, 2, 3)); // 6 ?>
当请求 URL 与此路由匹配时,Slim 会调用回调函数。Vance Lucas 之前写过一些关于 Lambda 函数的其他有趣用例的文章。通过避免状态和可变数据来鼓励安全编程。在函数式编程中,你应该编写每个只做一件事情并且不会产生任何副作用的函数。该范例对模块化和函数简洁性的强调可以使更容易根据不同的、小的子程序来推断你的程序。函数式编程还可以帮助你编写专注于你想要实现的目标的代码,而不是明确地管理过程中的偶然事件(将递归与必须管理循环计数器变量进行比较)。但是请记住,传统上与函数式编程相关的某些优点并不适用于 PHP,因为它并非设计为函数式编程语言。例如,无副作用的函数非常适合并行处理,但 PHP 脚本不会以这种方式运行。也不总是容易计算递归和惰性函数的成本,并且由于内部开销,可能会出现严重的性能问题。有时,为了提高效率,用可变性来编写程序更有意义。也许函数式编程最大的缺点是对于那些接受过命令式训练的人来说,它的学习曲线非常陡峭。但总的来说,函数式编程很有趣,学习它将为你提供思考旧问题的新的工具,帮助你作为程序员成长。它不是一个万能的解决方案,但可以根据需要应用于更简洁、更优雅的 PHP 代码。
总结
函数式编程不仅仅是一种编程范例;它是一种思考和推理程序的方式。如果你能进行函数式思考,你几乎可以用任何语言进行函数式编程。在本文中,我们讨论了函数式编程的基础知识,利用 PHP 的功能来编写并提供它们的示例。虽然本文中给出的示例可能对你来说并不实用,但你会发现许多情况,函数式风格可以显著提高你正在编写的代码的质量。尝试寻找这样的案例,进行函数式思考,并享受乐趣!图片来自 Fotolia
关于 PHP 函数式编程的常见问题
函数式编程和面向对象编程是 PHP 中使用的两种不同的范例。主要区别在于它们如何管理状态和数据。在函数式编程中,函数是一等公民,并且没有状态的概念。这意味着给定相同的输入,函数将始终产生相同的输出。另一方面,面向对象编程围绕对象及其交互展开,这些对象可以维护状态并随时间变化。这可能会导致不同的输出,即使输入相同也是如此。
要开始使用 PHP 进行函数式编程,你需要了解基本概念,例如纯函数、不变性和高阶函数。然后,你可以开始编写不改变状态且不产生副作用的函数。PHP 具有支持函数式编程的内置函数,例如 array_map
、array_filter
和 array_reduce
。你还可以使用 Laravel 集合之类的库,这些库为使用数据数组提供流畅、便捷的包装器。
PHP 中的函数式编程可以编写更简洁、更易读的代码。它可以帮助你避免常见的编程问题,例如副作用和状态更改,这可以使你的代码更易于预测和测试。函数式编程还可以编写更模块化的代码,因为函数可以轻松组合和重用。
虽然函数式编程有其优点,但它也有一些挑战。PHP 最初并非设计为函数式编程,因此某些功能可能不如为函数式编程设计的语言那样强大或高效。此外,函数式编程需要不同的思维方式,对于习惯于命令式或面向对象编程的开发人员来说,学习曲线可能很陡峭。
是的,PHP 是一种多范例语言,这意味着你可以混合使用不同的编程风格。你可以将面向对象编程用于从状态和行为中受益的应用程序部分,并将函数式编程用于从无状态、无副作用函数中受益的应用程序部分。这可以让你灵活地为应用程序的每个部分选择最佳方法。
函数式编程对 PHP 性能的影响可能会有所不同。在某些情况下,函数式编程可以编写更高效的代码,因为它避免了状态更改和副作用。但是,在其他情况下,它可能效率较低,因为它通常涉及创建新对象而不是修改现有对象。重要的是要分析和测试你的代码,以确保它满足你的性能要求。
有很多资源可用于学习 PHP 函数式编程。一些好的起点包括 PHP 手册(其中有一节关于函数式编程的内容)以及在线教程和文章。还有一些关于这个主题的书籍,例如 Gilles Crettenand 的《PHP 函数式编程》。
是的,你可以在 PHP 中使用函数式编程进行 Web 开发。函数式编程可以帮助你编写更简洁、更模块化的代码,这在 Web 开发环境中可能很有益。但是,请记住,PHP 是一种多范例语言,因此你也可以使用其他编程风格,例如面向对象编程。
在函数式编程中,错误处理通常是通过使用单子来完成的,单子是一种可以表示计算而不是值的数据结构。在 PHP 中,你可以使用 Maybe 单子进行错误处理。这允许你将操作链接在一起,如果任何操作失败,则跳过其余链。
函数式编程可以用于大型应用程序,但这取决于应用程序的具体要求。函数式编程可以编写更简洁、更模块化的代码,这在大规模环境中可能很有益。但是,在某些情况下它也可能效率较低,因此重要的是要考虑权衡。
以上是PHP主| PHP中的功能编程的详细内容。更多信息请关注PHP中文网其他相关文章!