懒惰评估和严格评估Haskell

时间:2014-01-19 14:50:41

标签: haskell lazy-evaluation evaluation strict

我理解懒惰评估是什么,它是如何工作的以及它具有的优势,但是你能解释一下Haskell的严格评估是什么吗?我似乎无法找到很多关于它的信息,因为懒惰的评估是最为人所知的。

每个人都有什么好处。什么时候实际使用了严格的评估?

1 个答案:

答案 0 :(得分:10)

严格在Haskell中以某种方式发生,

首先是一个定义。当且仅当其参数a未终止时,函数才是严格的,f a也不是。 Nonstrict(有时称为懒惰)恰恰相反。

你可以使用模式匹配

严格论证
-- strict
foo True   = 1
foo False  = 1

-- vs
foo _ = 1

由于我们不需要评估参数,我们可以传递类似foo (let x = x in x)的内容,但它仍然只返回1。但是,对于第一个函数,函数需要查看输入的值,以便它可以运行适当的分支,因此它是严格的。

如果由于某种原因我们无法模式匹配,那么我们可以使用名为seq :: a -> b -> b的魔术函数。 seq基本上规定,只要对其进行评估,它就会a评估所谓的weak head normal form

你可能想知道它为什么值得。让我们考虑一个案例研究,foldl vs foldl'foldl在它的累加器中很懒,所以它实现了像

这样的东西
 foldl :: (a -> b -> a) -> a -> [b] -> a
 foldl f accum []     = acuum
 foldl f accum (x:xs) = foldl (f accum x) xs

请注意,由于我们在accum中从不严格,因此我们会构建一系列大量的thunk,f (f (f (f (f (f ... accum)))..)))

这不是一个幸福的前景,因为这会导致记忆问题,实际上

*> foldl (+) 0 [1..500000000]
     *** error: stack overflow

现在更好的是,如果我们使用seq

强制执行每一步的评估
 foldl' :: (a -> b -> a) -> a -> [b] -> a
 foldl' f accum []     = accum
 foldl' f accum (x:xs) = let accum' = f accum x
                         in accum' `seq` foldl' f accum' xs

现在我们强制在每一步评估accum,使其更快。这将使foldl'在恒定空间中运行,而不是像foldl那样的堆栈溢出。

现在seq仅将其值评估为弱头正常形式,有时我们希望将它们完全评估为正常形式。为此,我们可以使用库/类型

 import Control.DeepSeq -- a library on hackage

  deepseq :: NFData a => a -> b -> a

这会强制a进行全面评估,

 *> [1, 2, error "Explode"] `seq` 1
    1
 *> [1, 2, error "Explode"] `deepseq` 1
    error: Explode
 *> undefined `seq` 1
  error: undefined
 *> undefined `deepseq` 1
  error undefined

所以这充分评估了它的论点。这对于并行编程非常有用,例如,你想要在一个核心发送回主线程之前对其进行全面评估,否则你只需创建一个thunk,所有实际的计算仍然是顺序的。