我知道 F# 中的变量默认是不可变的。但是,例如在 F# 交互中:
> let x = 4;;
val x : int = 4
> let x = 5;;
val x : int = 5
> x;;
val it : int = 5
>
因此,我将 4 分配给 x,然后将 5 分配给 x,它正在发生变化。这是正确的吗?它应该给出一些错误或警告吗?或者我只是不明白它是如何工作的?
我知道 F# 中的变量默认是不可变的。但是,例如在 F# 交互中:
> let x = 4;;
val x : int = 4
> let x = 5;;
val x : int = 5
> x;;
val it : int = 5
>
因此,我将 4 分配给 x,然后将 5 分配给 x,它正在发生变化。这是正确的吗?它应该给出一些错误或警告吗?或者我只是不明白它是如何工作的?
编写时let x = 3
,您将标识符绑定x
到值3
。如果您在同一范围内再次执行此操作,则您声明了一个新标识符,该标识符隐藏了前一个标识符,因为它具有相同的名称。
在 F# 中改变值是通过破坏性更新运算符<-
. 对于不可变的值,这将失败,即:
> let x = 3;;
val x : int = 3
> x <- 5;;
x <- 5;;
^^^^^^
stdin(2,1): error FS0027: This value is not mutable
要声明可变变量,请在mutable
后面添加let
:
let mutable x = 5;;
val mutable x : int = 5
> x <- 6;;
val it : unit = ()
> x;;
val it : int = 6
但你可能会问,这两者有什么区别?一个例子可能就足够了:
let i = 0;
while i < 10 do
let i = i + 1
()
尽管看起来,这是一个无限循环。i
循环内部的声明是隐藏外部的不同的i
。外层是不可变的,所以它总是保持它的值0
并且循环永远不会结束。正确的写法是使用可变变量:
let mutable i = 0;
while i < 10 do
i <- i + 1
()
x
没有改变,只是被下一个声明隐藏了。例如:
> let x = 4;;
val x : int = 4
> let x = "abc";;
val x : string = "abc"
>
您没有将 5 分配给x
,而是定义了一个新变量。
以下示例显示有两个不同的变量。(它还表明你可以“访问”旧的 x 如果它在一个闭包中,由另一个函数使用):
let x = 5;;
let f y = y+x;;
f 10;;
let x = 0;;
f 10;;
产量
>
val x : int = 5
>
val f : int -> int
> val it : int = 15
>
val x : int = 0
> val it : int = 15
如您所见,对 f 的两个调用都使用第一个变量x
。定义let x = 0;;
定义了一个新变量x
,但没有重新定义f
。
这是一个说明 F# 中标识符“阴影”(即隐藏)的最小示例:
let x = 0
do //introduce a new lexical scope
let x = 1 //"shadow" (i.e. hide) the previous definition of x
printfn "%i" x //prints 1
//return to outer lexical scope
printfn "%i" x //prints 0, proving that our outer definition of x was not mutated by our inner definition of x
您的示例实际上有点复杂,因为您在 F# Interactive (FSI) 中工作。FSI 在您的示例中动态发出类似于以下内容的代码:
module FSI_0001 =
let x = 4;;
open FSI_0001 //x = 4 is now available in the top level scope
module FSI_0002 =
let x = 5;;
open FSI_0002 //x = 5 is now available in the top level scope, hiding x = 4
module FSI_0003 =
let it = x;;
open FSI_0003
//... subsequent interactions