纯函数是函数式编程Functional Programming的核心概念,它保证了可预测性、可复现性、可组合性等特性。
特性 | 描述 |
---|---|
确定性(Deterministic) | 相同输入始终返回相同输出 |
无副作用(No Side Effects) | 不能修改全局变量、文件、数据库等外部状态 |
引用透明性(Referential Transparency) | 可以用计算结果替换函数调用,不影响程序逻辑 |
不依赖外部状态(No External State Dependency) | 只能依赖函数参数,不能依赖外部变量 |
不可变性(Immutability) | 不能修改输入参数 |
可缓存性(Cacheability) | 由于相同输入必定产生相同输出,计算结果可以被缓存 |
易于并行化(Parallelism-Friendly) | 由于无共享状态,可以在多线程环境中安全执行 |
可组合性(Composability) | 纯函数可以组合成更复杂的函数,增强代码复用性 |
易于测试(Testability) | 由于不依赖外部状态,测试更简单 |
1. 确定性(Deterministic)
定义:纯函数的输出完全由输入决定,只要输入相同,输出一定相同。
示例(纯函数 ✅)
def add(x, y):
return x + y
print(add(2, 3)) # 始终返回 5
add(2,3)
无论何时调用,都会返回 5,这是纯函数。
示例(非纯函数 ❌)
import random
def add_random(x):
return x + random.randint(1, 10)
print(add_random(2)) # 每次运行都可能返回不同的结果
random.randint(1, 10)
使得add_random(2)
的返回值不确定,因此不是纯函数。
2. 无副作用(No Side Effects)
定义:纯函数不能修改外部状态,如全局变量、文件、数据库等。
示例(纯函数 ✅)
def square(x):
return x * x
- 这个函数不会修改外部变量、不会打印日志、不会写入文件,因此是纯函数。
示例(非纯函数 ❌)
total = 0
def add_to_total(x):
global total
total += x # 修改了全局变量
return total
- 这个函数修改了全局变量
total
,所以它不是纯函数。
3. 参考透明性(Referential Transparency)
定义:如果一个函数调用可以用它的计算结果替换,而不影响程序行为,那么它是引用透明的。
示例(纯函数 ✅)
def multiply(x, y):
return x * y
z = multiply(3, 4) # 可以用 `12` 直接替换
multiply(3, 4)
可以直接替换成12
,不会影响程序逻辑,因此是纯函数。
示例(非纯函数 ❌)
import time
def get_time():
return time.time()
t = get_time() # 不能直接替换成某个固定值,因为每次都会变
get_time()
返回的时间每次都不同,因此不是引用透明的。
4. 不依赖外部状态(No External State Dependency)
定义:纯函数的结果不能依赖函数外部的变量状态。
示例(纯函数 ✅)
def add(x, y):
return x + y
- 该函数仅依赖输入参数
x
和y
,所以是纯函数。
示例(非纯函数 ❌)
factor = 2
def multiply(x):
return x * factor # 依赖外部变量 `factor`
- 这个函数依赖外部变量
factor
,如果factor
变化,函数的结果也会变,因此不是纯函数。
5. 不可变性(Immutability)
定义:纯函数不能修改输入参数。
示例(纯函数 ✅)
def add_to_list(lst, x):
return lst + [x] # 创建新列表,不修改原列表
- 这个函数不会修改原始
lst
,而是返回一个新列表,因此是纯函数。
示例(非纯函数 ❌)
def add_to_list_in_place(lst, x):
lst.append(x) # 直接修改了输入参数 `lst`
return lst
lst.append(x)
修改了原始列表lst
,因此不是纯函数。
6. 可缓存性(Cacheability)
定义:由于纯函数相同输入总是得到相同输出,所以可以缓存其结果,提高性能。
示例
from functools import lru_cache
@lru_cache(maxsize=100)
def fib(n):
if n < 2:
return n
return fib(n-1) + fib(n-2)
- 由于
fib(n)
每次相同输入都会得到相同输出,可以用@lru_cache
进行缓存,提高计算效率。
7. 易于并行化(Parallelism-Friendly)
定义:纯函数不共享状态,因此可以并行执行,而不会发生数据竞争。
示例
from multiprocessing import Pool
def square(x):
return x * x # 纯函数
with Pool(4) as p:
print(p.map(square, [1, 2, 3, 4, 5]))
square(x)
不会修改任何共享状态,所以可以安全地在多个进程中执行,提高计算效率。
8. 可组合性(Composability)
定义:多个纯函数可以组合成更复杂的函数,增强代码复用性。
示例
def add(x, y):
return x + y
def square(x):
return x * x
result = square(add(2, 3)) # (2 + 3)² = 25
add(2, 3)
的输出可以直接作为square()
的输入,组合性极强。
9. 易于测试(Testability)
定义:纯函数仅依赖输入参数,不依赖外部状态,测试时无需考虑外部环境。
示例
def multiply(x, y):
return x * y
# 纯函数可以直接测试
assert multiply(2, 3) == 6
assert multiply(5, 0) == 0
- 由于
multiply(x, y)
不依赖全局变量、文件、数据库,测试时只需检查输入输出关系,测试更简单。
总结
纯函数是确定性、无副作用、可复现、易测试的核心计算单位,广泛应用于函数式编程(如 Haskell)、数据处理(如 Spark)、并行计算(如 MapReduce),也是 Nix 等技术实现可复现性的关键概念。