这些功能完全相同吗?也就是说,第一和第二语法是最后一种语法的简便方法吗?或者是否存在一些理论上或实际上的差异,如果是,那又是什么?
let f1 a b = a + b
let f2 a = (fun b -> a + b)
let f3 = (fun a -> (fun b -> a + b) )
对我来说,它们似乎相同,f1 5
,f2 5
和f3 5
似乎返回相同的值。只是检查我在这里没有做出无效的假设。换句话说,我希望以知识为基础的答案,而不是说“是的,我相信它们是相同的”。
答案 0 :(得分:8)
您的假设是正确的,在这种情况下,功能完全相同。
您可以通过检查生成的IL代码(如Craig所示)来查看,您还可以通过查看F#编译器推断的类型来查看。在这两种情况下,您都会看到int -> int -> int
。 F#语言将其视为一个函数,它接受int
并返回int -> int
,但实际上它被编译为具有多个参数的方法(为了提高效率)。
如果您在fun
之后立即编写let .. =
,则编译器会将其转换为标准函数。但是,如果在返回函数之前进行一些计算,则可以编写有点不同的代码:
let f1 a b = printfn "hi"; a + b
let f2 a = printfn "hi"; (fun b -> a + b)
现在这两个函数非常不同,因为当你给它一个参数时,第二个函数会打印“hi”(然后它返回一个你可以调用的函数):
> 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
编写相同的代码,但第一个命令只会创建一个新函数,第二个命令将打印“hi”并执行添加。
在这种情况下,f2
生成的IL代码将不同。它将是返回函数(类型为FSharpFunc<int, int>
)的函数。 F#显示的类型也不同 - 它将是int -> (int -> int)
而不是int -> int -> int
。您可以以完全相同的方式使用这两种类型的值,但它提示您,当您给它一个参数时,第一种可能会产生一些效果。
答案 1 :(得分:5)
这是f1
的IL:
.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
正如你所看到的,它基本相同,所以是的,它们是相同的。
答案 2 :(得分:5)
两个功能是相同的。
可以将它们视为语法糖let f = fun a -> fun b -> a + b
实际差异很小。 f1
强调函数返回一个值,而f2
返回一个闭包,而闭包又产生一个值。使用f2
在创建组合器方面更具吸引力,例如parser combinators
另一方面,F#中的函数没有相等性,因此f1 5
和f2 5
是不同的值,但它们在相同的输入上产生相同的输出。