Header menu logo structured_programming_in_fsharp

Revisiting BFS

What is BFS
The common solution
Questioning the common solution
First discovery
First restriction
Second restriction
using System;

int[] MoveFromTo() {
    var from = new List<int>(new int[] { 1, 2, 3 });
    var to = new List<int>();
    while (from.Count > 0) {
        to.Add(from[0]);
        from.RemoveAt(0);
    }
    return to.ToArray();
}
let moveFromTo () =
    let rec loop from to' =
        match from with
        | [] -> (from, to')
        | x::xs -> 
            // here we replace to' by x::to' in a new instance of loop
            loop xs (x::to')
    
    let from = [1; 2 ;3]
    let to' = []
    loop from to'

How the implementation works

type GetChildren<'a> = { get: 'a -> (GetChildren<'a> * 'a list) }

let rec bfs (gc: GetChildren<'a>, n: int, level: 'a list) =
  seq {
    yield (n, level)

    let nextLevel =
      level
      |> List.fold
        (fun (gc, xs) x ->
          let newGc, ys = gc.get x
          (newGc, xs @ ys)
        )

        (gc, []) // initial state
        
    yield! 
        match nextLevel with
        | _, [] -> Seq.empty
        | (chl, nl) -> bfs  (chl, n+1, nl)
  }
Testing the implementation with a graph
type Graph<'a when 'a: comparison> = Map<'a, List<'a>>

let graphChildren (g: Graph<'a>) =
  let rec children visited =
    { get =
        fun x ->
          if Set.contains x visited then
            // x is already visited, no need to mark it as visited
            (children visited, [])
          else
            let newChildren = Set.add x visited |> children

            match Map.tryFind x g with
            | Some xs -> (newChildren, xs)
            | None -> (newChildren, []) }

  children Set.empty

let g = Map [ 1, [ 2; 3; 4 ]; 2, [ 5; 6 ]; 3, [ 7; 8 ]; 4, [ 9; 10 ] ]

bfs (graphChildren g, 0, [ 1 ]) |> Seq.iter (printfn "%A")
(0, [1])
(1, [2; 3; 4])
(2, [5; 6; 7; 8; 9; 10])
Testing the implementation with a tree

In a tree by visiting a node there's no way of going back to it, since there are no cycles. Keeping that in mind, we can rely on a sequence of children to be consumed by bfs. As long as we put children in the order bfs expects them, the implementation will be correct.

Example: We know if bfs gets the children of node X, then after that it will ask for children of node X+1, where X and X+1 are consecutive nodes in the same level.

type Tree<'a> = Node of 'a * Tree<'a> list

let rec childrenSeq (Node(_, xs)) =
  seq {
    match xs with
    | [] -> ()
    | _ ->
      let nodeValues = xs |> List.map (fun (Node(x, _)) -> x)
      yield nodeValues

    for x in xs do
      yield! childrenSeq x
  }

let treeChildren (t: Tree<'a>) =
  let chs = childrenSeq t |> Seq.toList

  let rec loop (chs: ('a list) list) =
    { get =
        fun _ ->
          match chs with
          | [] -> (loop [], [])
          | head::tail -> (loop tail, head) }

  loop chs

let t =
  Node(
    1,
    [ Node(2, [ Node(5, []); Node(6, []) ])
      Node(3, [ Node(7, []); Node(8, []) ])
      Node(4, [ Node(9, []); Node(10, []) ]) ]
  )

bfs (treeChildren t, 0, [ 1 ]) |> Seq.iter (printfn "%A")
(0, [1])
(1, [2; 3; 4])
(2, [5; 6; 7; 8; 9; 10])
type GetChildren<'a> = { get: ('a -> GetChildren<'a> * 'a list) }
'a
type 'T list = List<'T>
val bfs: gc: GetChildren<'a> * n: int * level: 'a list -> (int * 'a list) seq
val gc: GetChildren<'a>
val n: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
val level: 'a list
Multiple items
val seq: sequence: 'T seq -> 'T seq

--------------------
type 'T seq = System.Collections.Generic.IEnumerable<'T>
val nextLevel: GetChildren<'a> * '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 fold<'T,'State> : folder: ('State -> 'T -> 'State) -> state: 'State -> list: 'T list -> 'State
val xs: 'a list
val x: 'a
val newGc: GetChildren<'a>
val ys: 'a list
GetChildren.get: 'a -> GetChildren<'a> * 'a list
module Seq from Microsoft.FSharp.Collections
val empty<'T> : 'T seq
val chl: GetChildren<'a>
val nl: 'a list
type Graph<'a (requires comparison)> = Map<'a,List<'a>>
Multiple items
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val graphChildren: g: Graph<'a> -> GetChildren<'a> (requires comparison)
val g: Graph<'a> (requires comparison)
val children: visited: Set<'a> -> GetChildren<'a> (requires comparison)
val visited: Set<'a> (requires comparison)
val x: 'a (requires comparison)
Multiple items
module Set from Microsoft.FSharp.Collections

--------------------
type Set<'T (requires comparison)> = interface IReadOnlyCollection<'T> interface IStructuralEquatable interface IComparable interface IEnumerable interface IEnumerable<'T> interface ICollection<'T> new: elements: 'T seq -> Set<'T> member Add: value: 'T -> Set<'T> member Contains: value: 'T -> bool override Equals: obj -> bool ...

--------------------
new: elements: 'T seq -> Set<'T>
val contains: element: 'T -> set: Set<'T> -> bool (requires comparison)
val newChildren: GetChildren<'a> (requires comparison)
val add: value: 'T -> set: Set<'T> -> Set<'T> (requires comparison)
val tryFind: key: 'Key -> table: Map<'Key,'T> -> 'T option (requires comparison)
union case Option.Some: Value: 'T -> Option<'T>
val xs: List<'a> (requires comparison)
union case Option.None: Option<'T>
val empty<'T (requires comparison)> : Set<'T> (requires comparison)
val g: Map<int,int list>
val iter: action: ('T -> unit) -> source: 'T seq -> unit
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
type Tree<'a> = | Node of 'a * Tree<'a> list
val childrenSeq: Tree<'a> -> 'a list seq
union case Tree.Node: 'a * Tree<'a> list -> Tree<'a>
val xs: Tree<'a> list
val nodeValues: 'a list
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
val x: Tree<'a>
val treeChildren: t: Tree<'a> -> GetChildren<'a>
val t: Tree<'a>
val chs: 'a list list
val toList: source: 'T seq -> 'T list
val loop: chs: 'a list list -> GetChildren<'a>
val head: 'a list
val tail: 'a list list
val t: Tree<int>

Type something to start searching.