Haskell在多大程度上懒惰?

时间:2015-03-31 10:01:53

标签: haskell lazy-evaluation

我需要对Haskell的懒惰做一些澄清。

如果我有这个功能:

myFunction arg
  | arg == 1 = a
  | arg == 2 = a*b
  | arg == 3 = b+c
  | otherwise = (a+b)*c
     where
            a = ...
            b = ...
            c = ...
            d = ...

当我致电myFunction 1时,Haskell将只评估 a = ... 功能, b c ,也不是 d

但如果我写

myFunction arg
  | arg == 1 = a
  | arg == 2 = a*b
  | arg == 3 = b+c
  | otherwise = (a+b)*c
     where
            (a,b,c,d) = anotherFunction arg

Haskell的行为是什么?

  • 它只会评估 a 并将“懒惰”传播到anotherFunction吗?
  • 或者,它会评估整个元组(a,b,c,d)作为anotherFunction的结果吗?

4 个答案:

答案 0 :(得分:30)

在这两种情况下,除非要求价值,否则它不会评估任何事情。要求该值的一种方法是调用ghci中的函数(它在ghci中打印值,因此要求它)。假设您正在执行该函数,那么在第二种情况下,它会将元组评估为weak head normal form (WHNF),然后评估(a,b,c,d)中的第一个元素,因为只需要该值。其他元素bcd将采用thunk形式。事实上,您可以自己验证:

myFunction arg
  | arg == 1 = a
  | arg == 2 = a*b
  | arg == 3 = b+c
  | otherwise = (a+b)*c
  where
    (a,b,c,d) = anotherFunction arg

anotherFunction x = (x, undefined, undefined, undefined)

ghci中的演示:

λ> myFunction 1
1

答案 1 :(得分:14)

它只对a感兴趣,所以这意味着有一个隐式函数

thea :: (a,b,c,d) -> a
thea (a,_,_,_) = a

换句话说,Haskell 对元组的其他元素不感兴趣。然而,有时元组的元素共享一些结构。假设另一个函数定义为:

anotherFunction :: Int -> (Int,Int,Int,Int)
anotherFunction x = (z,t,f,g)
    where f = x*x
          g = f+x
          z = g-2
          t = 3

在这种情况下 - 为了评估第一个元素 - 还将评估第三个和第四个元素。但由于你不对它们做任何事情,Haskell特别不会对他们的结果感兴趣。

答案 2 :(得分:12)

正如其他人已经指出的那样,只会评估a

请记住,为了利用懒惰,anotherFunction在评估其组件之前返回元组是至关重要的。例如,考虑

anotherFunction n = if p > 1000 then (n, p) else (n, 0)
  where p = product [1..n]

以上将始终评估product [1..n],即使调用者只需要第一对组件(n)。这是因为在返回对之前需要评估if,这会强制p。相比之下,

anotherFunction n = (n, if p > 1000 then p else 0)
  where p = product [1..n]

将立即返回该对。如果仅评估其第一个组件,则根本不会计算p

答案 3 :(得分:7)

除非需要获取该变量的值,否则不会对其进行评估。基本上Haskell是如此懒惰,除非被告知不是这样。

你可以像这样确认这个

Prelude> :set +m
Prelude> let anotherFunction = (100, 1 `div` 0)
Prelude| 
Prelude> let myFunction arg
Prelude|                | arg == 1  = a
Prelude|                | otherwise = b
Prelude|                where
Prelude|                     (a, b) = anotherFunction
Prelude| 

此处,1 `div` 0会引发divide by zero错误。如果它评估所有元素,那么即使你用myFunction调用1,你也会遇到这个错误,但是

Prelude> myFunction 1
100

只有在使用任何其他值调用它时,才需要计算元组的第二个值,并且它将因divide by zero错误而失败。

Prelude> myFunction 2
*** Exception: divide by zero