Jea*_*ouX 28 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 = ...
Run Code Online (Sandbox Code Playgroud)
当我打电话时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
Run Code Online (Sandbox Code Playgroud)
Haskell的行为是什么?
anotherFunction吗?anotherFunction吗?Sib*_*ibi 30
在这两种情况下,除非要求该值,否则它不会评估任何内容.要求该值的一种方法是调用ghci中的函数(它打印值ghci并因此要求它).假设您正在执行该函数,那么在第二种情况下,它会将元组评估为弱头正规形式(WHNF),然后计算第一个元素,(a,b,c,d)因为只需要该值.其他元素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
anotherFunction x = (x, undefined, undefined, undefined)
Run Code Online (Sandbox Code Playgroud)
演示ghci:
?> myFunction 1
1
Run Code Online (Sandbox Code Playgroud)
Wil*_*sem 14
它只对它感兴趣a,所以这意味着有一个隐含的函数:
thea :: (a,b,c,d) -> a
thea (a,_,_,_) = a
Run Code Online (Sandbox Code Playgroud)
换句话说,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
Run Code Online (Sandbox Code Playgroud)
在这种情况下 - 为了评估第一个元素 - 还将评估第三个和第四个元素.但由于你不对它们做任何事情,Haskell对它们的结果不会特别感兴趣.
chi*_*chi 12
正如其他人已经指出的那样,只会a进行评估.
但请记住,为了利用懒惰,anotherFunction在评估其组件之前返回元组至关重要.例如,考虑一下
anotherFunction n = if p > 1000 then (n, p) else (n, 0)
where p = product [1..n]
Run Code Online (Sandbox Code Playgroud)
product [1..n]即使调用者只需要第一对组件(即n),上面也将始终进行评估.这是因为在if返回对之前需要进行评估,这就是强制要求p.相比之下,
anotherFunction n = (n, if p > 1000 then p else 0)
where p = product [1..n]
Run Code Online (Sandbox Code Playgroud)
将立即返回该对.如果仅评估其第一个组件,则p根本不会计算.
除非需要获取该变量的值,否则不会对其进行求值.基本上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|
Run Code Online (Sandbox Code Playgroud)
在这里,1 `div` 0会引发divide by zero错误.如果它评估所有元素,那么即使你调用myFunction它1,你也会得到那个错误,但是
Prelude> myFunction 1
100
Run Code Online (Sandbox Code Playgroud)
只有当您使用任何其他值调用它时,才需要评估元组的第二个值,并且它将失败并显示divide by zero错误.
Prelude> myFunction 2
*** Exception: divide by zero
Run Code Online (Sandbox Code Playgroud)