2012-10-30 5 views
8

Czy te funkcje są dokładnie takie same? To znaczy, czy 1. i 2. składnia są po prostu wygodnym skrótem dla ostatniej składni? Czy istnieje pewna różnica teoretyczna lub praktyczna, a jeśli tak, to co to jest?Czym różnią się te funkcje?

let f1 a b = a + b

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

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

Wydają się tak samo do mnie, f1 5, f2 5 i f3 5 wydają się powrócić identyczną wartość, na przykład. Tylko sprawdzam, nie robię tutaj błędnego założenia. Innymi słowy, mam nadzieję na odpowiedź opartą na wiedzy, a nie na powiedzeniu "Tak, uważam, że są takie same".

+2

Sprawdź [moja odpowiedź tutaj] (http://stackoverflow.com/questions/2175940/int-int-int-what-does-this-mean-in-f/2176428#2176428), może pomóc w zrozumieniu różnica –

+0

Z odpowiedzi uściśliłem pytanie, aby zawierało trzeci przypadek. – hyde

Odpowiedz

8

Twoje założenie jest poprawne, w tym przypadku funkcje są dokładnie takie same.

Można to zobaczyć, sprawdzając wygenerowany kod IL (jak wykazał Craig) i można to również zobaczyć, patrząc na typ wywnioskowany przez kompilator F #. W obu przypadkach zobaczysz int -> int -> int. Widoki języka F #, które jako funkcja, która pobiera int i zwraca int -> int, ale jest faktycznie skompilowana jako metoda z wieloma argumentami (dla wydajności).

Jeśli napiszesz fun natychmiast po let .. =, kompilator zamieni to w standardową funkcję. Jednakże, można napisać kod, który jest nieco inny, jeśli nie trochę obliczeń przed zwróceniem funkcję:

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

Teraz te dwie funkcje są bardzo różne, ponieważ drugi jeden drukuje „cześć”, gdy dajesz go tylko pojedynczy argument (i potem wraca do funkcji, które można nazwać):

> 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 

można napisać ten sam kod za pomocą f1, ale pierwsza komenda wystarczy utworzyć nową funkcję, a druga komenda print „hi” i uzupełnij. W takim przypadku wygenerowany kod IL dla f2 będzie różny. Będzie to funkcja, która zwraca funkcję (typu FSharpFunc<int, int>). Typ wyświetlany przez F # również jest inny - będzie to int -> (int -> int) zamiast int -> int -> int. Możesz używać wartości tych dwóch typów dokładnie w taki sam sposób, ale wskazuje na to, że pierwszy z nich może zrobić pewne efekty, gdy podasz mu pojedynczy argument.

5

Oto IL dla 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 

... i do 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 

Jak widać, to jest w zasadzie identyczne, więc tak, są takie same.

+0

Czy to naprawdę istotne, że produkują ten sam kod bajtowy dla niektórych przypadków testowych? To znaczy, bez względu na to, w jaki sposób korzystasz z tej funkcji, czy zawsze kończy się ona na wywołaniu tej "prawdziwej" metody w końcu, kiedy w końcu robi coś z funkcją? – hyde

+0

Tak, to robi. Tomas wyjaśnił, dlaczego. –

5

Dwie funkcje są takie same. Można je uważać za cukry syntaktyczne dla

let f = fun a -> fun b -> a + b 
Istnieje niewielka różnica praktyczna. f1 podkreśla, że ​​funkcja zwraca wartość, podczas gdy f2 zwraca zamknięcie, które z kolei generuje wartość. Zastosowanie f2 jest trochę bardziej atrakcyjne w tworzeniu kombinatorów, np. parser combinators.

Na marginesie, nie ma równości w funkcjach w F #, więc f1 5 i f2 5 są różne wartości, ale wytwarzają takie same wyjścia na tych samych wejściach.

+0

+1 Myślę, że jest to przydatna odpowiedź i dobry sposób na zrozumienie F #. Chociaż technicznie rzecz biorąc, są one cukrami syntaktycznymi dla 'let fab = ...', ponieważ F # (i inne języki ML) traktują funkcje zdefiniowane inaczej za pomocą 'let' i' fun' (funkcje zdefiniowane za pomocą 'let' mogą być ogólne i funkcjonalne używanie 'fun' nie może). Ten kod jest traktowany tak, jakby używał 'let f a b = ...' pod warunkiem, że ciało zawiera 'fun' bez żadnego innego kodu. –