使用 seq 实现无限列表以提高 Haskell 中的时间复杂度

问题描述

我不太明白 seq 究竟是如何工作的,以及如何使用 seq 实现一个功能。 所以作为一个练习,如果我想实现一个函数生成一个以数字 a 开头的列表。

例如,我正在使用以下功能

countup n = n : countup (n+1)

并尝试将 [1,2,3,4 ...] (从 1 开始,只需增加 1 并添加到列表中)作为一个无限的惰性列表。 我该怎么做?

更新:

我正在尝试使 (take k (countup 0)) 使用 O(1) 空间。 这里,take函数如下:

take k (x:xl) = 
    if k==0
    then x
    else take (k-1) xl

解决方法

countup n = n : countup (n+1)

列表的每个元素都被创建为一个 thunk previousElement + 1。因此,如果您 take 第 1000000 个或任何元素,那将是一个非常大的 thunk (...(n + 1)... + 1),其中包含约 1000000 个暂停。尽管 : 单元可以在创建后立即被 GC 处理(因此遍历列表脊本身需要 O(1) 空间),但元素本身复制了列表的结构,因此 {{ 1}} 仍然占用 take k (countup n) 空间。

如果评估 O(k) 单元格也会评估元素,我们会很高兴。我们可以用

:

现在,在评估 countup n = seq n $ n : countup (n + 1) 时,seq n $ n : countup (n + 1) 将导致 seqn 被评估。评估后者什么都不做(它已经被评估了),评估前者会执行任何 thunked 加法,以便 n : countup (n + 1) 永远不会建立。根据这个定义,+ 1 占用 take k (countup n) 空间(或者,实际上是 O(1))。

我们也可以将改进后的函数写成

O(log(n + k))