【问题标题】:"int -> int -> int" What does this mean in F#?“int -> int -> int” 这在 F# 中是什么意思?
【发布时间】:2011-01-11 16:01:42
【问题描述】:

我想知道这在 F# 中是什么意思。
“一个接受整数的函数,
它返回一个接受整数并返回整数的函数。”

但是这个我不是很懂。
谁能解释得这么清楚?

[更新]:

> let f1 x y = x+y ;;

 val f1 : int -> int -> int

这是什么意思?

【问题讨论】:

  • 你的新 sn-p 只是一个简单的函数,它接受 2 个 int 值并返回一个新的 int 值,即总和。所以 f1 5 4 将等于 9。“val f1 : int -> int -> int”部分只是 F# 告诉你刚刚在上一行声明的函数的结果。

标签: f# functional-programming currying


【解决方案1】:

它是一个接受整数并返回一个整数并返回一个整数的函数。

这在功能上等同于接受两个整数并返回一个整数的函数。这种处理带有多个参数的函数的方式在函数式语言中很常见,并且可以很容易地部分地将函数应用一个值。

例如,假设有一个 add 函数接受两个整数并将它们相加:

let add x y = x + y

您有一个列表,并且您想为每个项目添加 10 个。您将部分地将add 函数应用于值10。它将一个参数绑定到 10,而另一个参数未绑定。

let list = [1;2;3;4]
let listPlusTen = List.map (add 10)

这个技巧使组合函数变得非常容易,并且使它们非常可重用。如您所见,您无需编写另一个函数将 10 添加到列表项以将其传递给map。您刚刚重用了add 函数。

【讨论】:

  • > 让 f1 x y = x+y ;; val f1 : int -> int -> int 我很困惑这是什么意思?
  • 我更新了我的问题,所以你能解释一下 sn-p 吗?
  • @ali62b:在 F# 中,函数不使用括号来声明其参数。基本上,C# 中的 f(int x, int y) 变为 let f x y = ...
  • 您的评论并不完全正确。您可以(有时需要)声明采用元组的函数。
  • @Bruno:在元组的情况下,括号用于“元组声明”,而不是参数声明。 let f (x,y) 接受元组 typesingle 参数。
【解决方案2】:

这方面的典型例子可能是“加法器创建者” - 一个函数,给定一个数字(例如 3)返回另一个函数,该函数接受一个整数并将第一个数字添加到它。

所以,例如,在伪代码中

x = CreateAdder(3)
x(5) // returns 8
x(10) // returns 13
CreateAdder(20)(30) // returns 50

我不太习惯在 F# 中尝试编写它而不检查它,但 C# 会是这样的:

public static Func<int, int> CreateAdder(int amountToAdd)
{
    return x => x + amountToAdd;
}

这有帮助吗?

编辑:正如布鲁诺所说,您在问题中给出的示例正是我给出的 C# 代码示例,因此上述伪代码将变为:

let x = f1 3
x 5 // Result: 8
x 10 // Result: 13
f1 20 30 // Result: 50

