为什么这样评估?

时间:2014-04-11 11:11:55

标签: haskell

我有功能

doubleMe :: Num a => [a] -> [a]
doubleMe [] = []
doubleMe (x:xs) = (2*x):(doubleMe xs)

考虑一下:

doubleMe(doubleMe([1,2,3]))

第一步显然是

doubleMe(doubleMe([1,2,3])) = doubleMe(2:doubleMe[2,3])

因为没有其他可能性。

这是我想知道的下一步。为什么

doubleMe(2:doubleMe[2,3]) = 4:(doubleMe(doubleMe([2,3])))

而不是

doubleMe(2:doubleMe[2,3]) = doubleMe(2:4:doubleMe([3]))

到目前为止我唯一能够提出的答案是

  

因为它有意义。否则哈斯克尔就不会在名单上懒散地行动。

但这不是一个答案,而是一个拼写。什么是真正的答案?

2 个答案:

答案 0 :(得分:7)

  

第一步显然是doubleMe(doubleMe([1,2,3])) = doubleMe(2:doubleMe[2,3])

实际上不是显然!

为了区分,

doubleMe' = doubleMe

并考虑

doubleMe' $ doubleMe [1,2,3]

第一步如你所说的唯一原因,即

≡ doubleMe' $ 2 : doubleMe [2,3]

是因为doubleMe'需要能够将其参数与[]_:_相匹配。为此,运行时开始评估doubleMe [1,2,3]一点,即一个递归步骤。这会产生2 : doubleMe [2,3],足以让doubleMe'使用:

≡ 4 : doubleMe' (doubleMe [2,3])

请注意,实际上这个评估顺序不是必需语言:编译器可以重新排序(例如出于性能原因),所以实际上内部列表会立即完全处理 - < strong> if 它可以证明这不会改变语义,即对于无限列表,doubleMe [1..]如果只询问前几个结果,则不能卡在永恒循环中。

GHC不进行此类重新排序。

答案 1 :(得分:3)

要认识到的重要一点是,case只有case导致Haskell [1]中的评估。因此,当你说&#34;考虑一下:&#34;

doubleMe(doubleMe([1,2,3]))

说出我们正在考虑的是至关重要的。在Haskell [1]中,评估不会发生功能应用的副作用。导致(部分)评估它的唯一因素是case语句,模式匹配它。那么case如何在这里工作?

case doubleMe (doubleMe [1,2,3]) of
    []     -> ...
    x : xs -> ... x ... xs ...

如下进行。我们必须在函数调用的返回值上进行模式匹配,所以我们用它的body替换函数调用(desugaring函数参数模式匹配到case

case (case doubleMe [1,2,3] of
          []   -> []
          x:xs -> (2*x) : doubleMe xs)
     ) of
    []     -> ...
    x : xs -> ... x ... xs ...

我们已经引入了第二个案例,因此会导致评估

case (case (case [1,2,3] of
                 []   -> []
                 x:xs -> (2*x) : doubleMe xs
           ) of
          []   -> []
          x:xs -> (2*x) : doubleMe xs)
     ) of
    []     -> ...
    x : xs -> ... x ... xs ...

现在我们有第三个case。这个是直接匹配数据结构,因此它立即返回并选择要遵循的适当分支(:)绑定x1xs到{{ 1}}。

[2,3]

现在已对第二个case (case ((2*1) : doubleMe [2,3] ) of [] -> [] x:xs -> (2*x) : doubleMe xs) ) of [] -> ... x : xs -> ... x ... xs ... 的详细信息进行了评估,以便它可以继续选择适当的分支(同样是case)绑定:x和{{ 1}}到(2*1)

xs

最后原始doubleMe [2,3]可以选择其分支

case ((2*(2*1)) : doubleMe (doubleMe [2,3]))
     ) of
    []     -> ...
    x : xs -> ... x ... xs ...

下一个问题是如何评估case... (2*(2*1)) ... doubleMe (doubleMe [2,3])) ... 字词。答案是,只有在更高级别(2*(2*1))审查他们所属的表达方式时才能强迫他们使用。


[1]或者更确切地说是Haskell的 implementation 。原则上Haskell可以以不同的方式实现,但我所知道的所有版本都是这样的。