Haskell在多大程度上懒惰?

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吗?
  • 或者,它会评估整个元组(a,b,c,d)的结果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)

  • @JeanJouX undefineds非常有效地测试语言中的非严格性. (7认同)
  • @thefourtheye当某个输入未定义时,某些东西有效,这意味着该程序从未尝试过对它进行评估. (3认同)

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根本不会计算.


the*_*eye 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| 
Run Code Online (Sandbox Code Playgroud)

在这里,1 `div` 0会引发divide by zero错误.如果它评估所有元素,那么即使你调用myFunction1,你也会得到那个错误,但是

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)