Functional programming 函数符号表的平衡树

Functional programming 函数符号表的平衡树,functional-programming,binary-search-tree,sml,Functional Programming,Binary Search Tree,Sml,我正在做“用ML实现现代编译器”(Andrew Appel)的练习。其中之一(ex 1.1 d)是建议功能符号表采用平衡树数据结构。我们提到,这种数据结构应该在插入时重新平衡,而不是在查找时。由于对函数式编程一无所知,我发现这一点令人困惑。关于这个需求的关键洞察是什么?在每次插入和删除时重新平衡的树不需要在查找时重新平衡,因为查找不会修改结构。如果它在查找之前是平衡的,那么它将在查找期间和之后保持平衡 在函数式语言中,插入和重新平衡可能比过程式语言更昂贵。由于无法在适当的位置更改任何节点,因此可

我正在做“用ML实现现代编译器”(Andrew Appel)的练习。其中之一(ex 1.1 d)是建议功能符号表采用平衡树数据结构。我们提到,这种数据结构应该在插入时重新平衡,而不是在查找时。由于对函数式编程一无所知,我发现这一点令人困惑。关于这个需求的关键洞察是什么?

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

在函数式语言中,插入和重新平衡可能比过程式语言更昂贵。由于无法在适当的位置更改任何节点,因此可以通过以下方式替换节点:创建新节点,然后将其父节点替换为其子节点为新的子节点和未更改的大女儿的新节点,然后将祖父母节点替换为其子节点为新的父节点和其姐姐的节点,依此类推。当为更新的树创建新根节点并对所有替换的节点进行垃圾收集时,完成此操作。然而,一些树结构具有理想的特性,即它们在插入时只需要替换树的O(logn)个节点,并且可以重用其余的节点。这意味着红黑树的旋转(例如)的开销并不比不平衡插入多多少

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


这里有人问,.

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

在函数式语言中,插入和重新平衡可能比过程式语言更昂贵。由于无法在适当的位置更改任何节点,因此可以通过以下方式替换节点:创建新节点,然后将其父节点替换为其子节点为新的子节点和未更改的大女儿的新节点,然后将祖父母节点替换为其子节点为新的父节点和其姐姐的节点,依此类推。当为更新的树创建新根节点并对所有替换的节点进行垃圾收集时,完成此操作。然而,一些树结构具有理想的特性,即它们在插入时只需要替换树的O(logn)个节点,并且可以重用其余的节点。这意味着红黑树的旋转(例如)的开销并不比不平衡插入多多少

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


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

在实际操作中,坚持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
- 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
同样,可能您的符号表没有将字符串映射到整数,或者您可能有一个用于变量的符号表和一个用于函数的符号表

可以使用函子参数化id/值类型:

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
你可以这样使用它:

- ListSymTab.lookup "hello" (ListSymTab.insert "hello" 42 ListSymTab.empty);
> val it = SOME 42 : int option
- 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
基于列表的符号表只需比较标识符/符号是否相等即可。对于平衡树符号表,您需要可排序的标识符/符号

不要从头开始实施平衡树,例如:

要在类型T[…]上创建实现映射(字典)的结构,请执行以下操作:

使用T作为
字符串
尝试此示例,并将其作为
字符串进行比较。比较

$ 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);

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

在实际操作中,坚持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
- 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
同样,可能您的符号表没有将字符串映射到整数,或者您可能有一个用于变量的符号表和一个用于函数的符号表

可以使用函子参数化id/值类型:

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
你可以这样使用它:

- ListSymTab.lookup "hello" (ListSymTab.insert "hello" 42 ListSymTab.empty);
> val it = SOME 42 : int option
- 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
基于列表的符号表只需比较标识符/符号是否相等即可。对于平衡树符号表,您需要可排序的标识符/符号

不要从头开始实施平衡树,例如:

要在类型T[…]上创建实现映射(字典)的结构,请执行以下操作:

使用T作为
字符串
尝试此示例,并将其作为
字符串进行比较。比较

$ 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;
...
打开结构是探索可用函数及其类型的简单方法

W