我正在学习OCaml,而且我对变量的不变性有些困惑。根据我正在阅读的书,变量是不可变的。到目前为止这么好,但为什么我可以这样做:OCaml中的不可变变量
let foo = 42
let foo = 4242
我在想什么?
我正在学习OCaml,而且我对变量的不变性有些困惑。根据我正在阅读的书,变量是不可变的。到目前为止这么好,但为什么我可以这样做:OCaml中的不可变变量
let foo = 42
let foo = 4242
我在想什么?
我想解释的最好方式是用一个例子。考虑下面的代码(在OCaml的REPL执行):
# let foo = 42;;
val foo : int = 42
# let return_foo() = foo;;
val return_foo : unit -> int = <fun>
# let foo = 24;;
val foo : int = 24
# return_foo();;
- : int = 42
上面的代码执行以下操作:
42
到名称foo
。return_foo()
,该函数返回绑定到foo
的值。24
绑定到名称foo
(它隐藏了以前的foo
的绑定)。return_foo()
函数,返回42
。比较这与可变的值(创建OCaml中使用ref
)的行为:
# let foo = ref 42;;
val foo : int ref = {contents = 42}
# let return_foo() = !foo;;
val return_foo : unit -> int = <fun>
# foo := 24;;
- : unit =()
# return_foo();;
- : int = 24
其中:
42
一个可变的参考,并将其绑定到名称foo
。return_foo()
,该函数返回存储在与foo
绑定的引用中的值。24
在与foo
绑定的参考。return_foo()
函数,返回24
。名称foo
首先绑定到一个不可变的值42
,然后它将被反弹到另一个不可变的值4242
。您甚至可以将相同的名称绑定到不同类型的变量。在OCaml中,我们没有谈论变量的可变性,而是关于值的可变性。例如,如果将foo
绑定到一个值数组,这将是相同的名称,但绑定到可变数据,以便变量的值可以及时更改。最后,每个新绑定都隐藏了前一个绑定,所以原始foo仍然绑定到42
,但它不可见并且会收集垃圾。
也许一个小例子会澄清的理念是:
let example() =
let foo = 42 in (* 1 *)
let foo = 4242 in (* 2 *)
let foo = [|42|] in (* 3 *)
foo.(0) <- 56 (* 4 *)
这可能是更容易有如下心理模型:
(*1*) +--------+
+----> | 42 |
+------------+ | +--------+
| +----+
| foo +----+ +--------+
| | +----> | 4242 |
+---------+--+ (*2*) +--------+
|
| (*3*) +--------+
+------------> |[| 42 |]|
(*4*) +--------+
上线1
和2
我们只是绑定变量foo
到两个不同的值。在线3
我们将它绑定到包含一个元素的数组。在行4
上,我们更改了值,并且foo仍然绑定到相同的值,但是该值包含不同的数据。
我希望我没有混淆你更多;)
非常感谢你! – cfischer
的let
通常的形式是let ... in
表达,其中定义一个变量绑定,这仅在let
的主体的内部存在。 let
的主体是一个新的范围。
let x = 42 in (* body here *)
这里的let
的“身体”是一个新的范围是从外部的一个不同,从外部的所有变量与另外的局部变量x
,其仅在该let
的主体限定。
现在您正在讨论文件顶层的let
s。这些看起来语法有点不同(没有in
),但实际上它们是相同的,其中“body”是文件的其余部分。因此,您可以将let
作为新范围后的其余部分考虑在内,其中x
是此范围的局部变量。所以,你的代码是相同的:
let foo = 42 in (
let foo = 4242 in (
(* rest of file *)
)
)
这里你内心let
结合具有相同的名称已经存在于外部范围的变量的局部变量。这并不重要。您在内部范围内绑定了一个新变量。如果它恰好与外部作用域中的变量名称相同,则引用该名称的内部作用域中的代码将引用最内部的绑定。然而,这两个变量是完全独立的。
在类C语言,这将是这样的:
{
const int foo = 42;
{
const int foo = 4242;
// rest of code here
}
}
看到了吗?这里没有赋值给任何变量。
你不改变前一个的价值,只是作出一个新名字shadow shadowing前一个 –