我需要对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的行为是什么?
anotherFunction
吗?anotherFunction
的结果吗?答案 0 :(得分:30)
在这两种情况下,除非要求价值,否则它不会评估任何事情。要求该值的一种方法是调用ghci中的函数(它在ghci
中打印值,因此要求它)。假设您正在执行该函数,那么在第二种情况下,它会将元组评估为weak head normal form (WHNF),然后评估(a,b,c,d)
中的第一个元素,因为只需要该值。其他元素b
,c
和d
将采用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