Herewith the comp.lang.functional version of my article.  I may have 
tidied it up a little for the Wiki; if so, those changes are lost.  Let 
it hereby enter the Haskell List archive!



The following message is a courtesy copy of an article
that has been posted as well.

Matti Nykanen <[EMAIL PROTECTED]> writes:

> I  recently came  across an  algorithm that  constructs a  binary tree
> using single _but  not immediate_ assignments. By this  I mean that it
> attaches a newly  created node into the existing  tree, but leaves the
> children of  the totally unspecified.  Later the  algorithm returns to
> fill in the missing pieces.
> 
> I tried to  write it in Haskell,  but couldn't. If I create  a node, I
> have to give its children some  values to start with, and those cannot
> be changed later.  I don't think uniqueness types  (from, e.g., Clean)
> help here,  because the partially  constructed node is referred  to by
> two  places: its  parent in  the tree,  and the  "to do"  list  of the
> algorithm for the unfinished nodes.

The solution to this is a little trick called `tying the knot'.
Remember that Haskell is a lazy language.  A consequence of this is
that while you are building the node, you can set the children to the
final values straight away, even though you don't know them yet!  It
twists your brain a bit the first few times you do it, but it works
fine.

Here's an example (possibly topical!).  Say you want to build a
circular, doubly-linked list, given a standard Haskell list as input.
The back pointers are easy, but what about the forward ones?

data DList a = DLNode (DList a) a (DList a)

mkDList :: [a] -> DList a

mkDList [] = error "must have at least one element"
mkDList xs = let (first,last) = go last xs first
             in  first

  where go :: DList a -> [a] -> DList a -> (DList a, DList a)
        go prev []     next = (next,prev)
        go prev (x:xs) next = let this        = DLNode prev x rest
                                  (rest,last) = go this xs next
                              in  (this,last)

takeF :: Integer -> DList a -> [a]
takeF 0     _                 = []
takeF (n+1) (DLNode _ x next) = x : (takeF n next)

takeR :: Show a => Integer -> DList a -> [a]
takeR 0     _                 = []
takeR (n+1) (DLNode prev x _) = x : (takeR n prev)


(takeF and takeR are simply to let you look at the results of mkDList:
they take a specified number of elements, either forward or backward).

The trickery takes place in `go'.  `go' builds a segment of the list,
given a pointer to the node off to the left of the segment and off to
the right.  Look at the second case of `go'.  We build the first node
of the segment, using the given prev pointer for the left link, and
the node pointer we are *about* to compute in the next step for the
right link.

This goes on right the way through the segment.  But how do we manage
to create a *circular* list this way?  How can we know right at the
beginning what the pointer to the end of the list will be?

Take a look at mkDList.  Here, we simply take the (first,last)
pointers we get from `go', and *pass them back in* as the next and
prev pointers respectively, thus tying the knot.  This all works
because of lazy evaluation.

Hope this helps.

--KW 8-)
-- 
: Keith Wansbrough, MSc, BSc(Hons) (Auckland) ------------------------:
: PhD Student, Computer Laboratory, University of Cambridge, England. :
:  (and recently of the University of Glasgow, Scotland. [><] )       :
: Native of Antipodean Auckland, New Zealand: 174d47' E, 36d55' S.    :
: http://www.cl.cam.ac.uk/users/kw217/  mailto:[EMAIL PROTECTED]     :
:---------------------------------------------------------------------:


-- 
: Keith Wansbrough, MSc, BSc(Hons) (Auckland) -------------------:
: PhD Student, Computer Laboratory, University of Cambridge, UK. :
: Native of Antipodean Auckland, New Zealand: 174d47'E, 36d55'S. :
: http://www.cl.cam.ac.uk/users/kw217/ mailto:[EMAIL PROTECTED] :
:----------------------------------------------------------------:


Reply via email to