3

可能重复:
函数式编程:柯里化

我在这里阅读免费的 F# Wikibook:

http://en.wikibooks.org/wiki/F_Sharp_Programming

有一节解释什么是偏函数。它说使用 F# 你可以部分使用一个函数,但我就是不明白发生了什么。考虑以下用作示例的代码片段:

#light
open System

let addTwoNumbers x y = x + y
let add5ToNumber = addTwoNumbers 5

Console.WriteLine(add5ToNumber 6)

输出是 11。但我没有关注。我的函数“add5ToNumber”不要求提供参数,那么为什么我可以调用它并给它一个参数?

这些天我真的很喜欢学习 F#,宝贝步骤!

4

5 回答 5

3

咖喱是这样的:

addTwoNumbers是一个接受一个数字并返回一个接受一个数字并返回一个数字的函数。

事实上addTwoNumbers 5,一个接受一个数字并返回一个数字的函数也是如此,这就是柯里化的工作原理。由于您分配addTwoNumbers 5add5ToNumber,这使得add5ToNumber一个接受数字的函数返回一个数字。

我不知道 F# 中的类型定义是什么样的,但在 Haskell 中,函数的类型定义清楚地表明了这一点:

addTwoNumbers :: (Num a) => a -> a -> a

另一方面,如果你写addTwonumbers了一个二元组,

addTwoNumbers :: (Num a) => (a, a) -> a

then is 将是一个接受两个元组并返回一个数字的函数,因此add5ToNumber无法按照您的方式创建。

于 2010-11-08T13:40:57.593 回答
3

基本上,F# 中的每个函数都有一个参数并返回一个值。该值可以是 unit 类型,由 () 指定,这在概念上类似于某些其他语言中的 void。

When you have a function that appears to have more than one parameter, F# treats it as several functions, each with one parameter, that are then "curried" to come up with the result you want. So, in your example, you have:

let addTwoNumbers x y = x + y

That is really two different functions. One takes x and creates a new function that will add the value of x to the value of the new function's parameter. The new function takes the parameter y and returns an integer result.

So, addTwoNumbers 5 6 would indeed return 11. But, addTwoNumbers 5 is also syntactically valid and would return a function that adds 5 to its parameter. That is why add5ToNumber 6 is valid.

于 2010-11-08T13:48:50.287 回答
2

只是为了添加其他答案,当您对函数进行 curry 时,会在引擎盖下返回一个闭包。

[Serializable]
internal class addToFive@12 : FSharpFunc<int, int>
{
    // Fields
    [DebuggerBrowsable(DebuggerBrowsableState.Never), CompilerGenerated, DebuggerNonUserCode]
    public int x;

    // Methods
    internal addToFive@12(int x)
    {
        this.x = x;
    }

    public override int Invoke(int y)
    {
        return Lexer.add(this.x, y);
    }
}
于 2010-11-08T13:47:24.893 回答
1

这被称为eta-expansion:在函数式语言中,

let f a = g a

相当于

let f = g

这在数学上是有意义的:如果两个函数对于每个输入都相等,那么它们是相等的。

在您的示例g 中,addTwoNumbers 5您编写的代码完全等同于:

let add5toNumber y = addTwoNumbers 5 y

有几种不同的情况:

  • 在某些情况下,如果您省略它,类型系统可能无法识别y为普遍量化的。
  • 如果addTwoNumbers 5(仅使用一个参数)有副作用(例如将 5 打印到控制台),则 eta 扩展版本将在每次调用时打印 5,而 eta 缩减版本将在定义时打印它。如果addTwoNumbers 5涉及只能执行一次的大量计算,这也可能会产生性能影响。
  • Eta-reduction 对标签和可选参数不是很友好(但它们在 F# 中不存在,所以没关系)。

当然,除非您的新函数名称非常易读,否则提供省略参数的名称对读者来说总是有很大帮助。

于 2010-11-08T13:48:28.220 回答
0

addTwoNumbers接受 2 个参数 (xy)。

add5ToNumber分配给addTwoNumbers仅使用 1 个参数调用的输出,这会导致另一个函数“保存”第一个参数 ( x -> 5) 并接受另一个参数 ( y)。

当您将 6add5ToNumber传递给 时,它会将保存的x(5) 和给定的y(6)传递给addTwoNumbers,从而得到 11

于 2010-11-08T13:40:49.580 回答