这是一个Scala会话,用于定义和尝试一些函数:
scala> def test1(str: String) = str + str;
test1: (str: String)java.lang.String
scala> test1("ab")
res0: java.lang.String = abab
效果很好。
scala> val test2 = test1
<console>:6: error: missing arguments for method test1 in object $iw;
follow this method with `_' if you want to treat it as a partially applied function
val test2 = test1
^
糟糕。
scala> val test2 = test1 _
test2: (String) => java.lang.String = <function1>
scala> test2("ab")
res1: java.lang.String = abab
效果很好!
现在,我在折叠(_
等)时看到了_ + _
语法。据我所知,_
基本上意味着“一个论点”。所以test1 _
基本上意味着一个带有参数的函数,该函数被赋予test1
“。但为什么完全与test1
完全相同?为什么如果我附加_
?
所以我一直在探索......
scala> val test3 = (str: String) => str + str
test3: (String) => java.lang.String = <function1>
scala> test3("ab")
res2: java.lang.String = abab
scala> val test4 = test3
test4: (String) => java.lang.String = <function1>
这里没有_
! def
ed函数和val
ed函数之间有什么区别?
答案 0 :(得分:58)
def
在周围的对象/类/特征中声明一个方法,类似于在Java中定义方法的方式。您只能在其他对象/类/特征中使用def
。在REPL中,您无法看到周围的对象,因为它是“隐藏的”,但确实存在。
您无法为值分配def
,因为def
不是值 - 它是对象中的方法。
(x: T) => x * x
声明并实例化一个函数对象,它存在于运行时。函数对象是扩展FunctionN
特征的匿名类的实例。 FunctionN
个特征带有apply
方法。名称apply
是特殊的,因为它可以省略。表达式f(x)
被移植到f.apply(x)
。
底线是 - 由于函数对象是存在于堆上的运行时值,因此可以将它们分配给值,变量和参数,或者将它们作为返回值从方法返回。
要解决向值分配方法的问题(可能很有用),Scala允许您使用占位符字符从方法创建函数对象。上面示例中的表达式test1 _
实际上围绕方法test1
创建了一个包装函数 - 它等同于x => test1(x)
。
答案 1 :(得分:53)
def'ed函数和val'ed函数之间没有区别:
scala> def test1 = (str: String) => str + str
test1: (String) => java.lang.String
scala> val test2 = test1
test2: (String) => java.lang.String = <function1>
scala> val test3 = (str: String) => str + str
test3: (String) => java.lang.String = <function1>
scala> val test4 = test2
test4: (String) => java.lang.String = <function1>
请参阅?所有这些都是函数,由它们具有的X => Y
类型表示。
scala> def test5(str: String) = str + str
test5: (str: String)java.lang.String
您看到X => Y
类型了吗?如果你这样做,去看眼科医生,因为没有。此处的类型为(X)Y
,通常用于表示方法。
实际上,test1
,test2
,test3
和test4
都是返回函数的方法。 test5
是一种返回java.lang.String
的方法。另外,test1
到test4
不接受参数(无论如何只能test1
),而test5
则可以。{/ p>
所以,差异非常简单。在第一种情况下,您尝试将方法分配给val,但没有填写方法所采用的参数。所以它失败了,直到你添加了一个尾随下划线,这意味着将我的方法变成一个函数。
在第二个示例中,您有一个函数,因此您不需要执行任何其他操作。
方法不是函数,反之亦然。函数是FunctionN
类之一的对象。方法是与对象关联的某些代码的句柄。
在Stack Overflow上查看有关方法与函数的各种问题。
答案 2 :(得分:11)
下划线在不同的背景下意味着不同的东西。但它总是可以被认为是会发生的事情,但不需要命名为。
当应用代替参数时,效果是将方法提升为函数。
scala> def test1(str: String) = str + str;
test1: (str: String)java.lang.String
scala> val f1 = test1 _
f1: (String) => java.lang.String = <function1>
注意,该方法已成为type(String)=&gt;的函数。串。
Scala中方法和函数之间的区别在于方法类似于传统的Java方法。你不能将它们作为值传递。但是,函数本身就是值,可以用作输入参数和返回值。
提升可以更进一步:
scala> val f2 = f1 _
f2: () => (String) => java.lang.String = <function0>
提升此功能会产生另一个功能。这个时间类型()=&gt; (String)=&gt; (字符串)
据我所知,这种语法相当于用明确的下划线替换所有参数。例如:
scala> def add(i: Int, j: Int) = i + j
add: (i: Int,j: Int)Int
scala> val addF = add(_, _)
addF: (Int, Int) => Int = <function2>
scala> val addF2 = add _
addF2: (Int, Int) => Int = <function2>