2010-04-03 13 views
12

In F #:Differenza di F # e Clojure quando si chiama funzioni ridefinite

> 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 

In 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 

Si noti che in Clojure la versione più recente di F viene chiamato nell'ultima riga. In F #, tuttavia, viene ancora chiamata la vecchia versione di f. Perché questo e come funziona?

risposta

8

Come Gabe detto, F # usi interattive shadowing di valori quando si entra in una funzione con un nome già esistente (per più informazioni pedinamento, si veda per esempio this SO question). Ciò significa che il compilatore F # vede qualcosa di simile quando si esegue il codice:

> 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 # usa un nome storpiato (come @) che non è possibile utilizzare direttamente per distinguere tra le versioni di valore. D'altra parte, il comportamento di Clojure può essere probabilmente meglio compreso come un grande dizionario di funzioni. Utilizzando pseudo-sintassi, qualcosa di simile:

> 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 

Questo dovrebbe fare la distinzione del tutto chiaro.

Come nota a margine, c'è un possibile problema con l'approccio Clojure (almeno per un linguaggio come F #). Puoi dichiarare una funzione di qualche tipo, usarla e poi, il comando successivo può cambiare il tipo della funzione. Se F # ha utilizzato l'approccio Clojure, come dovrebbe funzionare il seguente esempio?

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

La funzione g utilizza f come se avesse due parametri di tipo int, ma la linea thrid cambia il tipo di funzione. Ciò rende l'approccio Clojure un po 'complicato per i linguaggi con controllo del testo.

+0

Vuoi dire con shadowing che una variabile in un ambito inferiore con lo stesso nome 'ombre' quelli di più alto Scopes? Nell'interfaccia F #, dovremmo leggere le istruzioni let successive come ambiti nidificati? Questo lo spiegherebbe! Nel caso Clojure non è una questione di ambito qui, ma in realtà cambia il legame di radice della var f (i var sono mutabili). –

+0

@Michiel - Sì, è esattamente giusto. Usando la sintassi non-leggera per F #, il tuo esempio sarebbe 'let f = ... in (let g = ... in (g 10; let f = ... in g 10))', dove la creazione di i nuovi ambiti sono un po 'più evidenti. – kvb

12

In Clojure il simbolo f acquisisce il nome f, mentre in F # il simbolo f acquisisce il valore di f. Quindi in Clojure ogni volta che chiami lo g, cerca f per scoprire a cosa si riferisce il nome in quel momento, mentre in F # ogni chiamata a g utilizza il valore che aveva f quando la funzione g era stata originariamente creata.

5

Gabe e Tomas hanno coperto bene le nozioni di base. Si noti che se si vuole F # a comportarsi come fa Clojure, è possibile utilizzare un legame mutevole e riassegnare 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 
Problemi correlati