在OCaml中使用数据结构的正确方法

时间:2013-01-21 14:26:40

标签: data-structures functional-programming ocaml

好的,我在OCaml中写了binary search tree

type 'a bstree = 
    |Node of 'a * 'a bstree * 'a bstree
    |Leaf


let rec insert x = function
    |Leaf -> Node (x, Leaf, Leaf)
    |Node (y, left, right) as node -> 
        if x < y then
            Node (y, insert x left, right)
        else if x > y then
            Node (y, left, insert x right)
        else
            node

我猜上面的代码没有问题。

使用时,我写

let root = insert 4 Leaf

let root = insert 5 root

...

这是use/insert到树的正确方法吗?

我的意思是,我想我不应该声明root,每次我再次更改变量root的值,对吗?

如果是这样,我怎样才能始终保留根并且可以随时在树中插入值?

1 个答案:

答案 0 :(得分:7)

这看起来像是插入树中的好功能代码。它在插入过程中不会改变树,而是创建一个包含该值的新树。不可变数据的基本思想是你不“保留”东西。您计算值并将它们传递给新函数。例如,这是一个从列表中创建树的函数:

let tree_of_list l = List.fold_right insert l Leaf

它的工作原理是将当前树传递给insert的每个新调用。

值得学习以这种方式思考,因为FP的许多好处来自于使用不可变数据。然而,OCaml是一种混合范式语言。如果你愿意,你可以使用引用(或可变记录字段)来“保留”树,因为它改变了值,就像在普通的命令式编程中一样。

修改

您可能认为以下会话显示了对变量x的修改:

# let x = 2;;
val x : int = 2
# let x = 3;;
val x : int = 3
# 

然而,看待这个的方法是这两个不同的值恰好都被命名为x。因为名称相同,所以隐藏了x的旧值。但是如果你有另一种方法来访问旧值,它仍然会存在。也许以下内容将说明工作原理:

# let x = 2;;
val x : int = 2
# let f () = x + 5;;
val f : unit -> int = <fun>
# f ();;
- : int = 7
# let x = 8;;
val x : int = 8
# f ();;
- : int = 7
# 

使用值8创建名为x的新事物不会影响f的作用。它仍然使用定义时存在的旧x

编辑2:

从树中不可移除地删除值类似于添加值。即,您实际上并未修改现有树。您创建一个没有您不想要的值的新树。正如插入不会复制整个树(它重新使用前一个树的大部分),因此删除也不会复制整个树。树的任何未更改的部分都可以在新树中重复使用。

编辑3

这是从树中删除值的一些代码。它使用辅助函数来连接两个已知不相交的树(此外,a中的所有值都小于b中的所有值):

let rec adjoin a b =
    match a, b with
    | Leaf, _ -> b
    | _, Leaf -> a
    | Node (v, al, ar), _ -> Node (v, al, adjoin ar b)

let rec delete x = function
    | Leaf -> Leaf
    | Node (v, l, r) ->
        if x = v then adjoin l r
        else if x < v then Node (v, delete x l, r)
        else Node (v, l, delete x r)

(希望我不只是破坏你的作业!)