什么是函数式编程?

函数式编程意味着使用函数来创建干净和可维护的软件以达到最佳效果。本文通过 JavaScript 和 Java 中的实际示例说明了函数式范式背后的概念。

函数式编程从最早的时候就一直是软件开发的潮流,但在现代时代具有新的重要性。本文着眼于函数式编程背后的概念,并通过 JavaScript 和 Java 中的示例提供实用的理解。

定义的函数式编程

函数是代码组织的基础;它们存在于所有高阶编程语言中。一般来说,函数式编程意味着使用函数来创建干净和可维护的软件的最佳效果。更具体地说,函数式编程是一组编码方法,通常被描述为编程范式

函数式编程有时被定义为与面向对象编程 (OOP) 和过程式编程相反。这是一种误导,因为这些方法不是相互排斥的,而且大多数系统倾向于同时使用这三种方法。

函数式编程在某些情况下提供了明显的好处,它在许多语言和框架中大量使用,并且在当前的软件趋势中很突出。它是一个有用且强大的工具,应该成为每个开发人员的概念和语法工具包的一部分。

纯函数

函数式编程的理想状态是所谓的纯函数。纯函数是其结果仅取决于输入参数的函数,其操作不会引发副作用,即除了返回值之外没有任何外部影响。

纯粹功能的美妙之处在于其架构的简单性。因为一个纯函数被简化为只有参数和返回值(即它的 API),它可以被看作是一个复杂的死胡同:它与它所运行的外部系统的唯一交互是通过定义的 API。

这与 OOP 形成对比,其中对象方法被设计为与对象(对象成员)的状态进行交互,并且与通常从函数内部操作外部状态的过程式代码形成对比。

然而,在实际实践中,函数通常最终需要与更广泛的上下文交互,正如 React 的useEffect钩子所证明的那样。

不变性

函数式编程哲学的另一个原则是不要修改函数之外的数据。在实践中,这意味着避免修改函数的输入参数。相反,函数的返回值应该反映所做的工作。这是一种避免副作用的方法。当函数在更大的系统中运行时,它可以更容易地推断函数的影响。

一级功能

除了纯函数理想之外,在实际的编码实践中,函数式编程还依赖于一流的函数。第一类函数是被视为“事物本身”的函数,能够独立存在并被独立对待。函数式编程试图利用语言支持将函数用作变量、参数和返回值来创建优雅的代码。

因为一流的函数是如此灵活和有用,所以即使是像 Java 和 C# 这样的强大的 OOP 语言也已经转移到合并一流的函数支持。这就是 Java 8 支持 Lambda 表达式的推动力。

描述第一类函数的另一种方式是作为数据的函数。也就是说,第一类函数可以像任何其他数据一样分配给变量。当您编写时,let myFunc = function(){}您将函数用作数据。

高阶函数

接受函数作为参数或返回函数的函数称为高阶函数——对函数进行操作的函数。

近年来,JavaScipt 和 Java 都增加了改进的函数语法。Java 添加了箭头运算符和双冒号运算符。JavaScript 添加了箭头运算符。这些运算符旨在使定义和使用函数更容易,尤其是作为匿名函数内联。匿名函数是在没有给定引用变量的情况下定义和使用的函数。

函数式编程示例:集合

也许函数式编程最突出的例子就是处理集合。这是因为能够在集合中的项目之间应用大块功能很自然地符合纯函数的想法。

考虑清单 1,它利用 JavaScriptmap()函数将数组中的字母大写。

清单 1. 在 JavaScript 中使用 map() 和匿名函数

let letters = ["a", "b", "c"];
console.info( letters.map((x) => x.toUpperCase()) ); // outputs ["A", "B", "C"]

这种语法的美妙之处在于代码非常集中。不需要命令式管道,例如循环和数组操作。这段代码清楚地表达了正在做的事情的思考过程。

如清单 2 所示,使用 Java 的箭头运算符可以实现相同的目的。

清单 2. 在 Java 中使用 map() 和一个匿名函数

import java.util.*; 
import java.util.stream.Collectors;
import static java.util.stream.Collectors.toList;
//...
List lower = Arrays.asList("a","b","c");
System.out.println(lower.stream().map(s -> s.toUpperCase()).collect(toList())); // outputs ["A", "B", "C"]

