无限列表中的懒惰评估

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无限列表中的懒惰评估相关的知识,希望对你有一定的参考价值。

嗨,我有以下代码:

let f n (xs) = if n < 0 then f (n-1) (n:xs) else xs
f (-3) [] !! 1

我希望它打印-4

但它不会打印任何内容并在后台继续计算。

我的代码出了什么问题?

答案

让我们逐步完成评估:

f (-3) []
f (-4) [-3]
f (-5) [-4, -3]
f (-6) [-5, -4, -3]
f (-7) [-6, -5, -4, -3]
...

考虑到这一点,你期望f (-3) [] !! 1是什么?索引1中的值会改变每次迭代,因此Haskell无法知道它是什么,直到它到达n >= 0的非递归情况,这种情况从未发生过。

如果您在另一个方向上构建列表,它将按预期工作:

let f n = if n < 0 then n : f (n - 1) else []

> f (-3) !! 1
-4
另一答案

所以这是假装整数类型:

data Int2 = ... -- 2 bit signed integers [-2, -1, 0, 1]
  deriving (Num, Ord, Eq, ...)

让我们假设您的函数是在Int2值上定义的:

f :: Int2 -> [Int2] -> [Int2]
f n (xs) = if n < 0 then f (n-1) (n:xs) else xs

这样可以很容易地计算出f n xs的评估步骤:

f 1 xs = xs
f 0 xs = xs
f (-1) xs = f (-2) (-1 : xs)
f (-2) xs = f 1 (-2 : xs) -- because finite signed arithmetic wraps around

从那里我们可以计算出f n []的全部价值:

f 1 [] = []
f 0 [] = []
f (-1) [] = f (-2) [-1] = f 1 [-2, -1] = [-2, -1]
f (-2) [] = f 1 [-2] = [-2]

每个都计算了一个值,但请注意在我们从f (-1) []获取列表之前需要3个评估步骤。

现在看看你是否可以计算出如果在4位数字上定义f (-1) []需要多少步骤。 8位? 32位? 64位?如果它使用没有下限的Integer怎么办?

懒惰在任何时候都不会帮助你,因为没有部分结果,只有递归调用。这是区别之间的区别:

lazyReplicate 0 _ = []
lazyReplicate n x = x : lazyReplicate (n - 1) x

strictReplicate n x = helper [] n x where
  helper xs 0 _ = xs
  helper xs n x = helper (x : xs) n x

以上是关于无限列表中的懒惰评估的主要内容,如果未能解决你的问题,请参考以下文章

Haskell 不会懒惰地评估交错

seq如何评估Haskell中的无限列表?

过滤功能不懒惰

itertools.product 是不是懒惰地评估它的论点?

Clojure:减少大型懒惰收集会占用内存

Python 会懒惰地评估 if 的条件吗? [复制]