【讨论】:

  • 乔恩,F# 版本正是 OP 在他的问题中所说的 f1。使用它:let x = f1 3x 5x 10f1 20 30,按照你的例子。
  • @ali62b:基本上是的。如果您将函数视为值(函数式语言中的基本内容),这是有道理的。假设带有一个参数的f1 函数的返回值只是另一个函数(C# 处理方式中的委托)。
  • @ali62b:我不确定我是否会说它正在“转换”它——这正是你的函数声明的意思。但如果它更容易理解为转换,那可能不会对您造成任何伤害。
  • 函数式编程中最重要的是将值和函数视为同一事物。例如,您可以将文字 10 视为一个不带参数并返回整数的函数。 Conversley,例如,您可以将指数函数exp 视为一个值。一个接受多个参数的函数可以类似地考虑:你可以传递一个参数,它会返回一个接受剩余参数的函数,或者你可以传递它的所有参数,它会返回结果。由于功能纯度,这一切皆有可能。
  • 当然,在像 F# 这样的柯里化语言中,我们可以简单地将 create_adder 定义为 create_adder = (+)
【解决方案3】:

您通常将此解释为一个接受两个整数并返回一个整数的函数。 你应该阅读currying

【讨论】:

    【解决方案4】:

    例子:

    let f b a = pown a b //f a b = a^b

    是一个函数,它接受一个 int(指数)并返回一个函数,该函数将其参数提升到该指数,例如

    let sqr = f 2

    let tothepowerofthree = f 3

    所以

    sqr 5 = 25

    tothepowerofthree 3 = 27

    【讨论】:

      【解决方案5】:

      一个接受整数的函数,它返回一个接受一个整数并返回一个整数的函数

      最后一部分:

      一个接受整数并返回整数的函数

      应该比较简单,C#例子:

      public int Test(int takesAnInteger) { return 0; }
      

      所以我们只剩下

      一个接受整数的函数,它返回(类似于上面的函数)

      又是 C#:

      public int Test(int takesAnInteger) { return 0; }
      public int Test2(int takesAnInteger) { return 1; }
      
      public Func<int,int> Test(int takesAnInteger) {
          if(takesAnInteger == 0) {
              return Test;
          } else {
              return Test2;
          }
      }
      

      【讨论】:

        【解决方案6】:

        这个概念被称为Higher Order Function,在函数式编程中很常见。

        函数本身只是另一种类型的数据。因此,您可以编写返回其他函数的函数。当然,您仍然可以拥有一个以 int 作为参数并返回其他内容的函数。结合两者并考虑以下示例(在python中):

        def mult_by(a):
            def _mult_by(x):
                return x*a
            return mult_by
        
        mult_by_3 = mult_by(3)
        
        print mylt_by_3(3)
        9
        

        (抱歉用了python,但我不知道f#)

        【讨论】:

        • python也是函数式语言吗?
        • 本身不是,但您可以使用它进行大量函数式编程。
        【解决方案7】:

        F# 类型

        让我们从头开始。

        F# 使用冒号 (:) 表示法来指示事物的类型。假设您定义了一个 int 类型的值:

        let myNumber = 5
        

        F# Interactive 会理解 myNumber 是一个整数,并会通过以下方式告诉您:

        myNumber : int
        

        读作

        myNumber 的类型为 int

        F# 函数类型

        到目前为止一切顺利。让我们介绍一下其他东西,函数类型。函数类型只是函数的类型。 F# 使用-&gt; 表示函数类型。这个箭头象征着写在它左边的东西变成了写在它右边的东西。

        让我们考虑一个简单的函数,它接受一个参数并将其转换为一个输出。这种函数的一个例子是:

        isEven : int -> bool
        

        这将介绍函数的名称(在: 的左侧)及其类型。这一行可以用英文读为:

        isEven 是类型函数,可将int 转换为bool

        请注意,要正确解释所讲的内容,您应该在“is of type”部分之后稍作停顿,然后立即阅读句子的其余部分,不要停顿。

        在 F# 中,函数是值

        在 F# 中,函数(几乎)并不比 普通 类型更特殊。它们是您可以传递给函数、从函数返回的东西,就像 bool、int 或字符串一样。

        如果你有:

        myNumber : int
        isEven : int -> bool
        

        您应该将intint -&gt; bool 视为同一类型的两个实体:类型。这里,myNumberint 类型的值,isEvenint -&gt; bool 类型的值(这就是我在谈到 短暂停时想要表示的以上)。

        功能应用

        包含-&gt; 的类型的值恰好也称为函数,并且具有特殊的能力:您可以将函数应用 一个值。所以,例如,

        isEven myNumber
        

        表示您正在将名为isEven 的函数应用于值myNumber。正如您通过检查isEven 的类型所期望的那样,它将返回一个布尔值。如果你正确实现了isEven,它显然会返回false

        返回函数类型值的函数

        让我们定义一个通用函数来确定一个整数是否是其他整数的倍数。我们可以想象我们函数的类型将是(括号在这里帮助你理解,它们可能存在也可能不存在,它们有特殊含义):

        isMultipleOf : int -> (int -> bool)
        

        你可以猜到,这读作:

        isMultipleOf 属于 (PAUSE) 函数类型,可将 int 转换为 (PAUSE) 函数,可将 int 转换为 bool

        (这里的(PAUSE)表示大声朗读时的停顿)。

        我们稍后会定义这个函数。在此之前,让我们看看如何使用它:

        let isEven = isMultipleOf 2
        

        F# Interactive 会回答:

        isEven : int -> bool
        

        读作

        isEven 的类型为 int -&gt; bool

        这里,isEven 的类型为 int -&gt; bool,因为我们刚刚将值 2 (int) 赋予了 isMultipleOf,正如我们已经看到的,它将 int 转换为 int -&gt; bool .

        我们可以将这个函数isMultipleOf 视为一种函数创建者

        isMultipleOf的定义

        现在让我们定义这个神秘的函数创建函数。

        let isMultipleOf n x =
            (x % n) = 0
        

        简单吧?

        如果你在 F# Interactive 中输入这个,它会回答:

        isMultipleOf : int -> int -> bool
        

        括号在哪里?

        请注意,没有括号。现在这对你来说不是特别重要。请记住,箭头是右结合。也就是说,如果你有

        a -> b -> c
        

        你应该把它理解为

        a -> (b -> c)
        

        右结合中的right意味着你应该解释为好像最右边的运算符周围有括号。所以:

        a -> b -> c -> d
        

        应该解释为

        a -> (b -> (c -> d))
        

        isMultipleOf 的用法

        所以,如您所见,我们可以使用isMultipleOf 来创建新函数:

        let isEven = isMultipleOf 2
        let isOdd = not << isEven
        let isMultipleOfThree = isMultipleOf 3
        let endsWithZero = isMultipleOf 10
        

        F# Interactive 会回应:

        isEven : int -> bool
        isOdd : int -> bool
        isMultipleOfThree : int -> bool
        endsWithZero : int -> bool
        

        但是您可以以不同的方式使用它。如果不想(或不需要)创建新函数,可以按如下方式使用:

        isMultipleOf 10 150
        

        这将返回 true,因为 150 是 10 的倍数。这与创建函数 endsWithZero 然后将其应用于值 150 完全相同。

        其实函数应用是左关联的,也就是说上面这行应该解释为:

        (isMultipleOf 10) 150
        

        也就是说,您将括号放在最左边的函数应用程序周围。

        现在,如果您能理解这一切,那么您的示例(即规范的 CreateAdder)应该是微不足道的!

        前段时间有人问this question 处理完全相同的概念,但使用的是 Javascript。在我的回答中,我给出了两个规范示例(CreateAdder、CreateMultiplier)inf Javascript,它们对返回函数更加明确。

        我希望这会有所帮助。

        【讨论】:

        • 这是一个非常有用且深入的答案!
        • 哥们,你在这里写一章当整本书出来的时候;)
        • 这是迄今为止我读过的关于函数类型的最佳解释。
        • 我认为“isMultipleOf 的用法”有错误。 isOdd 不应该是:not
        • 热狗,这是一个完整的答案。 +1
        【解决方案8】:

        【讨论】:

          【解决方案9】:

          在 F#(和许多其他函数式语言)中,有一个称为柯里化函数的概念。这就是你所看到的。本质上,每个函数都接受一个参数并返回一个值。

          一开始这似乎有点令人困惑,因为您可以写let add x y = x + y,它似乎添加了两个参数。但实际上,原来的add 函数只接受参数x。当您应用它时,它会返回一个函数,该函数接受一个参数 (y),并且已经填充了 x 值。当您应用该函数时,它会返回所需的整数。

          这显示在类型签名中。将类型签名中的箭头视为“获取我左侧的东西并返回我右侧的东西”的意思。在int -&gt; int -&gt; int 类型中,这意味着它接受int 类型的参数——一个整数——并返回一个int -&gt; int 类型的函数——一个接受一个整数并返回一个整数的函数。您会注意到这与上面对柯里化函数如何工作的描述完全匹配。

          【讨论】:

            【解决方案10】:

            这里已经有很多答案了,但我想提供另一个答案。有时以许多不同的方式解释同一件事可以帮助你“理解”它。

            我喜欢把函数想象成“你给我一些东西,我会给你一些别的东西”

            所以Func&lt;int, string&gt; 说“你给我一个 int,我给你一个字符串”。

            我还发现用“稍后”来思考更容易:“你给我一个 int,我会给你一个字符串”。当你看到像myfunc = x =&gt; y =&gt; x + y 这样的东西时,这一点尤其重要(“你给咖喱一个x,你会得到一些东西,你给它ay会返回x + y ”)。

            (顺便说一下,我假设您在这里熟悉 C#)

            所以我们可以将您的 int -&gt; int -&gt; int 示例表达为Func&lt;int, Func&lt;int, int&gt;&gt;

            我看待 int -&gt; int -&gt; int 的另一种方式是通过提供适当类型的参数从左侧剥离每个元素。当你没有更多的 -&gt; 时,你就没有了“后来者”,你会得到一个价值。


            (只是为了好玩),你可以将一个函数转换成一个函数,它把所有的参数都集中到一个“渐进式”(渐进式应用它们的官方术语是“部分应用”),这被称为“currying” ':

            static void Main()
            {
                //define a simple add function
                Func<int, int, int> add = (a, b) => a + b;
            
                //curry so we can apply one parameter at a time
                var curried = Curry(add);    
            
                //'build' an incrementer out of our add function
                var inc = curried(1);         // (var inc = Curry(add)(1) works here too)
                Console.WriteLine(inc(5));    // returns 6
                Console.ReadKey();
            }
            static Func<T, Func<T, T>> Curry<T>(Func<T, T, T> f)
            {
                return a => b => f(a, b);
            }
            

            【讨论】:

              【解决方案11】:

              这是我的 2 c。默认情况下,F# 函数启用部分应用程序或柯里化。这意味着当你定义这个时:

              let adder a b = a + b;;
              

              您正在定义一个接受整数并返回一个接受整数并返回整数的函数int -&gt; int -&gt; int的函数。然后,柯里化允许您部分应用一个函数来创建另一个函数:

              let twoadder = adder 2;;
              //val it: int -> int
              

              上面的代码将 a 预定义为 2,这样每当您调用 twoadder 3 时,它都会简单地将 2 添加到参数中。

              函数参数用空格分隔的语法等价于这个 lambda 语法:

              let adder = fun a -> fun b -> a + b;;
              

              这是一种更易读的方式来确定这两个函数实际上是链接的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2018-06-16
                • 1970-01-01
                • 1970-01-01
                • 2015-08-28
                • 2011-06-05
                • 2011-04-12
                • 2014-12-07
                • 1970-01-01
                相关资源
                最近更新 更多