2012-10-30 48 views
8

Queste funzioni sono esattamente le stesse? Cioè, la 1a e la 2a sintassi sono solo una comoda abbreviazione per l'ultima sintassi? O c'è qualche differenza teorica o pratica, e se sì, che cos'è?Qual è la differenza tra queste funzioni

let f1 a b = a + b

let f2 a = (fun b -> a + b)

let f3 = (fun a -> (fun b -> a + b))

Essi sembrano lo stesso per me, f1 5, f2 5 e f3 5 sembrano restituire valore identico, per esempio. Sto controllando che non sto facendo ipotesi errate qui. In altre parole, spero che una risposta basata sulla conoscenza, non uno che dice "Sì, credo che siano gli stessi".

+2

Controllare [la mia risposta qui] (http://stackoverflow.com/questions/2175940/int-int-int-what-does-this-mean-in-f/2176428#2176428), potrebbe aiutare a capire il differenza –

+0

Ho aumentato la domanda per contenere il terzo caso da una risposta. – hyde

risposta

8

L'ipotesi è corretta, in questo caso le funzioni sono identiche.

Si può vedere che ispezionando il codice IL generato (come dimostrato da Craig) e si può anche vedere che guardando il tipo inferito dal compilatore F #. In entrambi i casi, vedrai . Il linguaggio F # visualizza tale funzione come una funzione che prende int e restituisce int -> int ma in realtà viene compilata come metodo con più argomenti (per l'efficienza).

Se si scrive fun immediatamente dopo let .. =, il compilatore lo trasforma in una funzione standard. Tuttavia, è possibile scrivere codice che è un po 'diverso se si fa qualche calcolo prima di tornare alla funzione:

let f1 a b = printfn "hi"; a + b 
let f2 a = printfn "hi"; (fun b -> a + b) 

Ora le due funzioni sono molto diversi, in quanto il secondo stampe "Hi" quando si danno solo un singolo argomento (e quindi restituisce una funzione che si può chiamare):

> let f = f2 1;; 
hi      // The body is called, prints 
val f : (int -> int) // and returns function 

> f 2;;     // This runs the body of 'fun' 
val it : int = 3  // which performs the additiion 

è possibile scrivere lo stesso codice utilizzando f1, ma il primo comando sarà solo creare una nuova funzione e il secondo comando stamperà "hi" e fare l'aggiunta.

In questo caso, il codice IL generato per f2 sarà diverso. Sarà una funzione che restituisce una funzione (di tipo FSharpFunc<int, int>). Anche il tipo visualizzato da F # è diverso: sarà int -> (int -> int) anziché int -> int -> int. Puoi usare i valori di questi due tipi esattamente nello stesso modo, ma ti suggerisce che il primo potrebbe fare alcuni effetti quando gli dai un singolo argomento.

5

Ecco il IL per f1:

.method public static int32 f1(int32 a, 
           int32 b) cil managed 
{ 
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = (01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00) 
    // Code size  5 (0x5) 
    .maxstack 4 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: add 
    IL_0004: ret 
} // end of method Program::f1 

... e per f2:

.method public static int32 f2(int32 a, 
           int32 b) cil managed 
{ 
    .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationArgumentCountsAttribute::.ctor(int32[]) = (01 00 02 00 00 00 01 00 00 00 01 00 00 00 00 00) 
    // Code size  5 (0x5) 
    .maxstack 4 
    IL_0000: nop 
    IL_0001: ldarg.0 
    IL_0002: ldarg.1 
    IL_0003: add 
    IL_0004: ret 
} // end of method Program::f2 

Come si può vedere, è sostanzialmente identica, quindi sì, sono la stessa cosa.

+0

È davvero rilevante che producano lo stesso codice byte per alcuni casi di test? Cioè, non importa come usi la funzione, finisce sempre per chiamare quel metodo "reale" alla fine quando finalmente fa qualcosa con la funzione? – hyde

+0

Sì, lo fa. Tomas ha spiegato perché. –

5

Due funzioni sono le stesse. Potrebbero essere considerati zuccheri sintattici per

let f = fun a -> fun b -> a + b 

C'è una piccola differenza pratica.f1 sottolinea che la funzione restituisce un valore mentre f2 restituisce una chiusura, che a sua volta produce un valore. L'uso di f2 è un po 'più attraente nella creazione di combinatori, ad es. parser combinators.

Su una nota a margine, non c'è uguaglianza sulle funzioni in F #, quindi f1 5 e f2 5 sono valori diversi ma producono le stesse uscite sugli stessi ingressi.

+0

+1 Penso che questa sia una risposta utile e un buon modo per capire F #. Sebbene tecnicamente parlando, sono entrambi zuccheri sintattici per 'let fab = ...' perché F # (e altri linguaggi ML) trattano le funzioni definite usando 'let' e' fun' in modo diverso (le funzioni definite usando 'let' possono essere generiche e funzioni usare 'fun' non può). Questo codice viene trattato come se usasse 'let f a b = ..." a patto che il corpo contenga 'fun' senza alcun altro codice. –

Problemi correlati