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

问题描述 投票:6回答:2

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

scala
2个回答
7
投票

首先,一个主要的事情:带有两个参数ab并返回值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

0
投票

讨论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”。

© www.soinside.com 2019 - 2024. All rights reserved.