【问题标题】:What is the difference between these functions这些功能有什么区别
【发布时间】:2012-10-30 22:12:28
【问题描述】:

这些功能完全一样吗?也就是说,第一种和第二种语法只是最后一种语法的方便简写吗?还是有一些理论上或实践上的区别,如果有,是什么?

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 似乎返回相同的值。只是检查我没有在这里做出无效的假设。换句话说,我希望得到一个基于知识的答案,而不是说“是的,我相信它们是一样的”。

【问题讨论】:

  • 检查my answer here,可以帮助您了解差异
  • 我扩充了问题以包含答案中的第三种情况。

标签: function f# currying


【解决方案1】:

您的假设是正确的,在这种情况下,功能完全相同。

您可以通过检查生成的 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)

现在这两个函数非常不同,因为第二个函数在你只给它一个参数时会打印“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。您可以以完全相同的方式使用这两种类型的值,但它暗示第一种在您给它一个参数时可能会产生一些效果。

【讨论】:

    【解决方案2】:

    这是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
    

    如您所见,它本质上是相同的,所以是的,它们是相同的。

    【讨论】:

    • 它们为某些测试用例生成相同的字节码真的相关吗?也就是说,无论你如何使用该函数,当它最终对函数进行某些操作时,它是否总是最终调用那个“真正的”方法?
    • 是的,它确实做到了。托马斯解释了原因。
    【解决方案3】:

    两个函数是一样的。它们可以被认为是语法糖

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

    实际差别很小。 f1 强调函数返回一个值,而f2 返回一个闭包,而闭包又会产生一个值。 f2 的使用在创建组合器时更具吸引力,例如parser combinators.

    附带说明,F# 中的函数不相等,因此 f1 5f2 5 是不同的值,但它们在相同的输入上产生相同的输出。

    【讨论】:

    • +1 我认为这是一个有用的答案,也是理解 F# 的好方法。尽管从技术上讲,它们都是 let f a b = ... 的语法糖,因为 F#(和其他 ML 语言)以不同的方式处理使用 letfun 定义的函数(使用 let 定义的函数可以是通用的,而使用 fun 的函数不能)。只要正文包含 fun 而没有任何其他代码,此代码就会被视为使用 let f a b = ...
    猜你喜欢
    • 2014-02-12
    • 1970-01-01
    • 2020-06-28
    • 1970-01-01
    • 2011-01-10
    • 2013-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多