2016-09-22 34 views
7

Zostałem wprowadzony, że dane są domyślnie niezmienne w F #. Kiedy przypisujemy wartość do jakiejś zmiennej, tak naprawdę dzieje się tak, że ponownie wiąże wartość zmiennej, ale ustawienie nowej wartości jest czymś innym. Ponowne wiązanie nazywa się Shadowing, podczas gdy ustawienie nowej wartości jest niemożliwe, jeśli wyraźnie nie powiemy, że wartość zmiennej jest zmienna.Shadowing vs. Ustawianie wartości w F #

Czy ktoś może wyjaśnić mi tę koncepcję w szczegółach? jaka jest różnica między shadowing (ponowne wiązanie) przez

let var = "new_value" 

i ustawienie nowej wartości jak

var <- "new_value" 

Jest to moment, że podczas ponownego wiązania tworzymy inny obiekt i przypisujemy adres tego obiektu do zmiennej natomiast w drugi przykład zmieniamy samą wartość? Przyniosłem to ze zrozumienia pamięci/stosu na stosie ... Mogę się mylić.

Dzięki

Odpowiedz

9

Cieniowanie jest podczas tworzenia nowegowiążący, który wykorzystuje tę samą nazwę co poprzedni wiążące. To "cieniowanie" oryginalnej nazwy, która ją ukrywa, ale jej nie zmienia ani nie zastępuje. Spróbuj tego w FSI zobaczyć:

let foo = 42 

let printFoo() = 
    printfn "%i" foo 

printFoo() ;; 

ten wypisze:

42 

val foo : int = 42 
val printFoo : unit -> unit 
val it : unit =() 

Następnie dodać:

// ... more code 
let foo = 24 

printfn "%i" foo // prints 24 
printFoo();; 

ten wypisze:

24 
42 

val foo : int = 24 
val it : unit =() 

pamiętać, że nadal drukuje 42 po wywołaniu printFoo() - funkcja widzi pierwotne (bez cieniowania) powiązanie, ale nowy wydruk pokazuje nową wartość.

Korzystanie <- mutować wartość wymaga zmienny wiązanie:

let mutable bar = 42 

let printBar() = 
    printfn "%i" bar 

printBar();; 

ta, podobnie jak powyżej, druki 42. Zauważ, że nadpisać domyślne zachowanie niezmiennej tu z zmienny hasła.

Następnie zmień wartość w obrębie zmienny wiązanie:

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

To będzie drukować 24 razy, ponieważ, w przeciwieństwie do wersji zacienionej, mutacja zmienia oryginalne wiązania. Jeśli pozostawisz mutable wyłączone w oryginalnym powiązaniu, pojawi się błąd podczas korzystania z <-.

2

Ilekroć zastanawiam się, co faktycznie dzieje się używać narzędzi takich jak ILSpy

Na przykład:

let f() = 
    let x = Dictionary<int, string>() 
    let mutable x = ResizeArray<int> 16 
    x <- ResizeArray<int> 16 

Korzystanie ILSpy dekompilować go w kod C# staje się:

public static void f() 
{ 
    Dictionary<int, string> x = new Dictionary<int, string>(); 
    List<int> x2 = new List<int>(16); 
    x2 = new List<int>(16); 
} 

Tutaj to bardziej oczywiste jaka jest różnica między cieniem a ustawianiem.

Shadowing x tworzy nową zmienną o nazwie x2.

Ustawienie to normalne przyporządkowanie.

4

Aby dodać doskonałą odpowiedź Reed Copsey, jeśli piszesz pętlę, w której zmienisz wartość jakiegoś akumulatora, ustawisz oryginalną wartość jako mutable. Na przykład możesz to zrobić.

let mutable acc = 0 // declaration 
for i in 1..100 do 
    acc <- acc + i // assignment 

To jest mniej więcej odpowiednikiem kodu C#:

var acc = 0; 
for (int i = 1; i <= 100; i++) 
{ 
    acc = acc + i; // assignment 
    // Another way to write this: 
    // acc += i; 
} 

Jednak w Shadowing, jak w tym F # snippet:

let acc = 0 // declaration 
for i in 1..100 do 
    let acc = acc + i // another declaration only within the loop 

Nie jesteś rzeczywiście robi nic przydatny!! Druga deklaracja ma zakres tylko w pętli for i nie zmienia wartości oryginalnego acc.

A rough C# równoważne byłoby to:

var acc = 0; // declaration 
for (int i = 1; i <= 100; i++) 
{ 
    var acc2 = acc + i; // another declaration only within the loop 
} 

Należy pamiętać, że stosując acc (zamiast acc2) dla zmiennej wewnątrz nie będzie kompilować w C#, ponieważ nie mają Shadowing w tym kontekście.

Stosowanie cieniowania polega na tym, że uniemożliwia korzystanie z oryginalnego wariantu w bloku kodu, gdy nie jest on potrzebny. A więc jest jedna mniejsza zmienna, o którą trzeba się martwić.