背景
我在业余时间研究 ML 编程的 Ullmans Elements 。最终目标是自学ML 中的 Andrew Appels 现代编译器实现。
在 Elements of ML 中,Ullman 描述了差异列表:
LISP 程序员有一个技巧称为差异列表,其中通过保留作为函数的额外参数的列表来更有效地操作列表,该列表以某种方式表示您已经完成的工作。这个想法出现在许多不同的应用中;
Ullman 使用reverse
差异列表技术作为示例。这是一个在 O(n^2) 中运行的慢速函数。
fun reverse nil = nil
| reverse (x::xs) = reverse(xs) @ [x]
使用差异列表的速度更快
fun rev1(nil, M) = M
| rev1(x::xs, ys) = rev1(xs, x::ys)
fun reverse L = rev1(L, nil)
我的问题
我有这种二叉搜索树 (BST) 数据类型。
datatype 'a btree = Empty
| Node of 'a * 'a btree * 'a btree
一个以预购方式收集元素列表的简单解决方案是
fun preOrder Empty = nil
| preOrder (Node(x, left, right)) = [x] @ preOrder left @ preOrder right
但 Ullman 指出 @ 运算符很慢,并在练习 6.3.5 中建议我preOrder
使用差异列表来实现。
经过一番挠头后,我想出了这个功能:
fun preOrder tree = let
fun pre (Empty, L) = L
| pre (Node(x, left, right), L) = let
val L = pre(right, L)
val L = pre(left, L)
in
x::L
end
in
pre (tree, nil)
end
它预先输出元素。但它会在后序中评估树!而且代码比天真的代码更丑陋preOrder
。
> val t = Node(5,
Node(3,
Node(1, Empty, Empty),
Node(4, Empty, Empty)),
Node(9, Empty, Empty))
> preOrder t
val it = [5,3,1,4,9] : int list
现有技术
我尝试在 ML 编程中搜索对差异列表的引用,并找到了John Hughes 的原始文章,该文章描述了如何使用差异列表进行反向操作。
我还在 Haskell 中找到了Matthew Brecknells 差异列表博客文章以及示例。他区分了使用累加器(如 Ullmans 反向示例)和为差异列表创建新类型。他还展示了一种树木压平机。但是我很难理解 Haskell 代码,并且希望在标准 ML 中进行类似的公开。美国广播公司
问题
如何实现一个函数来实际评估树并预先收集元素?遍历后是否必须反转列表?还是有什么其他技巧?
我如何推广这种技术以适用于中序和后序遍历?
为 BST 算法使用差异列表的惯用方式是什么?