2012-10-30 226 views
8

這些功能是否完全一樣?也就是說,第一和第二語法對於最後的語法來說簡便嗎?或者是有一些理論上或實踐上的差異,如果是這樣,它是什麼?這些功能有什麼區別

let f1 a b = a + b

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

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

他們似乎同樣給​​我,f1 5f2 5f3 5似乎返回相同的值,例如。只是檢查我沒有在這裏作出無效的假設。換句話說,我希望以知識爲基礎的答案,而不是一個人說「是的,我相信他們是一樣的」。

+2

檢查[我的答案](http://stackoverflow.com/questions/2175940/int-int-int-what-does-this-mean-in-f/2176428#2176428),可以幫助你瞭解差異 –

+0

我增加了問題以包含來自答案的第三個案例。 – hyde

回答

8

你的假設是正確的,在這種情況下,功能是完全一樣的。

通過檢查生成的IL代碼(由Craig演示),您可以看到通過查看F#編譯器推斷的類型,您可以看到該代碼。在這兩種情況下,您都會看到int -> int -> int。 F#語言將這個函數作爲一個函數,它需要int並返回int -> int,但它實際上被編譯爲一個具有多個參數的方法(以提高效率)。

如果您在let .. =之後立即編寫fun,則編譯器將其轉換爲標準函數。但是,你可以,如果你返回功能之前,做一些運算編寫的代碼是有點不同:

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

現在,這兩個功能有很大的不同,因爲第二個打印「喜」當你給它只是一個一個參數(然後返回,你可以調用一個函數):

> 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 

您可以編寫使用f1相同的代碼,但是第一個命令將剛剛創建一個新功能,第二個命令將打印「喜」並進行添加。

在這種情況下,生成的f2的IL代碼將有所不同。這將是一個函數,返回函數(類型FSharpFunc<int, int>)。 F#顯示的類型也不同 - 它將是int -> (int -> int)而不是int -> int -> int。你可以用完全相同的方式使用這兩種類型的值,但它暗示你第一個可能會給你一個單一的參數。

5

這裏的IL爲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 

...和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 

正如你所看到的,它本質上是相同的,所以是的,它們是相同的。

+0

他們爲某些測試用例生成相同的字節碼是否真的相關?也就是說,不管你如何使用這個函數,它最終總是最終調用這個「真正的」方法,當它最終對函數做些什麼的時候呢? – hyde

+0

是的,它的確如此。 Tomas解釋了爲什麼。 –

5

兩個功能是一樣的。他們可以被認爲是語法糖爲

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

有實用小的差異。f1強調該函數返回一個值,而f2返回一個閉包,該閉包轉而產生一個值。在創建組合器時,使用f2更有吸引力。 parser combinators

在附註中,F#中的函數沒有相同性,所以f1 5f2 5是不同的值,但它們在相同的輸入上產生相同的輸出。

+0

+1我認爲這是一個有用的答案,也是理解F#的好方法。儘管從技術上講,它們都是'let fab = ...'的語法糖,因爲F#(和其他ML語言)不同地處理使用'let'和'fun'定義的函數(用'let'定義的函數可以是通用的並且函數使用'fun'不能)。只要包含'fun'而沒有任何其他代碼,這段代碼就好像它正在使用'let f a b = ...'一樣。 –