功能符号表的平衡树

时间:2018-01-08 14:13:43

标签: functional-programming binary-search-tree sml

我在ML""现代编译器实现的练习(Andrew Appel)。其中之一(前1.1 d)是为功能符号表推荐平衡树数据结构。上诉提到这样的数据结构应该在插入时重新平衡,而不是在查找时。作为函数式编程的全新,我发现这令人困惑。对此要求的主要见解是什么?

2 个答案:

答案 0 :(得分:3)

在每次插入和删除时重新平衡的树不需要在查找时重新平衡,因为查找不会修改结构。如果在查找之前它是平衡的,它将在期间和之后保持平衡。

在函数式语言中,插入和重新平衡可能比在程序语言中更昂贵。因为您无法更改任何节点,所以通过创建新节点替换节点,然后将其父节点替换为其子节点是新子节点和未更改的旧节点的新节点,然后将祖父节点替换为其父节点孩子是新的父母和她的姐姐,依此类推。在为更新的树创建新的根节点并垃圾收集所有已替换的节点时完成。但是,某些树结构具有所需的属性,它们需要在插入时替换树的 O (log N )节点,并且可以重新使用其余的。这意味着红黑树(例如)的旋转没有比不平衡插入更多的开销。

此外,您通常需要比更新符号表更频繁地查询符号表。因此,尝试更快地插入变得不那么诱人了:如果你正在插入,你也可以重新平衡。

The question of which self-balancing tree structure is best for a functional language在这里被问到more than once

答案 1 :(得分:0)

由于戴维斯尔已经广泛回答了你的问题,这里主要是一些实施提示。我想补充说,符号表的数据结构选择可能与玩具编译器无关。当编译器用于大量代码并且代码经常重新编译时,编译时间才开始成为问题。

坚持 O(n)插入/查找数据结构在实践中很好,直到它没有。

签名方面,您只需要键值映射,插入和查找:

signature SymTab =
sig
  type id
  type value
  type symtab

  val empty : symtab
  val insert : id -> value -> symtab -> symtab
  val lookup : id -> symtab -> value option
end

带列表的简单 O(n)实现可能是:

structure ListSymTab : SymTab =
struct
  type id = string
  type value = int
  type symtab = (id * value) list

  val empty = []
  fun insert id value [] = [(id, value)]
    | insert id value ((id',value')::symtab) =
        if id = id'
          then (id,value)::symtab
          else (id',value')::insert id value symtab
  fun lookup _ [] = NONE
    | lookup id ((id',value)::symtab) =
        if id = id' then SOME value else lookup id symtab
end

您可以像以下一样使用它:

- ListSymTab.lookup "hello" (ListSymTab.insert "hello" 42 ListSymTab.empty);
> val it = SOME 42 : int option

然后,也许您的符号表不会将字符串映射到整数,或者您可能有一个变量符号表和一个函数符号表。

您可以使用仿函数参数化id / value类型:

functor ListSymTabFn (X : sig
                            eqtype id
                            type value
                          end) : SymTab =
struct
  type id = X.id
  type value = X.value
  (* The rest is the same as ListSymTab. *)
end

您可以像以下一样使用它:

- structure ListSymTab = ListSymTabFn(struct type id = string type value = int end);
- ListSymTab.lookup "world" (ListSymTab.insert "hello" 42 ListSymTab.empty);
> val it = NONE : int option

基于列表的符号表所需要的只是可以比较标识符/符号的相等性。对于平衡树符号表,您需要标识符/符号可以订购。

不要从头开始实现平衡树,而是看例如:在SML/NJ's RedBlackMapFn

  

创建一个在T [...] 类型上实现地图(字典)的结构:

structure MapT = RedBlackMapFn (struct
                                  type ord_key = T
                                  val compare = compareT
                                end)

尝试使用T为string的此示例,并将其作为String.compare进行比较:

$ sml
Standard ML of New Jersey v110.76 [built: Sun Jun 29 03:29:51 2014]
- structure MapS = RedBlackMapFn (struct
                                    type ord_key = string
                                    val compare = String.compare
                                  end);
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[library $SMLNJ-LIB/Util/smlnj-lib.cm is stable]
[autoloading done]
structure MapS : ORD_MAP?
- open MapS;
...

打开结构是探索可用功能及其类型的简便方法。

然后我们可以为ListSymTabFn创建一个类似的函子,但是需要一个额外的比较函数:

functor RedBlackSymTabFn (X : sig
                                type id
                                type value
                                val compare : id * id -> order
                              end) : SymTab =
struct
  type id = X.id
  type value = X.value

  structure SymTabX = RedBlackMapFn (struct
                                       type ord_key = X.id
                                       val compare = X.compare
                                     end)

  (* The 'a map type inside SymTabX maps X.id to anything. *)
  (* We are, however, only interested in mapping to values. *)
  type symtab = value SymTabX.map

  (* Use other stuff in SymTabT for empty, insert, lookup. *)
end

最后,您可以将其用作符号表:

structure SymTab = RedBlackSymTabFn(struct
                                      type id = string
                                      type value = int
                                      val compare = String.compare
                                    end);