首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从OCaml中的列表创建双向链表

从OCaml中的列表创建双向链表
EN

Stack Overflow用户
提问于 2011-04-28 04:29:22
回答 3查看 3.6K关注 0票数 16

我经常被告知,使用OCaml中的Lazy模块,可以在诸如Haskell这样的惰性语言中做任何你能做的事情。为了测试这个声明,我正在尝试编写一个函数,将常规列表转换为ocaml中的静态双向链表。

代码语言:javascript
复制
type 'a dlist = Dnil | Dnode of 'a dlist * 'a * 'a dlist

对于这种类型,我可以手动创建几个静态双向链表:

代码语言:javascript
复制
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编写这个算法非常简单:

代码语言:javascript
复制
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中编译这个代码。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-04-28 15:09:38

如果您以从右到左的顺序构建链表(就像普通列表一样),那么每个节点的左侧元素将仅在该节点本身构建之后才会构建。您需要通过使left元素变得惰性来表示它,这意味着“这个值将在稍后构造”:

代码语言:javascript
复制
type 'a dlist = 
  | Dnil
  | Dnode of 'a dlist Lazy.t * 'a * 'a dlist

完成后,使用递归定义将每个节点构造为一个惰性值,该定义将惰性(仍未构造的)节点传递给构建下一个节点的函数调用(以便它可以访问上一个节点)。它实际上比看起来更简单:

代码语言:javascript
复制
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
票数 12
EN

Stack Overflow用户

发布于 2011-04-28 06:15:20

您只能构建在编译时确定的形状的循环、不可变的严格数据结构。我不打算正式地定义或证明这一点,但直观地说,一旦创建了数据结构,它的形状就不会改变(因为它是不可变的)。所以你不能添加到一个循环中。如果您创建循环的任何元素,则需要同时创建循环的所有其他元素,因为您不能有任何悬空指针。

Ocaml可以做Haskell能做的事情,但是你必须让Lazy模块参与进来!与Haskell不同,ML的数据结构是严格的,除非另有说明。惰性数据结构具有'a Lazy.t类型的片段。(在这个特定问题上,ML的输入比Haskell更精确。)惰性数据结构允许通过临时悬空指针(其链接值在指针第一次被解除引用时自动创建)来构建循环。

代码语言:javascript
复制
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

具有循环数据结构的另一种常见方法是使用可变节点。(事实上,严格编程的铁杆支持者可能会将惰性数据结构视为可变数据结构的特例,它不会过多地破坏引用透明性。)

代码语言:javascript
复制
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

当循环数据结构至少包含一个可变组件、一个函数(闭包)或模块时,循环数据结构非常有用。但编译器没有理由强制执行这一点--循环严格、不可变的一阶数据结构只是一种特殊情况,偶尔也会有用。

票数 7
EN

Stack Overflow用户

发布于 2011-04-28 06:01:25

代码语言:javascript
复制
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 x
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5810163

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档