F# 中什么是柯里化?

发布于 2024-10-01 07:54:17 字数 664 浏览 6 评论 0原文

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

我正在这里阅读免费的 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#,一步一步!

Possible Duplicate:
Functional programming: currying

I'm reading the free F# Wikibook here:

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

There's a section explaining what Partial Functions are. It says that using F# you can partially use a function, but I just can't understand what's going on. Consider the following code snippet that is used an example:

#light
open System

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

Console.WriteLine(add5ToNumber 6)

The ouput is 11. But I'm not following. My function 'add5ToNumber' doesn't ask for a paramter so why can I invoke it and give it it one?

I really like learning about F# these days, baby steps!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

内心旳酸楚 2024-10-08 07:54:17

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

当您的函数似乎具有多个参数时,F# 会将其视为多个函数,每个函数都有一个参数,然后对这些函数进行“柯里化”以得出您想要的结果。因此,在您的示例中,您有:

let addTwoNumbers x y = x + y

这实际上是两个不同的函数。我们采用 x 并创建一个新函数,该函数会将 x 的值添加到新函数参数的值中。新函数采用参数 y 并返回整数结果。

因此,addTwoNumbers 5 6 确实会返回 11。但是,addTwoNumbers 5 在语法上也是有效的,并且会返回一个将 5 添加到其参数的函数。这就是 add5ToNumber 6 有效的原因。

Basically, every function in F# has one parameter and returns one value. That value can be of type unit, designated by (), which is similar in concept to void in some other languages.

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.

飘然心甜 2024-10-08 07:54:17

柯里化是这样的:

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

所以 addTwoNumbers 5 实际上是一个接受一个数字并返回一个数字的函数,这就是柯里化的工作原理。由于您将 addTwoNumbers 5 分配给 add5ToNumber,这使得 add5ToNumber 成为一个接受数字并返回数字的函数。

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

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

另一方面,如果您编写 addTwonumbers 来获取两个元组,

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

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

Currying is something like this:

addTwoNumbers is a function that takes a number and returns a function that takes a number that returns a number.

So addTwoNumbers 5 is in fact a function that takes a number and returns a number, which is how currying works. Since you assign addTwoNumbers 5 to add5ToNumber, that make add5ToNumber a function that takes a number an returns a number.

I don't know what type definition looks like in F# but in Haskell, the type definition of functions makes this clear:

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

On the other hand, if you wrote addTwonumbers to take a two tuple,

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

then is would be a function that takes a two tuple and returns a number, so add5ToNumber would not be able to be created as you have it.

拧巴小姐 2024-10-08 07:54:17

只是为了添加其他答案,当您柯里化该函数时,在幕后会返回一个闭包。

[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);
    }
}

Just to add to the other answers, underneath the hood a closure is returned when you curry the function.

[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);
    }
}
天荒地未老 2024-10-08 07:54:17

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

let f a = g a

相当于

let f = g

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

在您的示例中,gaddTwoNumbers 5 并且您编写的代码完全等同于:

let add5toNumber y = addTwoNumbers 5 y

在某些情况下它们是不同的:

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

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

This is known as eta-expansion : in a functional language,

let f a = g a

Is equivalent to

let f = g

This makes mathematical sense : if the two functions are equal for every input, then they're equal.

In your example, g is addTwoNumbers 5 and the code you wrote is entirely equivalent to:

let add5toNumber y = addTwoNumbers 5 y

There are a few situations where they are different:

  • In some situations, the type system may not recognize y as universally quantified if you omit it.
  • If addTwoNumbers 5 (with one parameter only) has a side-effect (such as printing 5 to the console) then the eta-expanded version would print 5 every time it's called while the eta-reduced version would print it when it's defined. This may also have performance consequences, if addTwoNumbers 5 involved heavy calculations that can be done only once.
  • Eta-reduction is not very friendly to labels and optional arguments (but they don't exist in F#, so that's fine).

And, of course, unless your new function name is extremely readable, providing the names of the omitted arguments is always a great help for the reader.

上课铃就是安魂曲 2024-10-08 07:54:17

addTwoNumbers 接受 2 个参数(xy)。

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

当您将 6 传递给 add5ToNumber 时,它会将保存的 x (5) 和给定的 y (6) 传递给 addTwoNumbers,结果为 11

addTwoNumbers accepts 2 arguments (x and y).

add5ToNumber is assigned to the output of calling addTwoNumbers with only 1 argument, which results in another function that "saves" the first argument (x -> 5) and accepts one other argument (y).

When you pass 6 into add5ToNumber, its passing the saved x (5) and the given y (6) into addTwoNumbers, resulting in 11

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文