2013-05-12 14 views
7

Korzystanie przykład z Chrisa Smitha programowania F # 3.0:Dlaczego funkcja Fsharp Interactive umożliwia przechwytywanie zmiennych zmiennych przez zamknięcia?

let invalidUseOfMutable() = 
    let mutable x = 0 
    let incrementX() = x <- x + 1 
    incrementX() 
    x;; 

To nie zgodnie z oczekiwaniami:

Błąd FS0407: Zmienna zmienne 'x' jest stosowany w nieprawidłowy sposób. Zmiennych sterujących nie można przechwytywać przy zamknięciach.

Teraz wytnij i wklej ciało funkcji w FSharp Interactive:

let mutable x = 0 
let incrementX() = x <- x + 1 
incrementX() 
x;; 

I to działa!

val to: int = 1

Dlaczego?

+0

Cześć, nie generuje zamknięcia po prostu po uruchomieniu kodu ciała.use: let x = ref 0; niech incrementX() = x: =! x + 1; zobacz: Zamknięcia: http: //msdn.microsoft.com/en-us/library/dd233186.aspx – kwingho

+0

Zauważ, że kompilator także nie sprzeciwia się treści działa samodzielnie. –

Odpowiedz

8

Krótka odpowiedź: nie ze względu na fsi, ponieważ zmienna jest globalna.

Długa odpowiedź:

Dla normalnego (non-zmienny) chwytania, realizacja mądry zrobionego wartość jest kopiowana do obiektu funkcji, tak, że jeśli wrócisz tę funkcję i używać go poza zakresem, w którym zostało to zdefiniowane, wszystko działa dobrze.

let pureAddOne() = 
    let x = 1 
    let f y = x + y // the value 1 is copied into the function object 
    f 

let g = pureAddOne() 
g 3 // x is now out of scope, but its value has been copied and can be used 

Z drugiej strony, w celu przechwycenia zmienny, przechwytywanie należy zrobić odniesienie, w przeciwnym razie nie byłby w stanie go zmienić. Ale jest to niemożliwe, ponieważ we wcześniej wspomnianym przypadku, gdy zamknięcie jest zwracane i używane poza zakresem jego definicji, zmienna jest również poza zakresem i potencjalnie deallocated. To jest powód początkowego ograniczenia.

let mutableAddOne() = 
    let mutable x = 1 
    let f y = x <- x + y // x would be referenced, not copied 
    f 

let g = mutableAddOne() 
g 3 // x is now out of scope, so the reference is invalid! 
     // mutableAddOne doesn't compile, because if it did, then this would fail. 

Jeśli jednak zmienna jest globalna, wówczas nie ma problemu z takim zakresem, a kompilator je akceptuje. To nie tylko fsi; Jeśli spróbujesz skompilować następujący program z fsc, to działa:

module Working 

let mutable x = 1 // x is global, so it never goes out of scope 

let mutableAddOne() = 
    let f y = x <- x + y // referencing a global. No problem! 
    f 

let g = mutableAddOne() 
g 3 // works as expected! 

Podsumowując, jak kwingho powiedział, jeśli chcesz mieć zamknięcie, które przechwytuje lokalną wartość zmienny, użyj ref. Są one przydzielane do sterty (w przeciwieństwie do alokowanego w stosie lokalnego mutable), o ile zamknięcie zawiera odniesienie do niego, nie zostanie ono zwolnione.

Powiązane problemy