case樣本序列
先看一段代碼
val func1: Int => Int = {
case x => 1
}
等號的右邊是{case x => 1},一對花括號包裹着一個case語句。這樣的用法看到過很多次,但是很少有資料解釋爲什麼可以這樣用。查閱過很多資料,最終在《programming in Scala》(scala編程:黃海旭,高宇翔譯)裏出現過一段話。“樣本序列可以用在出現函數字面量的任何地方。實質上,樣本序列就是函數字面量,而且更普遍。函數字面量有一個入口點和參數列表,但是樣本序列可以有多個入口點,每個都有自己的參數列表。”這三句話總結起來有兩點:
- 樣本序列可以替代函數字面量使用
- 樣本序列和一般函數字面量的不同在於有多個入口點,每個入口點有自己獨立的參數列表
現在就可以解釋爲什麼在代碼中可以這樣使用了,首先 val func1: Int => Int 聲明瞭不可變變量要被賦值一個Int => Int 類型的函數字面量,所以{case x => 1}可以被賦值給func1。
這種用法在scala裏十分常見。大家一定經常使用的函數如foreach():
def foreach[U](f: A => U) { while (hasNext) f(next()) }
大家習慣性的使用方式是:
Seq(1, 2, 3).foreach{case x => println(x)}
這是因爲foreach要求使用一個A => U類型的函數字面量作爲參數,所有可以使用樣本序列{case x => println(x)}作爲參數。而上面的代碼其實是一種縮寫,實際上應該是:
Seq(1, 2, 3).foreach({case x => println(x)}) //比上面的代碼多了一對括號()
此外,如大家常用的操作,如map,reduce都可以用樣本序列替換函數字面量。
以上展示的都是單行樣本序列,然後還有多行樣本序列,原理都是一樣的。
一個問題?
scala中還有一個不常用的寫法:
val func2: Int => Int = {
x => 1
}
這種寫法和上面的上面的寫法幾乎一模一樣,唯獨少量一個case,不知道是不是樣例類的一種簡寫。如果有知道的還請解釋一下。我個人覺得有可能是閉包.
之前留了一個疑問,現在對這個疑問做下解釋:
其實上面的例子很簡單,只是一直陷入思維慣性,覺得很高深。我們在scala裏是可以直接定義匿名函數的,如:
現在我們考慮給這個匿名函數賦給一個值:
val func2 = (x:Int) => 1+x
然後大家應該記得scala的代碼塊,就是花括號連同它包裹的地方是有值得,他的值就是最後一句代碼所標識的值,那{(x:Int) => 1+x}的值就是(x:Int) => 1+x,那麼上一句代碼就可以表示爲:
val func2 = {
(x:Int) => 1+x
}
然後只需要聲明一下fun2的類型:
def func2:Int => Int = {
(x:Int) => 1+x
}
然後做一下縮寫,如去掉花貨號裏的()和x的數據類型聲明:
def func2:Int => Int = {
x: => 1+x
}
現在就得到了最初的形式。爲了進一步驗證正確性,可以嘗試在花括號里加一些代碼,發現也是可行的。
def func2: Int => Int = {
val y = 10
x: => 1 + x + y
}
然後我們嘗試在代碼塊的最後加一些代碼:
def func2: Int => Int = {
val y = 10
x => 1 + x + y
val z = 0
z
}
發現這樣也是可行的,按理說這樣應該不可以,因爲代碼塊的最後一句是一個z代碼的是一個Int類型,並不是 Int => Int類型的函數字面量。這是因爲上面呢的代碼產生了視覺誤差,實際上應該是下面這樣的。
def func2: Int => Int = {
val y = 10
x =>1 + x + y
val z = 0
z
}
最後兩句代碼是屬於Int=>Int這個方法的方法體的
x =>{1 + x + y
val z = 0
z}
然後嘗試把最後兩句代碼從裏面的函數的方法體裏取出來,就會報錯:
def func2: Int => Int = {
val y = 10
{ x => 1 + x + y }
val z = 0
z
}
Error:(9, 13) type mismatch;
found : Int
required: Int => Int
z