创建一个寄存器

时间:2017-03-20 12:18:13

标签: dictionary tree ocaml

我必须创建一个用于存储单词的类型树,就像树的每个节点都会有一个字母和下一个字符的列表(所以具有相同根的单词将共享相同的“部分/分支”树的基本上是一个n-ary,用作词典。 全部使用Caml语言

1 个答案:

答案 0 :(得分:2)

好吧,我不知道它是否是家庭作业,但我还是会回答:

首先,我们需要为字母定义签名类型。

module type LS = sig
  type t
  val compare : t -> t -> int
end

然后,我们需要定义我们的结构:

module Make (L : LS) = struct

  module M = Map.Make(L)

  type elt = L.t list
  type t = { word : bool; branches : t M.t }

  let empty = { word = false; branches = M.empty }

  let is_empty t = not t.word && M.is_empty t.branches

  let rec mem x t =
    match x with
      | [] -> t.word
      | c :: cl -> try mem cl (M.find c t.branches)
        with Not_found -> false

  let rec add x t =
    match x with
      | [] -> if t.word then t else { t with word = true }
      | c :: cl ->
        let b = try M.find c t.branches with Not_found -> empty in
        { t with branches = M.add c (add cl b) t.branches }
end

现在,一步一步:

  • module Make (L : LS) = struct是一个仿函数,如果我们给它一个LS类型的模块作为参数,它将返回一个新模块
  • module M = Map.Make(L) type elt = L.t list type t = { word : bool; branches : t M.t } 这是复杂的一点,一旦你拥有它,一切都开始清晰。我们需要表示一个节点(正如您在Wikipedia page of tries中看到的那样)。我的代表是:节点是
    • 一个真值,表示此节点代表一个单词(这意味着从根到此节点的所有字母都形成一个单词)
    • 来自它的分支。为了代表这些分支,我需要一本字典,幸运的是在OCaml中有Map 仿函数。因此,我的字段branches是与某些字母trie相关联的字段(这就是我写branches : t M.t的原因)。然后,一个元素是一个字母列表,您将找出我选择此类型而不是string的原因。
  • let empty = { word = false; branches = M.empty }空的trie是没有分支的记录(因此,只是根),而且这个根不是一个单词(所以word = false)(同一个is_empty的想法)
  • let rec mem x t = match x with | [] -> t.word | c :: cl -> try mem cl (M.find c t.branches) with Not_found -> false

    这里变得有趣。我的单词是一个字母列表,如果我想知道一个单词是否在我的trie中,我需要通过这个列表进行递归函数。

    • 如果我到达我的列表为空的点,这意味着我到达了一个节点,从根到它的路径由我的所有字母组成。我只需要知道,此节点的值wordtrue还是false
    • 如果我仍然至少有一封信,我需要找到与此信相对应的分支。
      • 如果我找到这个分支(这将是一个特里),我只需要进行递归调用以找到其中的其余部分(cl
      • 如果我找不到,我知道我的话中没有我的话,所以我可以回复假。
  • let rec add x t = match x with | [] -> if t.word then t else { t with word = true } | c :: cl -> let b = try M.find c t.branches with Not_found -> empty in { t with branches = M.add c (add cl b) t.branches }

    同样的想法。如果我想添加一个词:

    • 如果我的列表为空,则表示我添加了所有字母,并且我已到达与我的字对应的节点。在这种情况下,如果word已经true,则表示此词已添加,我不做任何事情。如果wordfalse,我只返回相同的分支(trie),但word等于true
    • 如果我的列表至少包含一个字母c,我会在当前节点中找到与其对应的分支(try M.find c t.branches with Not_found -> empty)并且我没有这样的分支,我只返回一个空的,然后我递归地将其余的字母添加到此分支,并将此新分支添加到与字母c关联的当前节点的分支(如果此分支已存在,则将在我使用时替换它字典)

Example of a trie and its evolutione

在这里,我们从空的trie开始,我们将添加到 top tea

如果我们不想使用仿函数,我们可以这样做:

  type elt = char list
  type t = { word : bool; branches : (char * t) list }

  let empty = { word = false; branches = [] }

  let is_empty t = not t.word && t.branches = []

  let find c l = 
    let rec aux = function
      | [] -> raise Not_found
      | (c', t) :: tl when c' = c -> t
      | _ :: tl -> aux tl
    in aux l

  let rec mem x t =
    match x with
      | [] -> t.word
      | c :: cl -> try mem cl (find c t.branches)
        with Not_found -> false

  let rec add x t =
    match x with
      | [] -> if t.word then t else { t with word = true }
      | c :: cl ->
        let b = try find c t.branches with Not_found -> empty in
        { t with branches = (c, (add cl b)) :: t.branches }