我经常被告知,使用OCaml中的Lazy模块,可以在诸如Haskell这样的惰性语言中做任何你能做的事情。为了测试这个声明,我正在尝试编写一个函数,将常规列表转换为ocaml中的静态双向链表。
type 'a dlist = Dnil | Dnode of 'a dlist * 'a * 'a dlist对于这种类型,我可以手动创建几个静态双向链表:
let rec l1 = Dnode (Dnil,1,l2)
and l2 = Dnode (l1,2,l3)
and l3 = Dnode (l2,3,Dnil)但是我想写一个'a list -> 'a dlist类型的函数,它可以在OCaml中给出任何列表来构建一个静态双向链表。例如,给定[1;2;3],它应该输出与上面的l1等价的内容。
用Haskell编写这个算法非常简单:
data DList a = Dnil | Dnode (DList a) a (DList a)
toDList :: [a] -> DList a
toDList l = go Dnil l
where
go _ [] = Dnil
go h (x:xs) = let r = Dnode h x (go r xs) in r但是我还不知道在哪里调用lazy才能在OCaml中编译这个代码。
发布于 2011-04-28 15:09:38
如果您以从右到左的顺序构建链表(就像普通列表一样),那么每个节点的左侧元素将仅在该节点本身构建之后才会构建。您需要通过使left元素变得惰性来表示它,这意味着“这个值将在稍后构造”:
type 'a dlist =
| Dnil
| Dnode of 'a dlist Lazy.t * 'a * 'a dlist完成后,使用递归定义将每个节点构造为一个惰性值,该定义将惰性(仍未构造的)节点传递给构建下一个节点的函数调用(以便它可以访问上一个节点)。它实际上比看起来更简单:
let dlist_of_list list =
let rec aux prev = function
| [] -> Dnil
| h :: t -> let rec node = lazy (Dnode (prev, h, aux node t)) in
Lazy.force node
in
aux (Lazy.lazy_from_val Dnil) list发布于 2011-04-28 06:15:20
您只能构建在编译时确定的形状的循环、不可变的严格数据结构。我不打算正式地定义或证明这一点,但直观地说,一旦创建了数据结构,它的形状就不会改变(因为它是不可变的)。所以你不能添加到一个循环中。如果您创建循环的任何元素,则需要同时创建循环的所有其他元素,因为您不能有任何悬空指针。
Ocaml可以做Haskell能做的事情,但是你必须让Lazy模块参与进来!与Haskell不同,ML的数据结构是严格的,除非另有说明。惰性数据结构具有'a Lazy.t类型的片段。(在这个特定问题上,ML的输入比Haskell更精确。)惰性数据结构允许通过临时悬空指针(其链接值在指针第一次被解除引用时自动创建)来构建循环。
type 'a lazy_dlist_value =
| Dnil
| Dnode of 'a lazy_dlist_value * 'a * 'a lazy_dlist_value
and 'a lazy_dlist = 'a lazy_dlist_value Lazy.t具有循环数据结构的另一种常见方法是使用可变节点。(事实上,严格编程的铁杆支持者可能会将惰性数据结构视为可变数据结构的特例,它不会过多地破坏引用透明性。)
type 'a mutable_dlist_value =
| Dnil
| Dnode of 'a mutable_dlist_value * 'a * 'a mutable_dlist_value
and 'a mutable_dlist = 'a mutable_dlist_value ref当循环数据结构至少包含一个可变组件、一个函数(闭包)或模块时,循环数据结构非常有用。但编译器没有理由强制执行这一点--循环严格、不可变的一阶数据结构只是一种特殊情况,偶尔也会有用。
发布于 2011-04-28 06:01:25
type 'a dlist = Dnil | Dnode of 'a dlist Lazy.t * 'a * 'a dlist Lazy.t
let rec of_list list = match list with
[] -> Dnil
| x :: [] ->
let rec single () = Dnode (lazy (single ()), x, lazy (single ()))
in single ()
| x :: y -> Dnode (
lazy (
of_list (match List.rev list with
[] | _ :: [] -> assert false
| x :: y -> x :: List.rev y
)
),
x,
lazy (
of_list (match list with
[] | _ :: [] -> assert false
| x :: y -> y @ x :: []
)
)
)
let middle dlist = match dlist with
Dnil -> raise (Failure "middle")
| Dnode (_, x, _) -> x
let left dlist = match dlist with
Dnil -> raise (Failure "left")
| Dnode (x, _, _) -> Lazy.force x
let right dlist = match dlist with
Dnil -> raise (Failure "right")
| Dnode (_, _, x) -> Lazy.force xhttps://stackoverflow.com/questions/5810163
复制相似问题