OCaml 中的 let 绑定和引用有什么区别?

What is the difference between let-bindings and references in OCaml?

OCaml 教程说引用是 "real variables",您可以在整个程序中对其进行分配和更改。让绑定不用于相同的目的。在此 link 中,它表示当名称由 let-binding 设置时,您不能“...重新分配它以指向不同的小部件”。我知道引用实际上存储在内存中,而 let-bindings 不是,但我不明白他们对分配的看法。

在 Ocaml 交互式会话中玩转,似乎 let-bindings 和 references 做同样的事情,只是语法上有一些不同。如果我使用 let-binding 将变量名称设置为某个整数值,该名称将 return 该值,直到我取消它或将名称重置为另一个整数,这是 let-binding 允许的。 ints、floats 和 strings 都是如此,但没有尝试过其他类型。我错过了什么?

# let let_var = 2;;
val let_var : int = 2
# let_var;;
- : int = 2
# let let_var = 3;;
val let_var : int = 3
# let_var;;
- : int = 3

看待事物的最佳方式是,let 绑定是一种为某事物赋予永久名称的方式。由于 OCaml 的范围规则允许您使用 re-use 名称,因此可以在内部上下文中为其他内容指定相同的名称。但名字在某种意义上仍然存在,并且仍然与其永久价值相关联。

这里有一个 session 可能有助于说明这一点:

$ ocaml
        OCaml version 4.02.1

# let v = 12;;
val v : int = 12
# let g x = v + x;;
val g : int -> int = <fun>
# g 10;;
- : int = 22
# let v = 200;;
val v : int = 200
# g 10;;
- : int = 22
#

在此 session 中,有两个 不同的 let 绑定具有相同的名称 v。函数 g 使用第一个。如果您创建一个名为 v 的新绑定,这不会改变 g.

的任何内容

另一方面,引用是一个值。它根本不是一个名字,它是一种可变的值;即,它包含另一个可以更改的值。以下 session 可能有助于说明这一点:

# let myref = ref 12;;
val myref : int ref = {contents = 12}
# let h x = !myref + x;;
val h : int -> int = <fun>
# h 10;;
- : int = 22
# myref := 200;;
- : unit = ()
# h 10;;
- : int = 210

这里 myref 是一个 let-bound 变量(永久地)指向一个引用值。最初,引用的值为 12。但该值可以更改。函数 h 使用存储在引用中的当前值。但请注意(再次)myref 没有改变。它仍然(永久地)与同一个引用相关联。

在我看来,let 定义了一个变量,OCaml 中的所有变量都是不可修改的——根本没有语法可以分配给变量。

有些变量是指向复合数据结构的指针,有些数据结构是可变的(包括数组、字符串和 records/objects with mutable 字段)。 "reference",一个可变单元格,是最简单的可变数据结构,标准库中的 Pervasives 中已经提供了操作符来处理它;但它与其他可变数据结构没有什么不同。

打个比方,就好像你在Java,所有变量总是final。你有一个数据结构 Ref<T>,你可以用 new Ref<Integer>(...) 分配它来获取对该对象的引用,你可以用 .get().set() 访问它的单个数据等,您可以将对此数据结构的引用分配给其他变量,以便您可以跨引用共享对该数据结构的更改。 OCaml 中的 ref 发生了完全相同的事情——你用 ref ... 分配它来获取引用,然后用 !<- 访问它的单个数据,并且您可以将引用分配给其他变量以跨引用共享对同一数据结构的视图。