匿名類函數的占位符語法
考慮下面的代碼:
val numSeq=Array(3.1415,2.71828,9.8)
下面想用foreach
方法輸出它們的整數,如果用占位符語法,比如下面這樣:
scala> numSeq foreach (println(_.toInt))
<console>:13: error: missing parameter type for
expanded function ((x$1: <error>) => x$1.toInt)
numSeq.foreach(println(_.toInt))
^
看起來,我們本意是將println(_.toInt)
解釋為x=>println(x.toInt)
,但是編譯器卻理解為x=>x.toInt
,自然不對。這是因為,添加了一對括號后((_.toInt)
)就形成了新的詞法作用域,因此println(_.toInt)
會被解讀為println(x=>x.toInt)
。
(參考:Scala placeholder syntax for anonymous function)
既然如此,不妨去掉括號看看:
scala> numSeq foreach (println_.toInt) 沒有加空格
<console>:13: error: not found: value println_
numSeq foreach (println_.toInt)
^
很容易錯的地方,就是在函數名和下劃線之間不加空格,這就會將println_
看做一個標識符。
scala> numSeq foreach (println _.toInt)
<console>:1: error: ')' expected but '.' found.
numSeq foreach (println _.toInt)
注意,scala里面,使用method()
可以簡寫為method
,但是method(x)
不能省略括號method x (錯誤)
。(這是Groovy的語法,搞混了_(:зゝ∠)_)
有一個特殊的下劃線用法,也就是說,指代整個參數列表,就可以寫成method _
,所以,println _.toInt
事實上是println _
,顯然后跟.toInt
就不合理了,因為這就變成( x=>println(x) ).toInx
,語法錯誤。
那么該怎么做呢?注意到在scala里,如果對象obj
調用一個方法method
,而該方法只接受一個參數x
,那么obj.method(x)
可以簡寫為obj method x
或 obj method (x)
,看起來method
就不是一個方法名而是一個中綴操作符,換言之
scala> numSeq foreach (Console println _.toInt)
3
2
9
此時,編譯器明確是三部分,所以_.toInt
就被視為一個整體,從而Console println _.toInt
就被翻譯成我們需要的x=>Console.println(x,toInt)
現在想要將numSeq
升序排序:
scala> numSeq.sortWith{ _.compareTo(_)<0 }
res93: Array[Double] = Array(2.71828, 3.1415, 9.8)
_.compareTo(_)<0
實際上是(x,y) => { x.compareTo(y)<0 }
更多參考:scala 下劃線解析報錯: missing parameter type for expanded func
下劃線文法的就近展開原則原則:
Underscores extend outwards to the closest closing Expr : top-level expressions or expressions in parentheses
匿名函數簡寫注意事項:
所以當匿名函數有多個括號嵌套的時候,不要使用_的簡寫方式 ,而應該用普通匿名函數的書寫方式。比如 (x:Int,y:Int)=> (x*2)+f(y+2)
_本身構不成一個表達式:expressions,所以如下是允許的
scala> List(1,2,3,4).foreach(print(_))
=>List(1,2,3,4).foreach(print _)
=>List(1,2,3,4).foreach(print)
=>List(1,2,3,4) foreach print
=>List(1,2,3,4) foreach print _