2016-09-22 47 views
7

我已经介绍过,数据默认情况下在F#中是不可变的。当我们为某些变量重新赋值时,真正发生的是重新绑定变量的值,但设置一个新值是不同的。 重新绑定被称为阴影,而如果我们明确地不说变量的值是可变的,则设置新值是不可能的。影子与F中的设置值#

有人能详细解释我这个概念吗?什么是由

let var = "new_value" 

和设置新的价值阴影(重新绑定)之间的区别就像

var <- "new_value" 

这是一个时刻,即重新绑定过程中,我们创建另一个对象,我们在分配一个对象的地址变量,而第二个例子我们改变了价值本身?我从堆栈/堆栈理解了内存。我可能是错的。

感谢

回答

9

阴影是,当你创建一个新结合使用相同的名称作为一个以前的绑定。这“隐藏”原始名称,隐藏它,但不会更改或替换它。在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.请注意,您与可变关键字这里覆盖默认不可改变的行为。

你那么内的可变绑定更改值:

bar <- 24 
printfn "%i" bar 
printBar();; 

这将打印24两次,因为与阴影版本,突变改变了原来的绑定。如果您在原始绑定中关闭mutable,则在使用<-时会出现错误。

2

每当我不知道究竟发生了我使用的工具,如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); 
} 

这是更明显的是阴影和设置有什么区别。

影子x创建一个名称为x2的新变量。

设置属于正常赋值。

4

要增加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; 
} 

然而,阴影,因为在这个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#中不能编译,因为它没有阴影在这方面。

阴影的使用是,它可以防止在你不需要的代码块中使用原始变体。所以有一个可以担心的变量。