我正在尝试执行懒惰评估。
我根据lazy list
函数创建了map
类型。
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)
)
第一个问题:
根据我的理解,lazy
只是将事物封装在() behind
内而不立即执行。
对于函数zlist_of_list
,整个
match l with
| [] -> Empty
| hd::tl -> Node (hd, zlist_of_list tl)
将会延迟,在应用zlist_of_list
时不执行任何位,map_z
也是如此。
我是对的吗?
下面,我尝试加倍 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
结果是
transforming 1
1
-1
transforming 2
2
-2
transforming 3
3
-3
我不明白。似乎执行是按列,而不是按行。我认为应该是
第二个问题:
为什么懒惰,执行顺序变得那样?
答案 0 :(得分:5)
对于你的第一个问题:没错,map_z
将返回计算列表下一部分的thunk,而不是列表本身。特别是,map_z
定义中的递归调用在强制执行之前不会下降到列表的其余部分 - 您可以从map_z
的结果中获取一个转换元素而不计算其余部分。
这也是你的第二个问题的答案:你看到一个元素被转换,然后传递给f1
,然后f2
的原因是你在每个步骤中从懒惰中获取一个元素列表和其他人仍然暂停。
这就是懒惰名单的重点!以这种方式做事很有用,因为它提供了一种使用无限(或非常大)列表进行编程的相当自然的方法。如果在使用元素之前必须首先计算整个列表,那么它实际上不是一个惰性数据结构。