在Scala中,我可以使用两个参数列表定义一个函数。
def myAdd(x :Int)(y :Int) = x + y
这样可以轻松定义部分应用的函数。
val plusFive = myAdd(5) _
但是,我可以通过定义和返回嵌套函数来完成类似的事情。
def myOtherAdd(x :Int) = {
def f(y :Int) = x + y
f _
}
美观上,我已经移动了下划线,但这仍然感觉就像是在干嘛。
val otherPlusFive = myOtherAdd(5)
我应该使用什么标准来偏好另一种方法?
答案 0 :(得分:9)
至少有四种方法可以完成同样的事情:
def myAddA(x: Int, y: Int) = x + y
val plusFiveA: Int => Int = myAddA(5,_)
def myAddB(x: Int)(y : Int) = x + y
val plusFiveB = myAddB(5) _
def myAddC(x: Int) = (y: Int) => x + y
val plusFiveC = myAddC(5)
def myAddD(x: Int) = {
def innerD(y: Int) = x + y
innerD _
}
val plusFiveD = myAddD(5)
您可能想知道哪个是最有效的或哪个是最好的风格(对于某些基于非绩效的最佳衡量标准)。
就效率而言,事实证明所有四个都基本相同。前两种情况实际上发出完全相同的字节码; JVM对多个参数列表一无所知,所以一旦编译器计算出来(你需要在案例A上使用类型注释来帮助它),它就完全相同了。第三种情况也非常接近,但由于它预先承诺返回一个函数并在现场指定它,它可以避免一个内部字段。第四种情况与前两种情况完全相同;它只是在方法内转换为Function1
而不是外部。
就风格而言,我建议B和C是最好的方法,取决于你正在做什么。如果您的主要用例是创建一个函数,而不是使用两个参数列表就地调用,那么使用C,因为它会告诉您它将要执行的操作。 (例如,这个版本对于来自Haskell的人来说也特别熟悉。)另一方面,如果你大部分时间都要调用它,但只是偶尔会把它称之为咖喱,那么就用B.再说一遍,它更清楚地说明了什么这是预期的。
答案 1 :(得分:6)
你也可以这样做:
def yetAnotherAdd(x: Int) = x + (_: Int)
您应该根据意图选择API。 Scala中有多个参数列表的主要原因是帮助进行类型推断。例如:
def f[A](x: A)(f: A => A) = ...
f(5)(_ + 5)
也可以使用它来拥有多个varargs,但我从未见过这样的代码。当然,还需要隐式参数列表,但这是另一回事。
现在,有许多方法可以让函数返回函数,这几乎就是currying的作用。如果API 应该被认为是一个返回函数的函数,则应该使用它们。
我认为很难比这更精确。
答案 2 :(得分:4)
让方法直接返回函数(而不是使用部分应用程序)的另一个好处是,当使用中缀表示法时,它会导致更清晰的代码,允许您在更复杂的表达式中避免使用括号和下划线。
考虑:
val list = List(1,2,3,4)
def add1(a: Int)(b: Int) = a + b
list map { add1(5) _ }
//versus
def add2(a: Int) = a + (_: Int)
list map add2(5)