Scala 提供的另一个帮助您编写函数式代码的特性是编写纯函数的能力。 一个_纯函数_可以这样定义:
- 函数
f
是纯函数,如果给定相同的输入x
,它总是返回相同的输出f(x)
- 函数的输出_只_取决于它的输入变量和它的实现
- 它只计算输出而不修改周围的世界
这意味着:
- 它不修改其输入参数
- 它不会改变任何隐藏状态
- 没有任何“后门”:不从外界读取数据(包括控制台、Web服务、数据库、文件等),也不向外界写入数据
作为这个定义的结果,任何时候你调用一个具有相同输入值的纯函数,你总是会得到相同的结果。
例如,您可以使用输入值 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
Monad 来处理输入和输出。
这些主题超出了本文档的范围,所以为了简单起见,这样认识 FP 会有所帮助: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)
如果您理解该代码,您会发现它符合纯函数定义。
要点
本节的第一个要点是纯函数的定义:
_纯函数_是仅依赖于其声明的输入及其实现来产生其输出的函数。 它只计算其输出,不依赖或修改外部世界。
第二个要点是每个现实世界的应用程序都与外部世界交互。 因此,考虑函数式程序的一种简化方法是,它们由一个纯函数核心组成,其他一些函数把这些纯函数包装起来与外部世界交互。
Contributors to this page:
Contents
- 导言
- Scala 3 特性
- 为什么是 Scala 3 ?
- Scala 的味道
- Hello, World!
- The REPL
- 变量和数据类型
- 控制结构
- 领域建模
- 方法
- 头等函数
- 单例对象
- 集合
- 上下文抽象
- 顶层定义
- 总结
- 类型初探
- 字符串插值
- 控制结构
- 领域建模
- 工具
- OOP 领域建模
- 函数式领域建模
- 方法
- 方法特性
- main 方法
- 总结
- 函数
- 匿名函数
- 函数变量
- Eta 扩展
- 高阶函数
- 自定义 map 函数
- 创建可以返回函数的方法
- 总结
- 打包和导入
- Scala 集合
- 集合类型
- 集合方法
- 总结
- 函数式编程
- 什么是函数式编程?
- 不可变值
- 纯函数
- 函数是值
- 函数式错误处理
- 总结
- 类型和类型系统
- 类型推断
- 泛型
- 相交类型
- 联合类型
- 代数数据类型
- 型变
- 不透明类型
- 结构化类型
- 依赖函数类型
- 其他类型
- 上下文抽象
- 扩展方法
- Given 实例和 Using 语句
- 上下文绑定
- Given 导入
- 实现类型类
- 多元相等性
- 隐式转换
- 总结
- 并发
- Scala 工具
- 使用 sbt 构建和测试 Scala 项目
- worksheet
- 与 Java 交互
- 向 Java 开发者介绍Scala
- Scala for JavaScript Developers
- Scala for Python Developers
- 下一步去哪