【问题标题】:lazy evaluation in infinte list无限列表中的惰性求值
【发布时间】:2019-01-22 23:51:36
【问题描述】:

您好,我有以下代码:

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

我希望它打印 -4

但它不打印任何东西并在后台保持计算。

我的代码有什么问题?

【问题讨论】:

  • 我不确定你在期待什么,但是如果你按照它的评估方式进行操作,递归只会在n &gt;= 0 时结束,但你每次都会从中减去 1。因此,无限递归。 [当你这样做时,你正在xs 中建立一个无限列表,但评估永远不会停止让 GHC “看到”这个。所以这不同于像[1..] 这样的“实际”无限列表,您可以非常高兴地对某些元素进行操作。]
  • 普通函数应用立即发生;递归调用的评估不会延迟到稍后的时间。

标签: list haskell infinite


【解决方案1】:

让我们逐步完成评估:

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

考虑到这一点,您希望f (-3) [] !! 1 是什么?索引1 中的值在每次迭代中都会发生变化,因此在到达n &gt;= 0 处的非递归情况之前,Haskell 无法知道它是什么,这永远不会发生。

如果你在另一个方向构建列表,它会像你期望的那样工作:

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

> f (-3) !! 1
-4

【讨论】:

    【解决方案2】:

    这是一个假装的整数类型:

    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 个评估步骤。

    现在看看你能不能算出计算f (-1) [] 需要多少步,如果它是在4 位数字上定义的。 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
    

    【讨论】:

      猜你喜欢
      • 2016-06-27
      • 2021-05-12
      • 2021-12-25
      • 1970-01-01
      • 2021-04-26
      • 2012-02-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多