Scala - 纯函数
本章将带您了解 Scala 编程中的纯函数概念。纯函数是函数式编程中的基本概念。这些函数由于没有副作用而具有可预测性和可靠性。这些函数对于相同的输入总是返回相同的输出。
纯函数
纯函数是指对于相同的输入总是返回相同输出的函数。纯函数是函数式编程的基础,因为它们具有可预测性和可测试性。
纯函数是满足以下条件的函数 -
- 它对于相同的输入总是给出相同的结果。
- 它没有副作用。它不会改变任何状态,也不会与外部世界交互(例如,没有 I/O 操作,没有修改全局变量)。
语法
Scala 中纯函数的语法为 -
def functionName(params: Type): ReturnType = {
// function body with no side effects
// 无副作用的函数体
}
纯函数示例
以下示例展示了在 Scala 编程中定义和使用纯函数 -
object Demo {
def add(a: Int, b: Int): Int = {
a + b
}
def main(args: Array[String]): Unit = {
println(add(3, 4))
println(add(3, 4))
}
}
将上述程序保存为 Demo.scala。使用以下命令编译和执行该程序。
命令
> scalac Demo.scala > scala Demo
输出
7 7
在示例中,add 函数是纯函数,因为它对于相同的输入总是产生相同的输出。它没有任何副作用。
纯函数的优势
纯函数有多种优势。纯函数是可预测的,因为它们对于相同的输入总是返回相同的结果。因此,调试和测试更容易。这些函数更容易测试,因为它们不依赖任何外部状态。它不会引起副作用。
纯函数与不可变性
您可以在纯函数中使用不可变数据结构来避免副作用。一旦创建值,不可变数据就不能被修改。
语法
使用纯函数结合不可变数据结构的语法为 -
def functionName(params: ImmutableType): ReturnType = {
// function body working with immutable data
// 使用不可变数据的函数体
}
示例
考虑在 Scala 编程中使用纯函数结合不可变数据结构的示例 -
object Demo {
def addToList(list: List[Int], element: Int): List[Int] = {
list :+ element
}
def main(args: Array[String]): Unit = {
val originalList = List(1, 2, 3)
val newList = addToList(originalList, 4)
println(newList)
println(originalList)
}
}
将上述程序保存为 Demo.scala。使用以下命令编译和执行该程序。
命令
> scalac Demo.scala > scala Demo
输出
List(1, 2, 3, 4) List(1, 2, 3)
在示例中,addToList 函数是纯函数并使用不可变数据工作。因此,原始列表保持不变。但是,addToList 函数返回了一个新列表。
组合纯函数
您可以将纯函数组合起来执行更复杂的操作。这是函数式编程的一个关键优势。
语法
组合纯函数的语法如下 -
def function1(params: Type): ReturnType = {
// function body
}
def function2(params: Type): ReturnType = {
// function body calling function1
}
示例
考虑在 Scala 编程中组合纯函数的示例 -
object Demo {
def square(x: Int): Int = x * x
def sumOfSquares(a: Int, b: Int): Int = {
square(a) + square(b)
}
def main(args: Array[String]): Unit = {
println(sumOfSquares(3, 4))
println(sumOfSquares(5, 6))
}
}
将上述程序保存为 Demo.scala。使用以下命令编译并执行该程序。
命令
> scalac Demo.scala > scala Demo
输出
25 61
在示例中,square 和 sumOfSquares 函数都是纯函数,它们被组合起来执行更复杂的计算。
高阶纯函数
高阶函数要么将其他函数作为参数,要么将函数作为返回值,或者两者兼具。纯高阶函数可以增强代码的模块化和可重用性。
语法
高阶纯函数的语法如下 -
def higherOrderFunction(f: Type => ReturnType, params: Type): ReturnType = {
// function body calling f
}
示例
考虑在 Scala 编程中高阶纯函数的示例 -
object Demo {
def applyOperation(f: Int => Int, x: Int): Int = {
f(x)
}
def increment(x: Int): Int = x + 1
def main(args: Array[String]): Unit = {
println(applyOperation(increment, 5))
println(applyOperation(increment, 10))
}
}
将上述程序保存为 Demo.scala。使用以下命令编译并执行该程序。
命令
> scalac Demo.scala > scala Demo
输出
6 11
在示例中,applyOperation 函数是一个高阶纯函数。它将另一个函数作为参数,并将其应用到一个给定的值上。
纯函数与递归
您可以在递归算法中使用纯函数进行复杂计算,而不会产生副作用。
语法
递归纯函数的语法如下 -
def recursiveFunction(params: Type): ReturnType = {
// base case
if (condition) baseResult
else {
// recursive case
recursiveFunction(newParams)
}
}
示例
考虑在 Scala 编程中递归纯函数的示例 -
object Demo {
def factorial(n: Int): Int = {
if (n <= 1) 1
else n * factorial(n - 1)
}
def main(args: Array[String]): Unit = {
println(factorial(5))
println(factorial(6))
}
}
将上述程序保存为 Demo.scala。使用以下命令编译并执行该程序。
命令
> scalac Demo.scala > scala Demo
输出
120 720
在示例中,factorial 函数是一个纯递归函数,用于计算给定数字的阶乘。
纯函数和 Memoization
Memoization 是一种优化技术。它会缓存昂贵的 function 调用结果,并在再次遇到相同输入时返回缓存结果。Pure functions 是 memoization 的理想候选,因为它们总是为相同的输入产生相同的输出。
语法
memoizing 一个 pure function 的语法如下 -
def memoize[A, B](f: A => B): A => B = {
val cache = scala.collection.mutable.Map[A, B]()
(x: A) => cache.getOrElseUpdate(x, f(x))
}
示例
考虑在 Scala 编程中 memoizing 一个 pure function 的示例 -
object Demo {
def fibonacci(n: Int): Int = {
if (n <= 1) n
else fibonacci(n - 1) + fibonacci(n - 2)
}
def memoize[A, B](f: A => B): A => B = {
val cache = scala.collection.mutable.Map[A, B]()
(x: A) => cache.getOrElseUpdate(x, f(x))
}
def main(args: Array[String]): Unit = {
val memoizedFibonacci = memoize(fibonacci)
println(memoizedFibonacci(10)) // 输出: 55
println(memoizedFibonacci(10)) // 输出: 55 (已缓存)
}
}
将上述程序保存为 Demo.scala。使用以下命令编译和执行该程序。
命令
> scalac Demo.scala > scala Demo
输出
55 55
在示例中,fibonacci function 被 memoized 以优化重复计算。因此它是 pure 的,并产生一致的结果。
Scala 中 Pure 和 Impure Functions 的区别
这是 Scala 中 pure 和 impure functions 的比较 -
| 特性 | Pure Function | Impure Function |
|---|---|---|
| 定义 | 总是为相同的输入返回相同的输出。 | 可能为相同的输入返回不同的输出。 |
| 副作用 | 没有副作用。 | 可能有副作用(例如,修改全局变量、执行 I/O 操作)。 |
| 状态修改 | 不修改任何状态。 | 可以修改状态。 |
| 测试 | 由于可预测性和无依赖关系,更容易测试。 | 由于潜在的副作用和对外部状态或 I/O 操作的依赖,更难测试。 |
| 示例 | def add(a: Int, b: Int): Int = a + b |
def printSum(a: Int, b: Int): Unit = println(a + b) |
Scala 中 pure functions 的示例有:abs、ceil、max 等。Scala 中 impure functions 的示例有:isEmpty、length、substring 等。
Pure Function 总结
- Scala 中的 pure functions 总是为相同的输入产生相同的输出。pure functions 没有副作用。
- 它们提供了可预测性、引用透明性、易于测试等特性。
- Pure functions 与 immutable data structures 配合良好,可以组合创建复杂操作。
- 高阶 pure functions 提供了代码模块化和可重用性。
- 你可以在 recursive algorithms 中使用 pure functions。这些非常适合 memoization 以优化性能。