我正在按照本教程进行操作,我看到以下代码:
def totalCostWithDiscountFunctionParameter(donutType: String)(quantity: Int)(f: Double => Double): Double = {
println(s"Calculating total cost for $quantity $donutType")
val totalCost = 2.50 * quantity
println("inside totalCostWithDiscountFunctionParameter")
f(totalCost)
}
def applyDiscount(totalCost: Double): Double = {
val discount = 2 // assume you fetch discount from database
totalCost - discount
}
println(s"Total cost of 5 Glazed Donuts with discount function = ${totalCostWithDiscountFunctionParameter("Glazed Donut")(5)(applyDiscount(_))}")
applyDiscount(_)
_
的意义何在?我也可以删除它,然后像这样按名称传递函数applyDiscount
并且代码有效。下划线的意义何在?是一回事吗?
applyDiscount(_)
是匿名函数的占位符语法。这将扩展到:
x => applyDiscount(x)
当您将applyDiscount
方法传递给函数时,即:
totalCostWithDiscountFunctionParameter("5")(applyDiscount)
然后scalac将执行eta扩展,这意味着将方法转换为函数值。
这些语义完全相同吗?接近,但不完全是。考虑下面给出的例子 Scala 中下划线的所有用途是什么?(略有修改,示例和提供的答案完全归功于@Owen(
trait PlaceholderExample {
def process[A](f: A => Unit)
val set: Set[_ => Unit]
set.foreach(process) // Error
set.foreach(process(_)) // No Error
}
编译时的第一个错误,而第二个错误成功,为什么会这样?让我们看一下用-Xprint:typer
编译的代码:
λ scalac -Xprint:typer Test.scala
FooBar.scala:11: error: polymorphic expression cannot be instantiated to expected type;
found : [A](A => Unit) => Unit
required: (Function1[_, Unit]) => ?
set.foreach(process) // Error
^
[[syntax trees at end of typer]]
package yuval.tests {
abstract trait PlaceholderExample extends scala.AnyRef {
def /*PlaceholderExample*/$init$(): Unit = {
()
};
def process[A](f: A => Unit): Unit;
<stable> <accessor> val set: Set[Function1[_, Unit]];
PlaceholderExample.this.set.foreach[U](process);
PlaceholderExample.this.set.foreach[Unit](((x$1: Function1[_, Unit]) => PlaceholderExample.this.process[_$1](x$1)))
}
}
您看到的第一件事是编译错误,因为 scalac 无法将方法从多态方法process
eta 扩展到单态函数。这意味着,当 scalac 尝试将A
绑定到实际类型时,它会查找(_ => Unit) => ?
的类型,但由于_
(存在(不是类型而失败。
另一方面,第二个示例扩展到x => process(x)
编译,因为当 scalac 遇到没有显式类型注释的 lambda 表达式时,它会查看方法签名的类型(在我们的例子中,foreach
期望一个_ => Unit
,它被归类为类型(并成功地将类型参数绑定到process
。
因此,在大多数情况下,你会发现两者是同构的(尽管它们实际上不是(甚至IntelliJ建议我可以写一个而不是另一个(,但有些边缘情况确实可以区分一个。