在Scala中讨论,使这些语句编译的类型是什么?

时间:2018-03-05 16:06:10

标签: scala

所以我在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的概念。也许通过一些解释回答这个问题对我很有帮助。感谢。

2 个答案:

答案 0 :(得分:7)

首先,一个主要内容:带有两个参数ab并返回值c的函数可以被视为一个函数,它接受a并返回需要b并返回c的函数。这种观点的改变&#34;被称为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

答案 1 :(得分:0)

理解IMO是Scala中最令人困惑的概念之一。这个术语本身来自函数式编程范式,并且根据wikipedia,是

  

翻译评估函数的技术   多个参数(或参数元组)用于评估a   函数序列,每个函数都有一个参数。

表示函数调用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)表示&#34;调用方法g不带参数,取回函数并将其应用于0&#34;。