在C中,您可以指向单链表的第一个和最后一个元素,提供对列表末尾的恒定时间访问。因此,可以在恒定时间内将一个列表附加到另一个列表。
据我所知,scheme默认情况下不提供此功能(即不断访问列表末尾)。要清楚,我不是在寻找“指针”功能。我明白这在计划中是非惯用的,并且(我认为)是不必要的。
有人可以1)证明能够提供一种在常规时间附加两个列表的方法,或者2)向我保证默认情况下这在方案或球拍中已经可用(例如,告诉我append
是实际上是一个不变的操作,如果我错了,不这么想)?
编辑:
我应该让自己更清楚。我正在尝试创建一个可检查的队列。我希望有一个列表,我可以1)在恒定时间内推到前面,2)在恒定时间内弹出背面,3)使用Racket的foldr
或类似的东西(Lisp右折叠)迭代
答案 0 :(得分:7)
标准Lisp列表无法在常量时间内附加。
但是,如果您创建自己的列表类型,则可以执行此操作。基本上,你可以使用记录类型(或只是一个cons单元)---让我们称之为“标题”---保存指向列表头部和尾部的指针,并在每次有人添加到列表时更新它
但是,请注意,如果您这样做,列表不再具有结构感。即,较长的列表不仅仅是较短列表的扩展,因为涉及额外的“标题”。因此,你失去了很多Lisp算法的简单性,它涉及在每次迭代时递归到列表的cdr
。
换句话说,缺乏简单的追加是一种权衡,可以更容易地编写递归算法。大多数函数式程序员都会认为这是正确的权衡,因为在纯函数意义上附加意味着你必须复制除最后一个列表以外的所有单元格 - 所以它不再是O(1),无论如何。
ETA反映OP的编辑
您可以创建一个队列,但行为相反:您向后面添加元素,并检索前面的元素。如果您愿意使用它,那么这样的数据结构是easy to implement in Scheme。 (是的,很容易在恒定的时间内附加两个这样的队列。)
Racket也有类似queue data structure,但它使用记录类型而不是cons单元格,因为Racket cons单元格是不可变的。您可以使用queue->list
(复杂度为O(n))将队列转换为列表,以便在需要折叠时使用。
答案 1 :(得分:5)
您想要一个FIFO队列。 user448810提到了纯功能FIFO队列的标准实现。
您对失去“Lisp列表的关键优势”的担忧需要稍微解压缩一下:
答案 2 :(得分:2)
听起来你正在寻找一个双端队列,而不是一个列表。 deque的标准习惯用法是保留两个列表,列表的前半部分按正常顺序排列,后半部分按相反的顺序排列,从而可以访问双端队列的两端。如果要访问的列表的一半是空的,则反转另一半并交换两半的含义。查看here以获得更全面的解释和示例代码。