在 Scala 中传递函数时为用法加下划线



我正在按照本教程进行操作,我看到以下代码:

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 无法将方法从多态方法processeta 扩展到单态函数。这意味着,当 scalac 尝试将A绑定到实际类型时,它会查找(_ => Unit) => ?的类型,但由于_(存在(不是类型而失败。

另一方面,第二个示例扩展到x => process(x)编译,因为当 scalac 遇到没有显式类型注释的 lambda 表达式时,它会查看方法签名的类型(在我们的例子中,foreach期望一个_ => Unit,它被归类为类型(并成功地将类型参数绑定到process

因此,在大多数情况下,你会发现两者是同构的(尽管它们实际上不是(甚至IntelliJ建议我可以写一个而不是另一个(,但有些边缘情况确实可以区分一个。

最新更新