所以我在Scala中有这个功能:
def f(a: Int)(b: Int)(c: Double)(d: Double): Double = a * c + b * d
问题是使以下语句编译的三种类型是什么。
def g: <Type1> = f(1)(2)(3.0)
def h: <Type2> = f(1)(2)
def k: <Type3> = f(1)
我还是Scala的新手,我并不是真正理解currying的概念。也许通过一些解释回答这个问题对我很有帮助。谢谢。
首先,一个主要的事情:带有两个参数a
和b
并返回值c
的函数可以被视为一个函数,它接受一个a
并返回一个函数,它接受b
并返回c
。这种“观点的改变”被称为currying。
想象一下总结两个数字的函数。你给它2和3,它返回5.它可以被视为一个函数,它接受一个数字并将一个函数从一个数字返回一个数字。你给它一个2,它返回一个函数,它需要一些数字并加2。
现在,您请求的一些类型:
// pseudocode!
def g: Double => Double
= f(1)(2)(3.0) // we supply three params and are left with only one, "d"
= (d: Double) => 1 * 3.0 + 2 * d // we comply with g's type
def h: Double => Double => Double // or (Double, Double) => Double
= f(1)(2) // we supply two params and are left with two more, "c" and "d"
= (c: Double)(d: Double) => 1 * c + 2 * d // we comply with h's type
def k: Double => Double => Double => Double // or (Double, Double, Double) => Double
= f(1) // we supply one param and are left with three more, "b", "c" and "d"
= (b: Double)(c: Double)(d: Double) => 1 * c + b * d // we comply with k's type
讨论IMO是Scala中最令人困惑的概念之一。这个术语本身来自函数式编程范式,根据wikipedia,它是
将带有多个参数(或参数元组)的函数的求值转换为评估函数序列的技术,每个函数都有一个参数。
这意味着函数调用f(a, b, c)
由f(a)(b)(c)
表示。看起来像Scala?不完全是。这里我们有三个函数调用,每个调用返回另一个函数。 f
的类型(在Scala中说)是Int => (Int => (Double => (Double => Double)))
。让我们来看看你的f
:
scala> def f(a: Int)(b: Int)(c: Double)(d: Double): Double = a * c + b * d
f: (a: Int)(b: Int)(c: Double)(d: Double)Double
如你所见,这里没有箭头。我们这里有一个多参数列表的方法。方法没有任何价值,无法在任何地方分配或传递,它属于一个对象。另一方面,函数是一个对象,可以分配或传递给另一个方法或函数。在大多数情况下,方法不允许省略参数列表:
scala> f(0)
<console>:01: error: missing argument list for method f
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `f _` or `f(_)(_)(_)(_)` instead of `f`.
但是有一个例外,因为错误消息暗示:如果f(0)
被放置在功能上下文中,Scala将执行自动eta扩展,这意味着它将您的方法转换为函数:
scala> val fl: (Int => (Double => (Double => Double))) = f(0)
fl: Int => (Double => (Double => Double)) = $$Lambda$1342/937956960@43c1614
eta-expansion在字面上意味着:
scala> val fl: (Int => (Double => (Double => Double))) = (b => (c => (d => f(0)(b)(c)(d))))
fl: Int => (Double => (Double => Double)) = $$Lambda$1353/799716194@52048150
将方法转换为curried函数的另一种(显式)方法是使用占位符(它会立即为您提供正确的类型):
scala> f _
res11: Int => (Int => (Double => (Double => Double))) = $$Lambda$1354/1675405592@4fa649d8
scala> f(0) _
res12: Int => (Double => (Double => Double)) = $$Lambda$1355/1947050122@ba9f744
另请注意:
def g: Int => (Double => (Double => Double)) = f(0)
实际上是
def g: Int => (Double => (Double => Double)) = (b => (c => (d => f(0)(b)(c)(d))))
即它是一种方法g
,它在运行中创建一个函数并返回它。所以g(0)
的意思是“调用方法g
没有参数,取回函数并将其应用于0
”。