我在SML目录中找到了Trie的这种实现:
signature DICT =
sig
type key = string (* concrete type *)
type 'a entry = key * 'a (* concrete type *)
type 'a dict (* abstract type *)
val empty : 'a dict
val lookup : 'a dict -> key -> 'a option
val insert : 'a dict * 'a entry -> 'a dict
val toString : ('a -> string) -> 'a dict -> string
end; (* signature DICT *)
exception InvariantViolationException
structure Trie :> DICT =
struct
type key = string
type 'a entry = key * 'a
datatype 'a trie =
Root of 'a option * 'a trie list
| Node of 'a option * char * 'a trie list
type 'a dict = 'a trie
val empty = Root(NONE, nil)
(* val lookup: 'a dict -> key -> 'a option *)
fun lookup trie key =
let
(* val lookupList: 'a trie list * char list -> 'a option *)
fun lookupList (nil, _) = NONE
| lookupList (_, nil) = raise InvariantViolationException
| lookupList ((trie as Node(_, letter', _))::lst, key as letter::rest) =
if letter = letter' then lookup' (trie, rest)
else lookupList (lst, key)
| lookupList (_, _) =
raise InvariantViolationException
(*
val lookup': 'a trie -> char list
*)
and lookup' (Root(elem, _), nil) = elem
| lookup' (Root(_, lst), key) = lookupList (lst, key)
| lookup' (Node(elem, _, _), nil) = elem
| lookup' (Node(elem, letter, lst), key) = lookupList (lst, key)
in
lookup' (trie, explode key)
end
(*
val insert: 'a dict * 'a entry -> 'a dict
*)
fun insert (trie, (key, value)) =
let
(*
val insertChild: 'a trie list * key * value -> 'a trie list
Searches a list of tries to insert the value. If a matching letter
prefix is found, it peels of a letter from the key and calls insert'.
If no matching letter prefix is found, a new trie is added to the list.
Invariants:
* key is never nil.
* The trie list does not contain a Root.
Effects: none
*)
fun insertChild (nil, letter::nil, value) =
[ Node(SOME(value), letter, nil) ]
| insertChild (nil, letter::rest, value) =
[ Node(NONE, letter, insertChild (nil, rest, value)) ]
| insertChild ((trie as Node(_, letter', _))::lst, key as letter::rest, value) =
if letter = letter' then
insert' (trie, rest, value) :: lst
else
trie :: insertChild (lst, key, value)
| insertChild (Root(_,_)::lst, letter::rest, value) =
raise InvariantViolationException
| insertChild (_, nil, _) = (* invariant: key is never nil *)
raise InvariantViolationException
(*
val insert': 'a trie * char list * 'a -> 'a trie
Invariants:
* The value is on the current branch, including potentially the current node we're on.
* If the key is nil, assumes the current node is the destination.
Effects: none
*)
and insert' (Root(_, lst), nil, value) = Root(SOME(value), lst)
| insert' (Root(elem, lst), key, value) = Root(elem, insertChild (lst, key, value))
| insert' (Node(_, letter, lst), nil, value) = Node(SOME(value), letter, lst)
| insert' (Node(elem, letter, lst), key, value) = Node(elem, letter, insertChild (lst, key, value))
in
insert'(trie, explode key, value)
end
(*
val toString: ('a -> string) -> 'a dict -> string
*)
fun toString f trie =
let
val prefix = "digraph trie {\nnode [shape = circle];\n"
val suffix = "}\n"
(* val childNodeLetters: 'a trie list * char list -> char list *)
fun childNodeLetters (lst, id) =
(foldr
(fn (Node(_, letter, _), acc) => letter::acc
| _ => raise InvariantViolationException) nil lst)
(* val edgeStmt: string * string * char -> string *)
fun edgeStmt (start, dest, lbl) =
start ^ " -> " ^ dest ^ " [ label = " ^ Char.toString(lbl) ^ " ];\n"
(* val allEdgesFrom: char list * char list *)
fun allEdgesFrom (start, lst) =
(foldr
(fn (letter, acc) =>
acc ^ edgeStmt(implode(start), implode(start @ [letter]), letter))
"" lst)
(* val labelNode: stirng * string -> string *)
fun labelNode (id: string, lbl: string) =
id ^ " [ label = \"" ^ lbl ^ "\" ];\n"
fun toString' (Root(elem, lst), id) =
let
val idStr = implode(id)
val childLetters = childNodeLetters(lst, id)
val childStr = foldr (fn (trie, acc) => acc ^ toString'(trie, id)) "" lst
in
(case elem
of SOME(value) =>
labelNode (idStr, f(value)) ^
allEdgesFrom (id, childLetters)
| NONE =>
labelNode (idStr, "") ^
allEdgesFrom (id, childLetters)) ^ childStr
end
| toString' (Node(elem, letter, lst), id) =
let
val thisId = id @ [letter]
val idStr = implode(thisId)
val childLetters = childNodeLetters(lst, thisId)
val childStr = foldr (fn (trie, acc) => acc ^ toString'(trie, thisId)) "" lst
in
(case elem
of SOME(value) =>
labelNode (idStr, f(value)) ^
allEdgesFrom (thisId, childLetters)
| NONE =>
labelNode (idStr, "") ^
allEdgesFrom (thisId, childLetters)) ^ childStr
end
in
prefix ^ (toString' (trie, [#"_", #"R"])) ^ suffix
end
end
每当我尝试使用上述函数在此实现中插入或查找字符串时:insert,lookup都会出现以下错误:
stdIn:1.2-1.8 Error: unbound variable or constructor: lookup
stdIn:1.2-1.8 Error: unbound variable or constructor: insert
我认为这是一个声明问题,但我不确定如何解决。 为什么会发生这种情况,如何在Trie数据结构中正确插入或搜索?
答案 0 :(得分:1)
首先,如果您没有此代码的知识产权,则应该链接到找到该代码的位置,而不是重复它,因为您没有提供任何署名。其次,该代码似乎可以正常工作。在这里,我要插入几个键并查找它们:
$ sml trie.sml
Standard ML of New Jersey v110.79 [built: Tue Aug 8 23:21:20 2017]
[opening trie.sml]
signature DICT =
sig
type key = string
type 'a entry = key * 'a
type 'a dict
val empty : 'a dict
val lookup : 'a dict -> key -> 'a option
val insert : 'a dict * 'a entry -> 'a dict
val toString : ('a -> string) -> 'a dict -> string
end
[autoloading]
[library $SMLNJ-BASIS/basis.cm is stable]
[library $SMLNJ-BASIS/(basis.cm):basis-common.cm is stable]
[autoloading done]
exception InvariantViolationException
structure Trie : DICT
- val foo = Trie.insert (Trie.empty, ("foo", 42));
val foo = - : int Trie.dict
- val bar = Trie.insert (foo, ("fab", 43));
val bar = - : int Trie.dict
- Trie.lookup bar "foo";
val it = SOME 42 : int option
- Trie.lookup bar "fab";
val it = SOME 43 : int option
- Trie.lookup bar "wat";
val it = NONE : int option