Version française
Home     About     Download     Resources     Contact us    
Browse thread
[Caml-list] Stack Overflow... (recursion in try-statement)
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: John Prevost <visigoth@c...>
Subject: Re: [Caml-list] Stack Overflow... (recursion in try-statement)
>>>>> "ob" == Oliver Bandel <oliver@first.in-berlin.de> writes:

    ob> Hello, why does an stack overflow-error occur here?

let rec traversedir dir =
          try ( [Unix.readdir dir] @ traversedir dir ) with
          End_of_file -> [];;

    ob> I have not tried it with very deep directories, so I did not
    ob> expect such an error...

    ob> What is the problem here?

The problem is that when you write:

[Unix.readdir dir] @ traversedir dir

the recursive call happens first.  Say you have a directory with only
one entry in it:

traversedir dir
=>
try ( [Unix.readdir dir] @ traversedir dir ) with
End_of_file -> []
=>
try ( [Unix.readdir dir] @
  (try [Unix.readdir dir] ...) ) with
End_of_file -> []

and so on.  Hence, readdir is never actually called.

Note that it is *not* safe to turn this around in order to fix things,
since the order of evaluation is not guaranteed.  In fact, unless it
has changed, the above will fail in bytecode but not native code, and
the reverse will work in bytecode but fail on native.  The appropriate
way to do it is:

let rec traversedir dir =
  try
    let entry = Unix.readdir dir in
    [entry] @ traversedir dir
  with End_of_file -> []

This has its own problems, since it's not tail recursive, but it will
not go into an endless loop.  A better formulation is:

let traversedir dir =
  let rec loop l =
    match (try Some (Unix.readdir dir) with End_of_file -> None) with
      | Some ent -> loop (ent :: l)
      | None     -> l
  in loop []

Whether to go this far and be tail recursive is up to you, of course.
Note that if you are going to go for tail recursion, it's important to
make sure the try is not around the tail call, since exception
handling blocks tail calls.

Finally, the following could be useful to you:

let mapdir f accum dir =
  let rec loop accum =
    match (try Some (Unix.readdir dir) with End_of_file -> None) with
      | Some ent -> f ent accum
      | None     -> accum
  in loop accum

let cons x y = x :: y
let traversedir = mapdir cons []

John.
-------------------
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