Haskell 函数式编程最核心的两个理念是:
- Purity
- Laziness
Weak Head Normal Form
Haskell evaluate 的方式是 outside-in,先代入函数定义,再逐步替换参数.更具体地说,Haskell 尝试对当前表达式进行 pattern matching,一旦可以匹配上,就进行替换.只有以下可以被 pattern match(也被称为 weak head normal form WHNF)
- 常数
- 最外层有一个构造函数的表达式
- 函数
Sharing
Haskell 对表达式会构建计算图 DAG,如果函数体中需要反复用到某一个表达式的结果,那么 Haskell 只会重复计算这个表达式一次,并用有向边表示“其他地方使用计算结果”
要想参数共享计算结果,他们必须是 same named,如:
- function parameter 中定义
let in里定义where里定义
例如考虑以下例子
1 | f :: Int -> Int |
而对于这个例子,则没有 sharing
1 | g :: Int -> Int -> Int |
Strictness
先回顾一下 foldl 和 foldr
1 | foldl :: (a -> b -> a) -> a -> [b] -> a |
从定义可以看出,foldr 更适合 laziness/short-circuiting operations,而 foldl 却需要遍历整个列表才能产出一个 WHNF 值.
foldl'
那么为什么我们还需要 foldl 呢?我们考察其变种 foldl',它强制其第二个参数 f z x 先完成计算再继续.我们发现 foldl' 由于所有计算都是直接完成的,没有 space leak.
foldl'在Data.ListModule 里
要想实现泛型版本的 foldl',我们需要一个内置函数 seq,seq a b 返回 b 的值,但是会先强制将 a 转为 WHNF 形式.