haskell - Haskell 的惰性是如何发挥作用的?

标签 haskell

考虑这个将列表中所有元素加倍的函数:

doubleMe [] = []
doubleMe (x:xs) = (2*x):(doubleMe xs)

然后考虑表达式

doubleMe (doubleMe [a,b,c])

很明显,在运行时,它首先扩展到:

doubleMe ( (2*a):(doubleMe [b,c]) )

(这很明显,因为据我所知不存在其他可能性)。

但我的问题是:到底为什么现在扩展到

2*(2*a) : doubleMe( doubleMe [b,c] )

而不是

doubleMe( (2*a):( (2*b) : doubleMe [c] ) )

直觉上,我知道答案:因为 Haskell 很懒。但有人能给我更准确的答案吗?

列表是否有什么特殊之处导致了这种情况,或者这个想法比列表更普遍?

最佳答案

doubleMe (doubleMe [a,b,c]) 不会扩展为 doubleMe ( (2*a):(doubleMe [b,c]) )。它扩展为:

case doubleMe [a,b,c] of
  [] -> []
  (x:xs) -> (2*x):(doubleMe xs)

即先展开外层函数调用。这是惰性语言和严格语言之间的主要区别:扩展函数调用时,您不会首先评估参数 - 而是用函数调用体替换函数调用,并暂时保留参数不变。

现在需要扩展doubleMe,因为模式匹配需要知道其操作数的结构才能对其求值,因此我们得到:

case (2*a):(doubleMe [b,c]) of
  [] -> []
  (x:xs) -> (2*x):(doubleMe xs)

现在模式匹配可以替换为第二个分支的主体,因为我们现在知道第二个分支是匹配的分支。因此,我们用 (2*a) 代替 x,用 doubleMe [b, c] 代替 xs,得到:

(2*(2*a)):(doubleMe (doubleMe [b,c]))

这就是我们得出结果的方式。

关于haskell - Haskell 的惰性是如何发挥作用的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28302972/

相关文章:

haskell - 如果多个 monad 为 "mixed",是否可以利用 Monadic 结构?

list - 按第一个元素对元组列表进行分组

sql - 通过键列表获取多个持久条目

Haskell 简单合并

list - 将 putStr 应用于列表的每个项目

haskell - 模式匹配数据类型及其在 Haskell 中的嵌套名称

debugging - 有关一般调试技术的推荐阅读

performance - 启用某些规​​则时,UU-Parsinglib 会急剧变慢

haskell - Random Monad 在replicatM 迭代之间是独立的吗?

haskell - Traversables 的自然法则是什么意思?