2010-04-03 9 views
12

En F #:Diferencia en Fa # y Clojure al llamar a funciones redefinidas

> 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 

En 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 

Nótese que en Clojure la versión más reciente de f es llamado en la última línea. En F #, sin embargo, todavía se llama a la versión anterior de f. ¿Por qué es esto y cómo funciona esto?

Respuesta

8

Como dijo Gabe , F # usos interactivos sombra de los valores cuando se introduce una función con un nombre que ya existe (para más información sobre el sombreado, véase por ejemplo this SO question). Esto significa que el F # compilador ve algo como esto cuando se ejecuta el código:

> 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 # utiliza algún nombre revuelto (como @) que no se puede utilizar directamente para distinguir entre las versiones del valor. Por otro lado, el comportamiento de Clojure puede ser mejor entendido como un gran diccionario de funciones. Usando pseudo-sintaxis, algo como esto:

> 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 

Esto debería hacer la distinción bastante clara.

Como nota al margen, hay un posible problema con el enfoque Clojure (al menos para un lenguaje como F #). Puede declarar una función de algún tipo, usarla y luego, el siguiente comando puede cambiar el tipo de la función. Si F # usó el enfoque de Clojure, ¿cómo debería funcionar el siguiente ejemplo?

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

La función g utiliza f como si tuviera dos parámetros de tipo int, pero la línea thrid cambia el tipo de la función. Esto hace que el enfoque de Clojure sea un poco complicado para los lenguajes verificados por tipo.

+0

¿Quieres decir con sombreado que una variable en un ámbito inferior con 'sombras' del mismo nombre los de mayor ¿alcances? En el interactivo F #, ¿debemos leer las declaraciones de let sucesivas como ámbitos anidados? Eso lo explicaría! En el caso de Clojure, aquí no se trata de una cuestión de alcance, sino realmente de cambiar el enlace de raíz de var f (los vars son mutables). –

+0

@Michiel - Sí, eso es exactamente correcto. Usando la sintaxis no liviana para F #, tu ejemplo sería 'let f = ... in (let g = ... in (g 10; let f = ... in g 10))', donde la creación de nuevos ámbitos es un poco más evidente. – kvb

12

En Clojure, el símbolo f captura el nombre f, mientras que en F # el símbolo f captura el valor de f. Entonces en Clojure cada vez que llamas al g busca f para averiguar a qué se refiere el nombre en ese momento, mientras que en F # cada llamada a g usa el valor que f tenía cuando se creó originalmente la función g.

5

Gabe y Tomas han cubierto bien los conceptos básicos. Tenga en cuenta que si quieres F # a comportarse como lo hace Clojure, se puede utilizar una unión mutable y reasignar 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 
Cuestiones relacionadas