Scala 为帮助你编写函数代码提供的另一项功能是编写纯函数的能力。纯函数可以这样定义
- 如果给定相同的输入
x
,函数f
是纯函数,那么它始终返回相同的输出f(x)
- 该函数的输出仅取决于其输入变量及其实现
- 它仅计算输出,而不修改其周围的世界
这意味着
- 它不修改其输入参数
- 它不会改变任何隐藏状态
- 它没有任何“后门”:它不会从外部世界(包括控制台、网络服务、数据库、文件等)读取数据,也不会向外部世界写入数据
根据此定义,任何时候你使用相同输入值调用纯函数,你都将始终获得相同的结果。例如,你可以使用输入值 2
无限次调用 double
函数,你将始终获得结果 4
。
纯函数示例
根据该定义,你可以想象,scala.math._ 包中的以下方法是纯函数
abs
ceil
max
这些 String
方法也是纯函数
isEmpty
length
substring
Scala 集合类上的大多数方法也作为纯函数工作,包括 drop
、filter
、map
以及更多。
在 Scala 中,函数和方法几乎完全可以互换,因此即使我们使用通用行业术语“纯函数”,此术语也可以用来描述函数和方法。如果您有兴趣了解如何将方法用作函数,请参阅 Eta 扩展 讨论。
不纯函数的示例
相反,以下函数是不纯的,因为它们违反了定义。
println
– 与控制台、文件、数据库、Web 服务、传感器等交互的方法都是不纯的。currentTimeMillis
– 与日期和时间相关的函数都是不纯的,因为它们的输出取决于其输入参数之外的其他内容sys.error
– 抛出异常的方法是不纯的,因为它们不会简单地返回结果
不纯函数通常会执行以下一项或多项操作
- 从隐藏状态读取,即它们访问未明确作为输入参数传递给函数的变量和数据
- 写入隐藏状态
- 改变它们给定的参数,或改变隐藏变量,例如它们包含的类中的字段
- 与外部世界执行某种 I/O
通常,您应该注意返回类型为
Unit
的函数。由于这些函数不返回任何内容,因此从逻辑上讲,您调用它的唯一原因是实现某种副作用。因此,通常这些函数的使用是不纯的。
但需要不纯函数 …
当然,如果应用程序无法读写外部世界,那么它就没有什么用处,所以人们提出了这个建议
使用纯函数编写应用程序的核心,然后围绕该核心编写一个不纯的“包装器”以与外部世界进行交互。正如某人曾经说过的,这就像在纯蛋糕上放一层不纯的糖衣。
需要注意的是,有一些方法可以使与外部世界的交互不那么纯。例如,您会听说使用 IO
单子来处理输入和输出。这些主题超出了本文档的范围,因此为了简单起见,可以认为 FP 应用程序有一个纯函数的核心,这些函数用其他函数包装起来以与外部世界进行交互。
编写纯函数
注意:在本节中,常见的行业术语“纯函数”通常用于指代 Scala 方法。
要在 Scala 中编写纯函数,只需使用 Scala 的方法语法编写它们(虽然您也可以使用 Scala 的函数语法)。例如,这是一个将给定的输入值加倍的纯函数
def double(i: Int): Int = i * 2
如果您熟悉递归,这里有一个计算整数列表总和的纯函数
def sum(xs: List[Int]): Int = xs match {
case Nil => 0
case head :: tail => head + sum(tail)
}
def sum(xs: List[Int]): Int = xs match
case Nil => 0
case head :: tail => head + sum(tail)
如果您理解该代码,您会看到它符合纯函数定义。
要点
本节的第一个要点是纯函数的定义
纯函数 是一个只依赖于其声明的输入和实现来生成其输出的函数。它只计算其输出,并且不依赖或修改外部世界。
第二个要点是,每个现实世界的应用程序都与外部世界进行交互。因此,考虑函数式程序的一种简化方法是,它们由一个纯函数核心组成,该核心用与外部世界交互的其他函数包装起来。
本页的贡献者
内容
- 简介
- Scala 特性
- 为什么选择 Scala 3?
- Scala 入门
- Hello, World!
- REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 一等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 摘要
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- 面向对象建模
- 函数式建模
- 方法
- 方法特性
- Scala 3 中的主要方法
- 摘要
- 函数
- 匿名函数
- 函数变量
- Eta 展开
- 高阶函数
- 编写自己的 map 方法
- 创建返回函数的方法
- 摘要
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 摘要
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数即值
- 函数式错误处理
- 摘要
- 类型和类型系统
- 推断类型
- 泛型
- 交集类型
- 并集类型
- 代数数据类型
- 协变
- 不透明类型
- 结构类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- 上下文参数
- 上下文界限
- Given 导入
- 类型类
- 多重相等
- 隐式转换
- 摘要
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- 工作表
- 与 Java 交互
- 面向 Java 开发人员的 Scala
- 面向 JavaScript 开发人员的 Scala
- 面向 Python 开发人员的 Scala
- 下一步去哪里