我被告知,默认情况下,数据在 F# 中是不可变的。
当我们为某个变量重新赋值时,真正发生的是它重新绑定了变量的值,但设置新值是不同的事情。
重新绑定称为“Shadowing”,如果我们不明确说明变量的值是可变的,则不可能设置新值。
有人可以更详细地向我解释这个概念吗?
阴影(重新绑定)有什么区别:
let var = "new_value"
并设置一个新值,如:
var <- "new_value"
这是一个时刻,在重新绑定期间,我们创建了另一个对象,并将该对象的地址分配给变量,而在第二个示例中,我们更改了值本身?我从堆/堆栈概念中引入了这一点..但我可能是错的。
Shadowing 是指您创建一个使用与先前绑定相同名称的 new 绑定。这会“隐藏”原始名称,隐藏它但不会更改或替换它。在 FSI 中尝试一下,看看:
let foo = 42
let printFoo () =
printfn "%i" foo
printFoo() ;;
这将打印:
42
val foo : int = 42
val printFoo : unit -> unit
val it : unit = ()
然后添加:
// ... more code
let foo = 24
printfn "%i" foo // prints 24
printFoo ();;
这将打印:
24
42
val foo : int = 24
val it : unit = ()
请注意,当您调用
printFoo()
时,它仍然打印 42 - 该函数看到原始(未阴影)绑定,但新打印显示新值。
使用
<-
来改变值需要可变绑定:
let mutable bar = 42
let printBar () =
printfn "%i" bar
printBar ();;
与上面一样,打印 42。请注意,您使用 mutable 关键字覆盖此处的默认不可变行为。
然后更改可变绑定中的值:
bar <- 24
printfn "%i" bar
printBar ();;
这将打印 24 两次,因为与阴影版本不同,突变改变了原始绑定。如果您在原始绑定中关闭
mutable
,则在使用 <-
时会出现错误。
补充 Reed Copsey 的出色答案,如果您正在编写一个循环来更改某种累加器的值,则可以将原始值设置为
mutable
。例如,您可以这样做。
let mutable acc = 0 // declaration
for i in 1..100 do
acc <- acc + i // assignment
这或多或少相当于 C# 代码:
var acc = 0;
for (int i = 1; i <= 100; i++)
{
acc = acc + i; // assignment
// Another way to write this:
// acc += i;
}
但是,在 Shadowing 中,如以下 F# 代码片段所示:
let acc = 0 // declaration
for i in 1..100 do
let acc = acc + i // another declaration only within the loop
你实际上并没有做任何有用的事情!第二个声明的作用域仅在
for
循环内,并且它不会更改原始 acc
的值。
粗略的 C# 等价物是这样的:
var acc = 0; // declaration
for (int i = 1; i <= 100; i++)
{
var acc2 = acc + i; // another declaration only within the loop
}
请注意,使用
acc
(而不是 acc2
)作为内部变量将无法在 C# 中编译,因为它在此上下文中没有 Shadowing。
阴影的用途是,它可以防止在您不想要的代码块中使用原始变体。这样就少了一个需要担心的变量。
每当我想知道到底发生了什么时,我都会使用像
ILSpy
这样的工具
例如:
let f () =
let x = Dictionary<int, string> ()
let mutable x = ResizeArray<int> 16
x <- ResizeArray<int> 16
使用ILSpy反编译成C#代码就变成了:
public static void f()
{
Dictionary<int, string> x = new Dictionary<int, string>();
List<int> x2 = new List<int>(16);
x2 = new List<int>(16);
}
这里可以更明显地看出阴影和设置之间的区别。
Shadowing
x
创建一个名为 x2
的新变量。
设置为正常赋值。