假设我们有一个函数def fun(x: X): X => Y
,我们使用fun _
而不是fun
将此函数作为参数传递给另一个函数。我理解fun _
实际上是一个函数值,而fun
是指一个函数定义。
例如,让:
val a = List(1,2,3,4)
def fun(x: Int) = {println(x); x + 1}
然后跑步:
//This line works
a.map(fun _)
//This one also works, even though "fun" is not a function value
a.map(fun)
它们具有相同的输出:
1
2
3
4
resX: List[Int] = List(2, 3, 4, 5)
在大多数情况下,它们的工作原理相同,是否存在函数值与函数定义不等的示例?
答案 0 :(得分:4)
在signature of map
中,您可以看到它正在等待
"功能"适用于每个元素
但是在您的代码中,fun
是类中的常规方法。所以当你这样做时:
a.map(fun _)
你明确要求进行eta扩展。当你这样做时:
a.map(fun)
你隐含地要求进行eta扩展。
由于fun
是"方法",并且正在预期使用Function
类型的地方使用,因此它automagically converted为{类型。基本上是这样的:
new Function1[Int, Int] {
def apply(x: Int): Int = fun(x)
}
将名称fun
转换为Function
的转换称为eta-expansion
。有关详细信息,请参阅documentation。
不幸的是,有多种方法可以执行您正在执行的操作 - a.map(fun)
,a.map(fun _)
,a.map(fun(_))
和a.map(x => fun(x))
。这是Scala中常见的场景之一,您可以自己明确地执行某些操作,或者明确要求编译器为您执行此操作,或者让编译器隐式执行此操作。由于隐含,它们可能具有不同的行为,并且它可能是混淆的主要来源。此外,_
在语言中严重超载,只会增加混乱。所以我一般都谨慎地使用隐式行为。
答案 1 :(得分:2)
正如其他人在评论中指出的那样,当需要一个值时,你需要使用fun _
语法(执行eta expansion)(方法本身没有价值)。在地图(或其他功能上下文)的上下文中,隐含地对该方法执行eta扩展。在某些情况下,必须手动触发eta扩展。
作为需要显式eta扩展的具体示例,请考虑以下有效代码段:
def f1(x: Int): Int = 2*x
def f2(x: Int): Int = 3*x
val fList1 = List(f1 _, f2 _)
fList1.map(_(2)) // List(4, 6)
而非此无效代码段。
val fList2 = List(f1, f2)