OCaml模式匹配中的奇怪行为

时间:2017-10-10 16:05:06

标签: pattern-matching ocaml

我正在尝试在OCaml中实现队列结构,现在编写一个测试值是否在队列中的函数。我最初写了一个正确的 - 或者至少我认为这是正确的 - 函数的实现。但是当我测试它时,我会遇到意外的测试失败。也就是说,当队列为空时它会返回false(一件好事)但在其他所有情况下都会返回false,无论队列是否为空,以及队列是否包含该值。所以我以这种愚蠢的方式(Some h -> true)重新编写了这个函数,以便试图找出问题所在。即使使用这个哑函数,当我将任何队列和任何值传递给它时,它仍然返回false,所以显然它正在以None的形式读取每个队列的头部,无论它是否为None

包含:

let contains (v: 'a) (q: 'a queue) : bool =
    if not (valid q) then failwith "contains: given invalid queue";
    let rec loop (value: 'a) (qn: 'a qnode option) : bool =
      begin match qn with
      | None -> false
      | Some h -> true
      end
    in loop elt q.head

测试

   let test () : bool =
    let q = create () in
    not (contains 1 q)
  ;; run_test "contains empty" test

  let test () : bool =
    let q = from_list [2; 3] in
    contains 3 q
  ;; run_test "contains non-empty true" test

  let test () : bool =
    let q = from_list [2; 3] in
    not (contains 4 q)
  ;; run_test "contains non-empty false" test

此处编写的其他功能已经过测试,并按预期推出。队列类型的类型声明是

  type 'a qnode = { v: 'a;
                    mutable next: 'a qnode option }

  type 'a queue = { mutable head: 'a qnode option;
                     mutable tail: 'a qnode option }

对于每个q.headNone的原因,我们将不胜感激。

from_list

一个辅助函数,遍历列表,将每个值转换为qnode并将其链接到下一个,返回结果列表。

  let rec vals_to_qnodes (l: 'a list) : 'a qnode list =
    begin match l with
    | [] -> []
    | [h] -> [{v = h; next = None}]
    | h1::h2::t -> let sofar = vals_to_qnodes (h2::t) in
                    begin match sofar with
                    | [] -> []
                    | x::y -> {v = h1; next = Some x}::x::y
                    end
    end

制作qnodes列表,找到它的第一个和最后一个元素,并将它们指定为队列的头部和尾部。

  let from_list (l: 'a list) : 'a queue =
    let qsList = vals_to_qnodes l in
    begin match qsList with
    | [] -> create ()
    | h::t -> begin match t with
              | [] -> {head = Some h; tail = Some h}
              | h2::t2 -> let x = List.rev t2 in
                          begin match x with
                          | [] -> {head = None; tail = None;}
                          | h3::t3 -> {head = Some h; tail = Some h3}
                          end
              end
    end

在我尝试简化它以缩小奇怪的行为之前,这是我最初用于包含函数的内容。

let contains (v: 'a) (q: 'a queue) : bool = 
  if not (valid q) then failwith "contains: given invalid queue";
  let rec loop (value: 'a) (qn: 'a qnode option) : bool = 
    begin match qn with 
    | None -> false
    | Some h -> if v == value then true 
                else loop value h.next
    end
  in loop v q.head

1 个答案:

答案 0 :(得分:2)

当我尝试您的代码时,它的行为与您描述的不同:

# let n = { v = 1; next = None };;
val n : int qnode = {v = 1; next = None}
# let q = { head = Some n; tail = Some n };;
val q : int queue =
  {head = Some {v = 1; next = None}; tail = Some {v = 1; next = None}}
# contains 3 q;;
- : bool = true

所以,我会说答案取决于from_list实际返回的内容。

作为附加评论,您对contains的定义引用了elt,我在任何可以看到的地方都没有定义。

<强>更新

您的新代码未定义create。我为create提供了这个定义:

let create () = { head = None; tail = None }

如果我运行from_list [1;2],这就是我所看到的:

# from_list [1;2];;
- : int queue = {head = None; tail = None}

这可以解释为什么头部看起来是None: - )