-1

Description of the problem

I have a lazy infinite binary tree:

type 'a tree_node = TN of 'a * 'a inf_btree * 'a inf_btree
  and 'a inf_btree = 'a tree_node Lazy.t 

let rec ibt_map (f : 'a -> 'b) (t : 'a inf_btree) : 'b inf_btree = 
  let TN (x, ltree, rtree) = Lazy.force t in 
  lazy (TN (f x, ibt_map f ltree, ibt_map f rtree))

let rec example : int inf_btree = 
  lazy (TN (1, 
              ibt_map ((+) 1) example, 
              ibt_map ((+) 2) example
           )
       ) 
;;

and a lazy stream:

type 'a link_node = LN of 'a * 'a stream 
  and 'a stream = 'a link_node Lazy.t

Now I want to transform a tree into a stream, in such a way that it preserves the order of the elements in the tree. More precisely, I want elements close to the root to come early in the stream. However, if it's done depth-first then half the tree will never occur in the stream:

let df_tree_to_stream (t : 'a inf_btree) : 'a stream = 
  let TN (x, ltree, rtree) = Lazy.force t in 
  let substream1 = df_tree_to_stream ltree in 
  let substream2 = df_tree_to_stream rtree in 
  lazy (LN (x, substream1)) 
  (* how to work in substream2 ??? *)

An attempted solution by alternation

We could try to merge the two streams so that they alternate, but then the order of the elements will not be preserved. In the given example, the tree looks like

       1
    3    2
   5 6  4 3
  .........

The order of the stream should be

1, 3, 2, 5, 6, 4, 3, ...

But if we simply alternate the streams returned by subtrees, then the tree rooted at 3 would have streams that start with 5 and 6. So this subtree transforms into the stream 3, 5, 6 ... The other subtree becomes 2, 4, 3 ... So the overall resulting stream would be

1, 3, 2, 5, 4, ...

We could instead manage the order of visitation of nodes in the tree by maintaining a queue.


Question

My question is, is there simpler a way? Can we get the desired order not using a queue and only using recursion?

Addem
  • 3,635
  • 3
  • 35
  • 58

1 Answers1

0

It is unusual to try to get away from the tail recursive version, but yes you can hide the queue of the breadth first traversal in a call stack. For instance, you can define an interleave function that switches the left and right source with decreasing frequency:

let rec interleave first period count l r () =
  if count = 0 then
    if first then
      interleave false period period r l ()
    else
      let period = 2 * period in
      interleave true period period r l ()
  else
    match l () with
    | Seq.Nil -> r ()
    | Seq.Cons(y,l) ->
      Seq.Cons(y, interleave first period (count-1) l r)

let interleave = interleave true 1 1

let rec to_seq tree () =
  let lazy (TN(x,l,r)) = tree in
  Seq.Cons(x, interleave (to_seq l) (to_seq r))
Chris
  • 26,361
  • 5
  • 21
  • 42
octachron
  • 17,178
  • 2
  • 16
  • 23