2012-10-30 172 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 = ...'一样。 –