我正在學習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前一個 –