在ML中,大多数形式的数据是不可变的,即一旦我们创建好该类型的绑定,则无法在后续更改其数据。这也是函数式编程之所以为函数式的原因之一。
思考下面的代码,来体会无突变的好处:
fun sort_pair (pr : int * int) =
if #1 pr < #2 pr
then pr
else (#2 pr, #1 pr)
fun sort_pair (pr : int * int) =
if #1 pr < #2 pr
then (#1 pr, #2 pr)
else (#2 pr, #1 pr)
在ML中,上面的两种实现对调用者来说都是难以区分的。因为“元组”(tuples)是不可变的。第一个例子相对更好,因为它可以避免在分支语句上创建新的“pair”。
假如ML有可变性
val x = (3, 4)
val y = sort_pair x
... (*某些操作使#1 x赋值为5*)
val z = #1 y
那y、z的值是什么?
y的值都是(3, 4),有两种可能:
- y只是x的别名(或引用)
- y是新的一个pair。
有可变性的情况下,当后续某些操作,使#1 x被赋予新值时,那y的值是什么?z呢?这都如上诉情况,完全取决于具体实现。
没有可变性的情况下,我们就不需要担心,可变性的绑定会在某些未知的地方被更改;保持专注在其他事上;并且可以使用别名(或引用)以节约内存。
另一个值得思考的例子
fun append (xs : int list, ys : int list) =
if null xs
then ys
else hd (xs) :: append (tl(xs), ys)
val x = [2,4]
val y = [5,3,0]
val z = append(x,y)
在执行了上面的代码后,我们将获得一个怎样的list呢?
- 第一种情况:得到[2, 4],后面连接y的别名(上图上部分)
- 第二种情况:得到一个全新的list(上图下部分)
在ML中,是第一种情况。在此情况下,有可变性是非常糟糕的,假如list z中某一元素被改变,那么可能会导致y也被影响。