使用OCAML中的链接列表队列进行删除

时间:2019-02-21 03:38:09

标签: linked-list queue ocaml optional

我正在尝试在OCAML中实现单链接列表(队列),这很困难,而且不像我想象的那样直观。让我首先说明我要实现的队列的不变性:

q.head and q.tail are either both None, or
   - q.head and q.tail both point to Some nodes, and
     - q.tail is reachable by following 'next' pointers
       from q.head
     - q.tail's next pointer is None.

使用以下选项将队列及其关联节点声明为:

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

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

我有很多测试用例,但首先失败的是这个:

let test (): bool = 
  let q = from_list [5; 6; 7] in
  delete 5 q;
  valid q && to_list q = [6; 7]
;; run_test "delete element from front of queue" test

Valid()仅测试队列是否满足不变量。这是我的实现:

let delete (elt: 'a) (q: 'a queue) : unit =
    if not (valid q) then failwith "delete: given invalid queue";
    let rec loop_delete (elt: 'a) (no: 'a qnode option) : unit = 
      begin match no with 
      |None -> ()
      |Some n -> (*Item to delete is only item in queue*)
               if n.v = elt && n.next = None then 
               (q.head <- None; q.tail <- None) 
               else if n.v = elt && n.next = Some n 
               then (q.head <- n.next; n.next <- None) else loop_delete elt n.next
      end
    in loop_delete elt q.head

为什么n.next无法正确更新?而且,我确定我的实现还有很多其他问题。任何帮助表示赞赏。

编辑:

根据以下建议,我进行了以下调整。这些评论表明仍然存在困惑。

let delete (elt: 'a) (q: 'a queue) : unit =
    if not (valid q) then failwith "delete: given invalid queue";
    let rec loop_delete (elt: 'a) (prev: 'a qnode option) 
    (curr: 'a qnode option) : unit = 
      begin match prev, curr with 
      |_, None -> ()
      |None, Some n -> if n.v = elt && n.next = None then (q.head <- None; q.tail <- None) (*One element in list only...right??*)
      else if n.v = elt && n.next != None then (*How to continuously update prev and curr??* else ()
      |Some n, Some n1 -> (*??*)
      end
    in loop_delete elt None q.head

1 个答案:

答案 0 :(得分:0)

(很抱歉,我先前的回答是错误的。我想念您在辅助函数中直接使用了q。)

您的函数loop_delete寻找匹配的节点,但是当找到匹配的节点时,似乎做出了不必要的假设。如果匹配的节点n位于队列(n.next = None)的末尾,则假定它是队列的唯一元素(将q.headq.tail设置为{ {1}})。如果匹配的节点不在队列的末尾,则似乎假定匹配的节点在队列的末尾(它将None设置为q.head)。

作为一个单独的问题,这不是一个合理的测试:

n.next

它测试的是n.next = Some n 的{​​{1}}指针是否指向next本身。如果您只是想知道n是否指向任何节点,您可以说n

作为更一般的注释,从单链列表中删除时,通常需要跟踪两个节点,即您感兴趣的节点和列表中的前一个节点。找到要删除的节点后,需要更改上一个节点的n.next值。

在OCaml中进行编码时,您不需要真正考虑指针,但是将n.next <> None视为空指针,将next视为指向节点{{1 }}。对于第一个呼叫,您没有上一个节点,因此呼叫看起来像这样:

None

当您想移至Some n内的下一个节点时,呼叫可能看起来像这样:

n

实质上,您正在传递两个loop_delete elt None q.head 类型的值。它们代表上一个节点和当前节点。如果没有上一个节点,那是因为您在列表的开头。如果没有当前节点,那是因为您在列表的末尾。

对不起,较早时无用的评论。也许这会好一点。