繁体   English   中英

currying和高阶函数之间的区别

[英]Difference between currying and higher-order functions

看看Scala中的编程 (控件抽象)我看到这两个具有相同效果的例子:

1.高阶函数

def withPrintWriter(file: File, op: PrintWriter => Unit) {
  val writer = new PrintWriter(file)
  try {
    op(writer)
  } finally {
    writer.close()
  }
}

2.卷曲功能

def withPrintWriter(file: File)(op: PrintWriter => Unit) {
  val writer = new PrintWriter(file)
  try {
    op(writer)
  } finally {
    writer.close()
  }
}

他们之间有什么区别? 我们能否始终以两种方式获得相同的结果?

高阶函数curried函数的概念通常以正交方式使用。 高阶函数只是一个函数, 它将函数作为参数或作为结果返回函数,它可能会也可能不会被咖喱。 在一般用法中,引用高阶函数的人通常在谈论将另一个函数作为参数的函数。

另一方面, curried函数是一个返回函数作为结果的函数。 完全curried函数是单参数函数,它返回普通结果或返回完全curried函数。 注意,curried函数必然是高阶函数,因为它返回一个函数作为其结果。

因此,您的第二个示例是返回高阶函数的curried函数的示例。 这是curried函数的另一个例子,它没有将函数作为参数,以各种( 几乎相当的 )方式表示:

def plus(a: Int)(b:Int) = a + b
def plus(a: Int) = (b: Int) => a + b
val plus = (a: Int) => (b: Int) => a + b

高阶函数是将函数作为参数或返回函数或两者兼有的函数。

def f(g: Int => Int) = g(_: Int) + 23

scala> f(_ + 45)
res1: Int => Int = <function1>

scala> res1(4)
res2: Int = 72

这是一个更高阶的函数,它将函数作为参数并返回另一个函数。 如您所见,高阶函数是curry的先决条件。 咖喱功能看起来像这样:

def curry[A,B,C](f: (A,B) => C) = (a: A) => (b: B) => f(a,b)

scala> curry((a: Int, b: Int) => a+b)
res3: Int => (Int => Int) = <function1>

scala> res3(3)
res4: Int => Int = <function1>

scala> res4(3)
res5: Int = 6

所以回答你的问题:它们是两个不同的概念,其中一个(高阶函数)是另一个(currying)的预先获取。

从语义上讲,我可以在curried和not curried函数之间找到一个区别。 使用非curried版本时,当您使用withPrintWriter调用时,这是一个单一的方法调用。 使用curried版本,它实际上将是两个方法调用。 可以这样想:

withPrintWriter.apply(file).apply(op)

除此之外,我认为很多人在这种情况下使用currying来表达风格。 在这里使用currying使这看起来更像是一个语言功能,然后只是一个自定义函数调用,因为你可以像这样使用它:

withPrintWriter(file){ op =>
   ...
}

以这种方式使用它试图从语言本身模拟一些控制结构的痛苦,但同样,这只是一种风格的东西,它确实带来了额外的方法调用的开销。

您可以以几乎相同的方式使用非咖喱版本,但它看起来并不干净:

withPrintWriter(file, { op =>
   ...
})

编辑 @drexin在他的回答中提出了一个很好的观点,这对我来说值得一提。 当你想到该方法的curried版本的签名时,它确实是:

Function1[File, Function1[PrintWriter, Unit]]

它们大多是相同的,但在类型推断方面存在差异。 Scala无法在单个方法调用的参数之间推断类型,但它能够推断多个参数列表的类型。

考虑:

def foo1[T](x : T, y : T => T) = y(x)
def foo2[T](x : T)(y : T => T) = y(x)

foo1(1, t => t + 1) //does not compile with 'missing parameter type'
foo2(1)(t => t + 1) //compiles

您可以在此答案中看到一些其他信息: 未推断出多参数闭包参数类型

严格地说,你提供的例子并不是真正的咖喱,它只有多个参数列表。 只是在多种情况下,多个参数列表Scala函数看起来很像curried函数。 但是,当您调用多参数列表函数但不填写一个或多个参数列表时,它实际上是部分应用程序的示例,而不是currying。 使用其所有参数调用多参数列表只是一个函数调用,而不是每个参数列表一个。

有两个用例,其中多个参数列表函数是有用的。 第一个是隐式参数的情况,因为所有隐式参数都必须位于与任何显式参数分开的自己的参数列表中。 第二个用例是接受其他函数作为参数的函数,因为如果一个函数的参数在它自己的参数列表中,你可以省略括号并只使用大括号,使函数调用看起来像某种控制结构。

除此之外,差异纯粹是装饰性的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM