I am trying to implement a queue structure in OCaml, and now writing a function that tests whether a value is in the queue. I originally wrote a correct--or at least I think it was a correct--implementation of the function. But when I tested it, I would get unexpected test failures. Namely, it would return false when the queue was empty (a good thing) but would also return false in every other cases, whether the queue was empty or not, and whether the queue contained the value or not. So I re-wrote the function in this kind of dumb way (where Some h -> true
) in order to try to figure out what the problem is. Even with this dumb function, it's still returning false when I pass it any queue and any value, so clearly it's reading the head of every queue as a None
, whether it is a None
or not.
Contains:
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
Tests
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
The other functions written here have been tested and come out as expected. The type declaration for the queue type is
type 'a qnode = { v: 'a;
mutable next: 'a qnode option }
type 'a queue = { mutable head: 'a qnode option;
mutable tail: 'a qnode option }
Any thoughts about why it's taking every q.head
to be None
would be appreciated.
from_list
A helper function that walks through a list turning each value into a qnode and linking it to the next, returning the resulting list.
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
Make the list of qnodes, find its first and last elements, and assign them as the head and tail of the queue.
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
Here's what I originally had for the contains function before I tried to simplify it to narrow in on the strange behavior.
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