程式扎記: [Scala 小學堂] Scala Gossic : 繼續深入 - 模式比對 (部份函式)

標籤

2016年7月14日 星期四

[Scala 小學堂] Scala Gossic : 繼續深入 - 模式比對 (部份函式)

Source From Here
部份函式(Partial function)
在使用模式比對時,所使用的 案例序列Case sequence)實際上是一種 函式常量Function literal)寫法。你也可以將之當作函式來使用,例如(一個求 費式數列 的例子):
  1. val fibonacci: Int => Int = {  
  2.     case 0 => 0  
  3.     case 1 => 1  
  4.     case n => fibonacci(n - 1) + fibonacci(n - 2)  
  5. }  
  6.   
  7. println(fibonacci(10)) // 55  
一般在定義函式時,函式會有一個執行函式本體的進入點(Entry point),且只會有一個參數列(Parameter list)。案例序列 也是一種 函式常量寫法,只不過這個函式會有多個進入函式本體的點,每個 => 之後相當於一個函式本體,而每個模式比對案例(Case)是進入該函式本體所使用的參數列。既然 案例序列 是一種函式常量寫法,那麼你自然也可以將之傳遞,例如:
  1. sealed abstract class Drawing  
  2. case class Point(x: Int, y: Int) extends Drawing  
  3. case class Circle(p: Point, r: Int) extends Drawing  
  4. case class Cylinder(c: Circle, h: Int) extends Drawing  
  5.   
  6. class Graphic {  
  7.     def show(how: Drawing => Any) = {  
  8.         how(Point(11))  
  9.         how(Circle(Point(22), 2))  
  10.         how(Cylinder(Circle(Point(33), 3), 3))  
  11.     }  
  12. }  
  13.   
  14. val g = new Graphic  
  15. g.show {  
  16.     case Point(_, _)                         => println("點")  
  17.     case Circle(Point(_, _), _)              => println("圓")  
  18.     case Cylinder(Circle(Point(_, _), _), _) => println("柱")  
  19. }  
Scala 中一個應用的實例,可以在使用 scala.actors.Actor (scala.actors - Actor-based concurrency which is deprecated and replaced by Akka actors, scala-actors.jar) 的方法時看到,例如 Actor 的 receive 方法就接受一個 案例序列 所傳入的函式實字(以下只是示範,之後還會介紹 Actor 的使用):
  1. import scala.actors.Actor._  
  2.   
  3. val caller = self  
  4. actor {  
  5.     caller ! args(0)  
  6. }  
  7.   
  8. receive {  
  9.     case "some"  => println("do something...")  
  10.     case "other" => println("do other...")  
  11. }  
事實上,案例序列 所定義的是個 部份函式Partial function),所謂部份函式是語言的一種特性,表示你所宣告的函式對於輸入可能有定義也可能沒有定義。舉個例子來說,下面的函式對於 Point(1, 1)Point(2, 2) 有定義,但對於其它的 Point 情況該怎麼執行則沒有定義:
  1. case class Point(x: Int, y: Int)  
  2.   
  3. val what: Point => Int = {  
  4.     case Point(11) => 1  
  5.     case Point(22) => 2  
  6. }  
  7.   
  8. println(what(Point(11)))  // 1  
  9. println(what(Point(22)))  // 2  
  10. println(what(Point(33)))  // MatchError  
如果你想要知道某個部份函式對於某種情況是否有定義,則可以使用 scala.PartialFunction 來宣告,例如:
  1. case class Point(x: Int, y: Int)  
  2.   
  3. val what: PartialFunction[Point, Int] = {  
  4.     case Point(11) => 1  
  5.     case Point(22) => 2  
  6. }   
scala.PartialFunction 有個 isDefinedAt() 方法,可以讓你測試某個案例是否存在,例如:
  1. if(what.isDefinedAt(Point(11)))  
  2.     println(what(Point(11)))  
  3. else  
  4.     println("函式沒有定義此情況")  
  5.       
  6. if(what.isDefinedAt(Point(33)))  
  7.     println(what(Point(33)))  
  8. else  
  9.     println("函式沒有定義此情況")  
事實上,當你明確告訴編譯器某個函式為部份函式時,也就是使用 scala.PartialFunction 宣告時,編譯器會替你作類似以下的轉譯動作:
  1. new PartialFunction[Point, Int] {  
  2.     def apply(p: Point) = p match {  
  3.         case Point(11)  => 1  
  4.         case Point(22)  => 2  
  5.     }  
  6.   
  7.     def isDefinedAt(p: Point) = p match {  
  8.         case Point(11)  => true  
  9.         case Point(22)  => true  
  10.         _                 => false  
  11.     }  
  12. }  


沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!