Version française
Home     About     Download     Resources     Contact us    
Browse thread
[Caml-list] Python's yield, Lisp's call-cc or C's setjmp/longjmp in OCaml
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: David Brown <caml-list@d...>
Subject: Re: [Caml-list] Python's yield, Lisp's call-cc or C's setjmp/longjmp in OCaml
On Thu, Dec 18, 2003 at 10:14:19AM +0900, Nicolas Cannasse wrote:

> type 'a tree =
>     |  Node of 'a tree list
>     |  Leaf of 'a
> 
> let rec iterator = function
>     | Node l -> List.iter iterator l
>     | Leaf x -> yield x
> 
> Doing it using a closure is more difficult, since we need to reproduce the
> stack using a data structure. That goes of course for all recursive data
> structures.

Ocaml does help us a bit by sort of having lazy functionality.  Remember
that lazy (expr) is roughly equivalent to (fun () -> expr), and is
equivalent in the case where Lazy.force is only called once.  The
closures are very capable of representing the recursive structure, right
in the closures them selves.

What makes this a little complicated is that your definitions make calls
to things like List.iter, which are not defined for lazy lists.  The
first thing to do, is rewrite your function to just return a list of the
results.  This could be done directly in Haskell, and the lazy
evaluation would give you exactly what you want.

  let rec iterator = function
    | Node l -> List.flatten (List.map iterator l)
    | Leaf x -> [x]

To make this lazy, we need to define our own lazy list type.  This is a
lot cleaner than the implicit closures I posted previously, since the
conversion to lazy form is fairly straightforward.  The thing that makes
this tricky is that we are mixing lazy with regular lists.  First, let's
rewrite the needed list functions (flatten, map, and append)
appropriately for the combination of regular and lazy lists we are
using.

  let rec lazy_map f = function
    | [] -> Nil
    | a::l ->
        let r = f a in
        Pair (r, lazy (lazy_map f l))

  let rec lazy_append l r = match l with
    | Nil -> r
    | Pair (a, ar) ->
        Pair (a, lazy (lazy_append (Lazy.force ar) r))

  let rec lazy_flatten = function
    | Nil -> Nil
    | Pair (l, r) ->
        lazy_append l (lazy_flatten (Lazy.force r))

Now we can directly rewrite your above iterator in a lazy fashion:

  let rec lazy_iterator = function
    | Node l -> lazy_flatten (lazy_map lazy_iterator l)
    | Leaf x -> Pair (x, lazy (Nil))

The following code shows that it indeed works:

  let rec force_iter f = function
    | Nil -> ()
    | Pair (a, l) ->
        f a;
        force_iter f (Lazy.force l)

  let showb () =
    let flat = lazy_iterator sample_tree in
    force_iter (fun x -> printf "%d," x) flat;
    printf "\n"

  let () = showb ()

Coming up with some good lazy data structures (lists are often enough)
and possibly some ocamlp4 grammar to make them as easy to use as regular
lists would be sufficient for this kind of problem.

A good exercise would be to rewrite all of this using a
continuation-passing style.

Dave

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners