1️⃣ 有副作用的函数示例
假设我们要实现一个累加器,每次调用函数都会累加值并打印:
var total = 0
func addAndPrint(_ x: Int) -> Int {
total += x // 修改外部变量 → 副作用
print("Total:", total) // 打印 → 副作用
return total
}
addAndPrint(5) // Total: 5
addAndPrint(3) // Total: 8
分析:
- 副作用 1:修改了外部变量
total - 副作用 2:打印输出
- 非纯函数原因:同样输入
x = 3,每次返回值可能不同(依赖total的当前值)
2️⃣ 纯函数替代实现
纯函数的思路:不依赖外部状态、不修改外部变量、输出只依赖输入。
func add(_ x: Int, to total: Int) -> Int {
return total + x
}
// 使用:
let total1 = add(5, to: 0) // 5
let total2 = add(3, to: total1) // 8
print("Total1:", total1) // Total1: 5
print("Total2:", total2) // Total2: 8
特点:
- 输入:
x和total - 输出:
total + x - 不修改外部变量
- 同样输入总是得到同样输出
3️⃣ 优势对比
| 特性 | 有副作用函数 | 纯函数 |
|---|---|---|
| 输出是否确定 | 不确定(依赖外部状态) | 确定(只依赖输入) |
| 是否修改外部状态 | 是 | 否 |
| 并发安全 | ❌ 需要加锁 | ✅ 天然线程安全 |
| 可组合性 | 差,容易引发 bug | 好,可与其他函数组合 |
4️⃣ 使用场景示例
如果我们要累加数组元素:
有副作用写法:
var total = 0
[1,2,3].forEach { total += $0 }
print(total) // 6
纯函数写法:
let total = [1,2,3].reduce(0, +)
print(total) // 6
reduce+ 闭包是纯函数- 没有修改外部状态
- 可安全并发、可预测
💡 总结:
副作用 = 修改外部状态或产生不可控影响
纯函数替代 = 所有状态通过输入参数传入,输出仅依赖输入