清单 2 使用 Java 8 的流库来执行大写字母列表的相同任务。请注意,核心箭头运算符语法实际上与 JavaScript 相同,它们做同样的事情,即创建一个接受参数、执行逻辑并返回值的函数。(重要的是要注意,如果这样定义的函数体周围没有大括号,那么返回值会自动给出。)

继续 Java,考虑清单 3 中的双冒号运算符。此运算符允许您引用类上的方法:在本例中,toUpperCase是 String 类上的方法。清单 3 与清单 2 做同样的事情。不同的语法在不同的场景中派上用场。

清单 3. Java 双冒号运算符

// ...
List upper = lower.stream().map(String::toUpperCase).collect(toList());

在上面的所有三个示例中,您都可以看到高阶函数在起作用。两种语言的map()函数都接受一个函数作为参数。

换句话说,您可以将函数传递给其他函数(在 Array API 或其他函数中)作为函数接口。提供者函数(使用参数函数)是通用逻辑的插件。

这看起来很像 OOP 中的策略模式(实际上,在 Java 中,在底层生成具有单个方法的接口),但函数的紧凑性使得组件协议非常紧凑。

作为另一个示例,请考虑清单 4,它在 Node.js 的 Express 框架中定义了一个路由处理程序。

清单 4. Express 中的功能性路由处理程序

var express = require('express');
var app = express();
app.get('/', function (req, res) {
  res.send('One Love!');
});

清单 4 是函数式编程的一个很好的示例,因为它允许明确定义映射路由以及处理请求和响应所需的确切内容——尽管可能有人认为在函数体中操作响应对象是一个副作用.

咖喱函数

现在考虑返回函数的函数的函数式编程概念。这比作为参数的函数少见。清单 5 有一个来自常见 React 模式的示例,其中粗箭头语法是链接的。

清单 5. React 中的柯里化函数

handleChange = field => e => {
e.preventDefault();
// Handle event
}

上面的目的是创建一个事件处理程序,它将接受相关字段,然后是事件。这很有用,因为您可以将其handleChange应用于多个字段。简而言之,同一个处理程序可用于多个字段。

清单 5 是一个柯里化函数的示例。“柯里化函数”是一个有点令人沮丧的名字。它尊重一个人,这很好,但它没有描述这个概念,这是令人困惑的。无论如何,这个想法是,当您拥有返回函数的函数时,您可以将调用链接在一起,这比创建具有多个参数的单个函数更灵活。

在调用这类函数时,您会遇到独特的“链式括号”语法:handleChange(field)(event).

大型编程

前面的示例提供了在重点上下文中对函数式编程的实际理解,但函数式编程旨在为大型编程带来更大的好处。换句话说,函数式编程旨在创建更清洁、更有弹性的大规模系统。

很难提供这方面的例子,但一个真实的例子是 React 推动功能组件的举措。React 团队注意到,组件的更简洁的功能样式提供了随着接口架构变得更大而复合的好处。

另一个大量使用函数式编程的系统是ReactiveX。基于 ReactiveX 使用的事件流类型的大型系统可以从解耦的软件组件交互中受益。Angular 全面采用 ReactiveX (RxJS) 作为对这种能力的认可。

变量范围和上下文

最后,作为范式不一定是函数式编程的一部分,但在进行函数式编程时需要注意的一个非常重要的问题是变量范围和上下文。

在 JavaScript 中,上下文具体表示this关键字解析的内容。在 JavaScript 箭头运算符的情况下,this指的是封闭的上下文。使用传统语法定义的函数接收自己的上下文。DOM 对象上的事件处理程序可以利用这一事实来确保this关键字引用正在处理的元素。

Scope是指变量范围,即哪些变量是可见的。对于所有 JavaScript 函数(胖箭头函数和传统函数)以及 Java 的箭头定义的匿名函数,作用域是封闭函数体的作用域——尽管在 Java 中,只有那些实际上是 final 的变量可以是访问。这就是为什么这些函数被称为闭包的原因。该术语表示该函数包含在其包含范围内。

记住这一点很重要:此类匿名函数可以完全访问范围内的变量。内部函数可以针对外部函数的变量进行操作。这可以被认为是非纯函数副作用。

最后“不积跬步,无以至千里”,希望未来的你能成为:有梦为马 随处可栖!加油,为大家收集了更多最新的Java面试资料,有文档、有攻略、有视频。有需要的同学可以关注+点赞私信找博主免费领取哦

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值