Header menu logo structured_programming_in_fsharp

Queue

A queue is data structure that stores a sequence of elements, while supporting two basic operations: enqueue and dequeue

Let's visualize how it works:

 [0; 1; 2]
= { enqueue 42 }
 [0; 1; 2; 42]
 [0; 1; 2]
= { dequeue }
 0, [1; 2]
First restriction: rely on inmutable data structures
Solution:
type Queue<'a> = { front: 'a list; rear: 'a list }

let enqueue (q: Queue<'a>) (x: 'a) = { q with rear = x :: q.rear }

let dequeue (q: Queue<'a>) =
  match q.front, List.rev q.rear with
  | [], [] -> None
  | [], x :: xs -> Some(x, { front = xs; rear = [] })
  | x :: xs, _ -> Some(x, { q with front = xs })

let rec queueToSeq (q: Queue<'a>) =
  seq {
    match dequeue q with
    | None -> ()
    | Some(x, nq) ->
      yield x
      yield! queueToSeq nq
  }

Let's make a simple test for our algorithm:

let xs = Seq.init 10 id |> Seq.toList
let q = xs |> Seq.fold (fun q x -> enqueue q x) {front = []; rear = []}
let ys = queueToSeq q |> Seq.toList
List.zip xs ys |> List.forall (fun (x, y) -> x = y)

True

Notice that in the current implementation of Microsoft's F# compiler, the following pattern match

match q.front, List.rev q.rear with
| [], [] -> ...
| [], x :: xs -> ...
| x :: xs, _ -> ...

evaluates List.rev q.rear always before matching any branch. This means that the code could easily be made more efficient. However, I think it makes no sense to evaluate such an expression until after q.front has been matched, since in some situations this would be sufficient to decide which branch to take. For this reason, I prefer to present the algorithm as it is, and leave it to the reader to optimise it if they think it is necessary.

type Queue<'a> = { front: 'a list rear: 'a list }
'a
type 'T list = List<'T>
val enqueue: q: Queue<'a> -> x: 'a -> Queue<'a>
val q: Queue<'a>
val x: 'a
Queue.rear: 'a list
val dequeue: q: Queue<'a> -> ('a * Queue<'a>) option
Queue.front: 'a list
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val rev: list: 'T list -> 'T list
union case Option.None: Option<'T>
val xs: 'a list
union case Option.Some: Value: 'T -> Option<'T>
val queueToSeq: q: Queue<'a> -> 'a seq
Multiple items
val seq: sequence: 'T seq -> 'T seq

--------------------
type 'T seq = System.Collections.Generic.IEnumerable<'T>
val nq: Queue<'a>
val xs: int list
module Seq from Microsoft.FSharp.Collections
val init: count: int -> initializer: (int -> 'T) -> 'T seq
val id: x: 'T -> 'T
val toList: source: 'T seq -> 'T list
val q: Queue<int>
val fold<'T,'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> source: 'T seq -> 'State
val x: int
val ys: int list
val zip: list1: 'T1 list -> list2: 'T2 list -> ('T1 * 'T2) list
val forall: predicate: ('T -> bool) -> list: 'T list -> bool
val y: int

Type something to start searching.