Reputation: 25812
I am trying to under the execution of lazy evaluation.
I created a lazy list
type and according map
function.
type 'a zlist = 'a node_t lazy_t
and 'a node_t = Empty | Node of 'a * 'a zlist
let rec zlist_of_list l = lazy (
match l with
| [] -> Empty
| hd::tl -> Printf.printf "transforming %d\n" hd;Node (hd, zlist_of_list tl)
)
let rec list_of_zlist zl =
match Lazy.force zl with
| Empty -> []
| Node (hd, tl) -> hd::(list_of_zlist tl)
let rec map_z f zl = lazy (
match Lazy.force zl with
| Empty -> Empty
| Node (hd, tl) -> Node (f hd, map_z f tl)
)
First question:
From my understanding, lazy
just encapsulate things inside () behind
without immediate execution.
So for function zlist_of_list
, the whole
match l with
| [] -> Empty
| hd::tl -> Node (hd, zlist_of_list tl)
Will be delayed, not a single bit is executed when zlist_of_list
is applied, so does map_z
.
Am I right?
Below, I try to do double lazy map
let f1 x = Printf.printf "%d\n" x; x
let f2 x = Printf.printf " %d\n" (-x); (-x)
let zl = zlist_of_list [1;2;3]
let zl_m2 = map_z f2 (map_z f1 zl)
let _ = list_of_zlist zl_m2
The outcome is
transforming 1
1
-1
transforming 2
2
-2
transforming 3
3
-3
The I don't understand. It seems the execution is by column, not by row. I thought it should be
Second question:
Why via lazy, the execution order becomes like that?
Upvotes: 1
Views: 1194
Reputation: 9377
To your first question: that's right, map_z
will return a thunk that calculates the next part of the list, not the list itself. In particular, the recursive call within the definition of map_z
will not descend into the rest of the list until it is forced - you can take one transformed element from the result of a map_z
without calculating the rest.
This is also the answer to your second question: the reason you see one element being transformed, then passed to f1
, then f2
is that at each step you are taking one element from a lazy list and the others remain suspended.
And that's the whole point of lazy lists! Doing things that way is useful because it provides a fairly natural way to program with infinite (or very large) lists. If an entire list had to be calculated first before using the elements, it would not really be a lazy data structure.
Upvotes: 5