程式扎記: [Scala 小學堂] Scala Gossic : 繼續深入 - 提取器 (不同個數的元素提取)

標籤

2016年7月16日 星期六

[Scala 小學堂] Scala Gossic : 繼續深入 - 提取器 (不同個數的元素提取)

Source From Here
不同個數的元素提取
unapply() 方法可以僅提取一個結果,例如:
  1. object Address {  
  2.     def unapply(str: String): Option[String] = {  
  3.         val data = str.split(",")  
  4.         if (data.length == 3) Some(data(2)) else None  
  5.     }  
  6. }  
  7.   
  8. val Address(addr) = "B123456,Justin,Kaohsiung"    
  9. println(addr)           // Kaohsiung  
或是傳回 Boolean 型態,表示模式比對是否成功,可以運用這個特性,改寫上面找出住在高雄的學生姓名之範例:
  1. object Student {  
  2.     def unapply(str: String) = {  
  3.         val data = str.split(",")  
  4.         if (data.length == 3) Some(data(0), data(1), data(2)) else None  
  5.     }  
  6. }  
  7.   
  8. object Kaohsiung {  
  9.     def unapply(str: String): Boolean = str == "Kaohsiung"  
  10. }  
  11.   
  12. val students = List(  
  13.                    "B123456,Justin,Kaohsiung",  
  14.                    "B98765,Monica,Kaohsiung",  
  15.                    "B246819,Bush,Taipei"  
  16.                )  
  17.                  
  18. students.foreach(_ match {  
  19.     case Student(_, name, addr @ Kaohsiung()) => println(name)  
  20.     case _ =>  
  21. })  
注意上例中,模式比對時 Kaohsiung() 的括號是必要的,這用以區別要使用 Kaohsiung 的 unapply() 方法而不是 Kaohsiung 物件。這個例子也示範了使用提取器進行模式比對的一個好處,你可以連續提取,上例中,先使用 Student 提取器提取出 name 與 addr,再進一步使用 Kaohsiung() 提取器判斷 addr 模式比對是否成功。

如果事先無法知道提取方法會提取出的元素個數,則無法使用 unapply() 方法 來定義提取的動作,例如,也許使用者會輸入一串文字,當中包括大小寫字母,你想要提取出其中大寫字母,但問題是使用者輸入的文字長度是無法預測的,自然也就無法預測所提取的大寫字母不個數會有多少。若想要提取的元素個數不定,則可以定義 unapplySeq() 方法,例如:


你在 List 模式 中看到的比對方式,都可以套用在上例的提取器中,例如:
  1. val strs = Array(  
  2.               "Scala is Java?",   
  3.               "Java is Scala?",  
  4.               "Java 7 will include closure!",  
  5.               "Scala already has closure.")  
  6. strs.foreach(_ match {  
  7.     case Uppercase('J')       => println("only 'J' mentioned")  
  8.     case Uppercase('J''S')  => println("'JS' mentioned")  
  9.     case other                => println("other: " + other)  
  10. })  
事實上,List 正是實作了 unapplySeq() 方法,才可以使用 List 模式 中所看到的比對模式。unapplySeq() 方法也可以選擇性的傳回固定個數的元素之後,再提供未定個數的元素部份,例如下例找出使用者名稱為 "caterpillar" 的郵件,取得其網域切割後的字串陣列:
  1. object Email {  
  2.     def unapplySeq(s: String): Option[(String, Seq[String])] = {  
  3.         val parts = s.split("@")  
  4.         if(parts.length == 2) Some((parts(0), parts(1).split("\\.")))  
  5.         else None  
  6.     }  
  7. }  
  8.   
  9. val strs = Array(  
  10.               "caterpillar@gmail.com",   
  11.               "caterpillar@openhome.cc",  
  12.               "Justin@openhome.cc")  
  13. strs.foreach(_ match {  
  14.     case Email("caterpillar", domain @ _*) => println(domain)  
  15.     case _ =>  
  16. })  


沒有留言:

張貼留言

網誌存檔

關於我自己

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