2012-10-30 8 views
8

Sind diese Funktionen genau gleich? Das heißt, sind die 1. und 2. Syntax nur bequeme Abkürzungen für die letzte Syntax? Oder gibt es einen theoretischen oder praktischen Unterschied, und wenn ja, was ist das?Was ist der Unterschied zwischen diesen Funktionen

let f1 a b = a + b

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

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

Sie scheinen mir gleich, f1 5, f2 5 und f3 5 scheinen identisch Wert zurückzukehren, zum Beispiel. Ich überprüfe nur, dass ich hier keine ungültige Annahme mache. Mit anderen Worten, ich hoffe eine wissensbasierte Antwort, nicht eine, die sagt "Ja, ich glaube, sie sind die gleichen".

+2

Überprüfen Sie [meine Antwort hier] (http://stackoverflow.com/questions/2175940/int-int-int-what-does-this-mean-in-f/2176428#2176428), könnte Ihnen helfen, das zu verstehen Unterschied –

+0

Ich erweiterte die Frage, um 3. Fall von einer Antwort zu enthalten. – hyde

Antwort

8

Ihre Annahme ist richtig, in diesem Fall sind die Funktionen genau gleich.

Das können Sie sehen, indem Sie den generierten IL-Code untersuchen (wie von Craig gezeigt), und Sie können das auch sehen, wenn Sie sich den vom F # -Compiler abgeleiteten Typ ansehen. In beiden Fällen sehen Sie int -> int -> int. Die F # -Sprache sieht das als eine Funktion an, die int nimmt und int -> int zurückgibt, aber sie wird tatsächlich als eine Methode mit mehreren Argumenten (für Effizienz) kompiliert.

Wenn Sie fun sofort nach let .. = schreiben, verwandelt der Compiler das in eine Standardfunktion. Allerdings können Sie Code schreiben, der ein bisschen anders ist, wenn Sie vor der Rückkehr in die Funktion eine Berechnung tun:

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

Nun werden die beiden Funktionen sind sehr unterschiedlich, weil die zweite druckt „hallo“, wenn Sie es nur geben ein Argument (und dann gibt er eine Funktion, die Sie aufrufen können):

> 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 

Sie können den gleichen Code schreiben f1 verwenden, aber der erste Befehl wird nur eine neue Funktion erstellen und der zweite Befehl druckt „hallo“ und mach die Addition. In diesem Fall wird der generierte IL-Code für f2 unterschiedlich sein. Es wird eine Funktion sein, die eine Funktion (vom Typ FSharpFunc<int, int>) zurückgibt. Der von F # angezeigte Typ ist auch anders - es wird int -> (int -> int) statt int -> int -> int sein. Sie können die Werte dieser beiden Typen auf die gleiche Weise verwenden, aber es weist darauf hin, dass der erste einige Auswirkungen hat, wenn Sie ihm ein einzelnes Argument geben.

5

Hier ist die IL für 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 

... und für 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 

Wie Sie sehen können, ist es im Wesentlichen identisch, also ja, sie gleich sind.

+0

Ist es wirklich relevant, dass sie denselben Byte-Code für einen Testfall erzeugen? Das heißt, egal wie Sie die Funktion verwenden, ruft sie am Ende immer dann diese "echte" Methode auf, wenn sie endlich etwas mit der Funktion macht? – hyde

+0

Ja, das tut es. Tomas erklärte warum. –

5

Zwei Funktionen sind gleich. Sie könnten als syntaktische Zucker für

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

Es gibt kleine praktische Unterschied.f1 betont, dass die Funktion einen Wert zurückgibt, während f2 einen Abschluss zurückgibt, der wiederum einen Wert erzeugt. Die Verwendung von f2 ist ein wenig attraktiver beim Erstellen von Kombinatoren, z. parser combinators.

Nebenbei bemerkt gibt es keine Gleichheit bei den Funktionen in F #, also sind f1 5 und f2 5 unterschiedliche Werte, aber sie erzeugen die gleichen Ausgänge an den gleichen Eingängen.

+0

+1 Ich denke, das ist eine nützliche Antwort und eine gute Möglichkeit, F # zu verstehen. Obwohl beide technisch gesehen syntaktische Zucker für 'let fab = ...' sind, behandeln F # (und andere ML-Sprachen) Funktionen, die mit 'let' und' fun' definiert sind (Funktionen, die mit 'let' definiert sind, können generisch sein und funktionieren mit 'Spaß' kann nicht). Dieser Code wird so behandelt, als würde er 'let f a b = ...' verwenden, solange der Body 'fun' ohne irgendeinen anderen Code enthält. –