2010-04-03 10 views
12

W F #:Różnica w F # i Clojure Dzwoniąc redefinicji funkcji

> let f x = x + 2;; 

val f : int -> int 

> let g x = f x;; 

val g : int -> int 

> g 10;; 
val it : int = 12 
> let f x = x + 3;; 

val f : int -> int 

> g 10;; 
val it : int = 12 

W Clojure:

1:1 user=> (defn f [x] (+ x 2)) 
#'user/f 
1:2 user=> (defn g [x] (f x)) 
#'user/g 
1:3 user=> (g 10) 
12 
1:4 user=> (defn f [x] (+ x 3)) 
#'user/f 
1:5 user=> (g 10) 
13 

pamiętać, że w Clojure najnowsza wersja F jest wywoływana w ostatnim wierszu. W języku F # wciąż jednak nazywa się starą wersję f. Dlaczego tak jest i jak to działa?

Odpowiedz

8

Jak Gabe powiedział, F # interaktywne zastosowania shadowing wartości po wprowadzeniu funkcji z nazwą, która już istnieje (więcej informacji na temat cieniowania, patrz na przykład this SO question). Oznacza to, że F # kompilator widzi coś takiego po uruchomieniu kodu:

> let [email protected] x = x + 2;; 
> let [email protected] x = [email protected] x;; 
> [email protected] 10;; 
val it : int = 12 
> let [email protected] x = x + 3;; 
> [email protected] 10;; 
val it : int = 12 

F # używa niektórych zniekształcone nazwy (jak @), że nie można użyć bezpośrednio do rozróżnienia między wersjami wartości. Z drugiej strony zachowanie Clojure'a można prawdopodobnie najlepiej rozumieć jako duży słownik funkcji. Przy użyciu pseudo-składni, coś takiego:

> symbols[f] = fun x -> x + 2;; 
> symbols[g] = fun x -> symbols[f] x;; 
> symbols[g] 10;; 
val it : int = 12 
> symbols[f] = fun x -> x + 3;; 
> symbols[g] 10;; 
val it : int = 13 

Powinno to uczynić rozróżnienie dość jasne.

Na marginesie, istnieje jeden możliwy problem z podejściem Clojure (przynajmniej dla języka takiego jak F #). Możesz zadeklarować funkcję jakiegoś typu, użyć jej, a następnie następne polecenie może zmienić typ funkcji. Jeśli F # używał podejścia Clojure, jak powinien działać poniższy przykład?

> let f a b = a + b;; 
> let g x = f x x;; 
> let f() = printf "f!";; 
> g 0;; 

Funkcja g wykorzystuje f jak gdyby dwa parametry typu int, ale Thrid linia zmienia się rodzaj funkcji. To sprawia, że ​​podejście Clojure jest nieco trudne dla języków sprawdzanych przez typ.

+0

Czy chodziło Ci o cieniowanie, że zmienna w niższym zakresie o tej samej nazwie „Cienie” tych z wyższym zakresy? Czy w interaktywnym F # powinniśmy czytać kolejne wyrażenia let jako zagnieżdżone zakresy? To by to tłumaczyło! W przypadku Clojure nie jest to kwestia zakresu, ale tak naprawdę zmiana powiązania z korzenia var f (vars są zmienne). –

+0

@Michiel - Tak, dokładnie tak. Używając nielekkiej składni dla F #, twoim przykładem będzie "let f = ... in (niech g = ... in (g 10, let f = ... in g 10))", gdzie tworzenie nowe zakresy są nieco bardziej widoczne. – kvb

12

W Clojure f symbol rejestruje nazwę f, natomiast w F # f symbol rejestruje wartość f. Tak więc w Clojure za każdym razem, gdy wywołujesz g, wyszukuje nazwę, do której odnosi się w danym momencie, podczas gdy w F # każde połączenie z g używa wartości, która miała f, gdy pierwotnie utworzono funkcję g.

5

Gabe i Tomas dobrze opisali podstawy. Zauważ, że jeśli chcą F # zachowywać się podobnie jak Clojure, można użyć zmienny wiązanie i przypisanie f:

let mutable f = fun x -> x + 2 
let g x = f x 

g 10;; // 12 

f <- fun x -> x + 3 // note, assign new value, don't create new binding 

g 10;; //13