我在Lisp(Scheme)中开发了一个纯函数队列,如下所示:
;Internal functions
(define (delay-cons x s)
(cons x (lambda () s)))
(define (delay-car s)
(car s))
(define (delay-cdr s)
((cdr s)))
(define (delay-append s t)
(if (null? s)
t
(delay-cons (delay-car s) (delay-append (delay-cdr s) t))))
;API
(define (enqueue x q) (delay-append q (delay-cons x empty)))
(define dequeue delay-cdr)
(define peek delay-car)
(define empty '())
(define empty? null?)
delay-cons类似于cons,但它通过将尾部包装在闭包中来暂停对尾部的评估。类似的延迟 - 附加(delay-append s t)通过尾部的递归暂停将t附加到s。
因此,每个enqueue包含一层闭包,使其成为O(1),每个peek只检索一个使其成为O(1)的值,并且每个dequeue检索并评估一个闭包使其成为O(1)。
我在其他地方看不到这个;例如,在Okasaki的Purely Functional Data Structures中,最简单的队列是Banker的队列,它比这复杂得多,并且只有摊销O(1)排队,偷看和出队。这使我怀疑我的推理中存在错误。
这个数据结构是否合理?在某处有参考吗?
编辑: 延迟 - 缺点是在延迟附加中使用的错误;我尝试使用像宏一样的功能(感谢Will Ness)。
我尝试使用
更正它(define (delay-append s t)
(if (null? s)
t
(cons (delay-car s) (lambda () (delay-append (delay-cdr s) t)))))
但这不适用于API。
答案 0 :(得分:5)
首先,delay-cons
可以不是一个功能。它必须是一个宏。 For instance,
(define-syntax s-cons
(syntax-rules ()
((s-cons h t) (cons h (lambda () t)))))
在麻省理工学院计划中工作。
但是你可以使用delay-cons
中的delay-append
而不是来解决这个问题:
(define (delay-append s t)
(if (null? s)
t
(cons (delay-car s) (lambda () (delay-append (delay-cdr s) t)))))
所以没关系。
至于复杂性,delay-append
并非没有成本。它包裹着原始队列。想象一下,它有30个元素;然后你又一个追加10个。现在,原始文件被包裹在10层delay-append
中,必须对其进行导航以获取这30个元素中的每一个(实际上29个,因为头部被拉出到直接car
,{{{ 1}})。因此对于delay-append
- 附加的,n
- 访问的使用模式,它看起来像二次复杂性。
Haskell上下文中关于此问题的经典论文是“Why are difference lists more efficient than regular concatenation?”。您的n
类似于“常规连接”:
delay-append
以下是插图:
[] ++ t = t
s ++ t = (head s) : ((tail s) ++ t)