我正在尝试在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
答案 0 :(得分:0)
(很抱歉,我先前的回答是错误的。我想念您在辅助函数中直接使用了q
。)
您的函数loop_delete
寻找匹配的节点,但是当找到匹配的节点时,似乎做出了不必要的假设。如果匹配的节点n
位于队列(n.next = None
)的末尾,则假定它是队列的唯一元素(将q.head
和q.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
类型的值。它们代表上一个节点和当前节点。如果没有上一个节点,那是因为您在列表的开头。如果没有当前节点,那是因为您在列表的末尾。
对不起,较早时无用的评论。也许这会好一点。