2012-10-30 8 views
8

Ces fonctions sont-elles exactement les mêmes? Autrement dit, la première et la deuxième syntaxe sont-elles des raccourcis pratiques pour la dernière syntaxe? Ou y a-t-il une différence théorique ou pratique, et si oui, de quoi s'agit-il?Quelle est la différence entre ces fonctions

let f1 a b = a + b

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

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

Ils semblent la même chose pour moi, f1 5, f2 5 et f3 5 semblent revenir valeur identique, par exemple. Je vérifie juste que je ne fais pas d'hypothèse invalide ici. En d'autres termes, j'espère une réponse basée sur la connaissance, pas un qui dise "Ouais, je crois qu'ils sont les mêmes".

+2

Cochez [ma réponse ici] (http://stackoverflow.com/questions/2175940/int-int-int-what-does-this-mean-in/2176428#2176428), pourrait vous aider à comprendre le différence –

+0

J'ai augmenté la question pour contenir le 3ème cas d'une réponse. – hyde

Répondre

8

Votre hypothèse est correcte, dans ce cas, les fonctions sont exactement les mêmes.

Vous pouvez voir cela en inspectant le code IL généré (comme démontré par Craig) et vous pouvez également voir cela en regardant le type déduit par le compilateur F #. Dans les deux cas, vous verrez int -> int -> int. Le langage F # considère que comme une fonction qui prend int et renvoie int -> int mais il est en fait compilé comme une méthode avec plusieurs arguments (pour l'efficacité).

Si vous écrivez fun immédiatement après let .. =, le compilateur devient alors une fonction standard. Cependant, vous pouvez écrire du code qui est un peu différent si vous faites quelques calculs avant de retourner la fonction:

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

Maintenant, les deux fonctions sont très différentes, car la deuxième imprime « salut » quand vous lui donnez juste un seul argument (et il retourne une fonction que vous pouvez appeler):

> 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 

vous pouvez écrire le même code en utilisant f1, mais la première commande va simplement créer une nouvelle fonction et la deuxième commande imprimera « salut » et faites l'addition.

Dans ce cas, le code IL généré pour f2 sera différent. Ce sera une fonction qui retourne une fonction (de type FSharpFunc<int, int>). Le type affiché par F # est également différent - il sera int -> (int -> int) au lieu de int -> int -> int. Vous pouvez utiliser les valeurs de ces deux types exactement de la même manière, mais cela vous indique que le premier peut faire des effets lorsque vous lui donnez un seul argument.

5

est ici l'IL pour 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 

... et pour 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 

Comme vous pouvez le voir, il est essentiellement identique, donc oui, ils sont les mêmes.

+0

Est-il vraiment pertinent qu'ils produisent le même code d'octet pour certains cas de test? C'est-à-dire, peu importe comment vous utilisez la fonction, finit-elle toujours par appeler cette méthode "réelle" à la fin quand elle fait finalement quelque chose avec la fonction? – hyde

+0

Oui, il le fait. Tomas a expliqué pourquoi. –

5

Deux fonctions sont identiques. Ils peuvent être considérés comme des sucres syntactiques pour

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

Il existe une petite différence pratique.f1 souligne que la fonction renvoie une valeur tandis que f2 renvoie une fermeture, qui à son tour produit une valeur. L'utilisation de f2 est un peu plus attrayante dans la création de combinateurs, par ex. parser combinators. Sur une note de côté, il n'y a pas d'égalité sur les fonctions dans F # donc f1 5 et f2 5 sont des valeurs différentes, mais elles produisent les mêmes sorties sur les mêmes entrées.

+0

+1 Je pense que c'est une réponse utile et un bon moyen de comprendre F #. Bien que techniquement parlant, ils sont tous deux des sucres syntaxiques pour 'let fab = ...' parce que F # (et d'autres langages ML) traitent les fonctions définies en utilisant 'let' et' fun' différemment (les fonctions définies en utilisant 'let' peuvent être génériques et fonctions en utilisant 'fun' ne peut pas). Ce code est traité comme s'il utilisait 'let f a b = ...' tant que le corps contient 'fun' sans aucun autre code. –

Questions connexes