Scala入门到精通——第二十五节 提取器(Extractor)
本節(jié)主要內(nèi)容
1. apply與unapply方法
apply方法我們已經(jīng)非常熟悉了,它幫助我們無需new操作就可以創(chuàng)建對象,而unapply方法則用于析構(gòu)出對象,在模式匹配中特別提到,如果一個(gè)類要能夠應(yīng)用于模式匹配當(dāng)中,必須將類聲明為case class,因?yàn)橐坏┍欢x為case class,Scala會自動幫我們生成相應(yīng)的方法,這些方法中就包括apply方法及unapply方法。本節(jié)將從提取器(也稱析構(gòu)器)的角度對unapply方法進(jìn)行介紹。先看下面的這個(gè)例子(來源于programmin in scala)
object EMail{//apply方法用于無new構(gòu)造對象def apply(user: String, domain: String) = user + "@" + domain//unapply方法用于在模式匹配中充當(dāng)extractordef unapply(str: String): Option[(String, String)] = {val parts = str split "@"if (parts.length == 2) Some(parts(0), parts(1)) else None} } object ApplyAndUnapply {val email=EMail("zhouzhihubeyond","sina.com")//下面的匹配會導(dǎo)致調(diào)用EMail.unapply(email)case EMail(user,domain) => println("user="+user+" domain="+domain)} }- 1
在上述的代碼中,我們將unapply方法定義為:
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
這是有道理的,原因在于可能會有不合法的email用于模式匹配,例如:
object ApplyAndUnapply extends App{def patternMatching(x:String)=x match {//下面的匹配會導(dǎo)致調(diào)用EMail.unapply(email)case EMail(user,domain) => println("user="+user+" domain="+domain)//匹配非法郵箱case _ => println("non illegal email")}val email=EMail("zhouzhihubeyond","sina.com")patternMatching(email) patternMatching("搖擺少年夢") }從構(gòu)造與析構(gòu)的角度來看,apply方法也被稱為injection(注入),unapply方法也被稱為提取器,這兩個(gè)方法就像孿生兄弟一樣,經(jīng)常在類或?qū)ο笾斜欢x。以前我們在用類進(jìn)行模式匹配的時(shí)候都必須要將類聲明為case class,今天我們將不通過case class,而是定義一個(gè)普通的類實(shí)現(xiàn)自己的apply和unapply方法來實(shí)現(xiàn)模式匹配,代碼如下:
//定義一個(gè)普通類 class Person(val firstName:String,val secondName:String)//在伴生對象中定義apply方法和unapply方法 object Person{def apply(firstName: String, secondName: String) = new Person(firstName,secondName)def unapply(person: Person):Option[(String,String)]={if(person!=null) Some(person.firstName,person.secondName)else None} }val p=Person("搖擺少年夢","周")p match {//析構(gòu)出firstName,secondeNamecase Person(firstName,secondName) => println("firstName="+firstName+" secondName="+secondName)case _ => println("null object")}2. 零變量或單變量綁定的模式匹配
上一節(jié)講的模式模式匹配綁定的是兩個(gè)變量,它可以擴(kuò)展到任意變量維度,這一節(jié)中我們對零變量和單個(gè)變量綁定的特殊情況進(jìn)行介紹,我們來看下面的這個(gè)例子,該例子來源于 programmin in scala
//Twice用于匹配重復(fù)出現(xiàn)的字符串,它綁定的是一個(gè)變量 //即返回的類型是Option[String] object Twice {def apply(s: String): String = s + sdef unapply(s: String): Option[String] = {val length = s.length / 2val half = s.substring(0, length)if (half == s.substring(length)) Some(half) else None} } //未綁定任何變量,僅僅返回Boolean類型 object UpperCase {def unapply(s: String): Boolean = s.toUpperCase == s }object NonAndOneVariablePattern extends App{def userTwiceUpper(s: String) = s match {//下面的代碼相當(dāng)于執(zhí)行了下面這條語句//UpperCase.unapply(Twich.unapply(EMail.unapply(s)))case EMail(Twice(x @ UpperCase()), domain) =>"match: " + x + " in domain " + domaincase _ =>"no match"}val email=EMail("搖擺少年夢搖擺少年夢","sina.com")println(userTwiceUpper(email)) }代碼中的EMail(Twice(x @ UpperCase()),其執(zhí)行順序是先調(diào)用EMail的unapply方法,然后再調(diào)用Twice中的unapply方法,最后調(diào)用UpperCase的unapply方法,如果返回true,則將Twice 中返回的字符串賦值給x。
3. 提取器與序列模式
List伴生對象具有下列定義形式:
object List { def apply[T](elems: T*) = elems.toList def unapplySeq[T](x: List[T]): Option[Seq[T]] = Some(x) ... }從上面的代碼來看,與一般的提取器不同的是,序列模式采用unapplySeq代替unapply方法,并且返回的類型是Option[Seq[T]] ,在講模式匹配的時(shí)候我們提到過,序列模式中的匹配經(jīng)常會使用占位符_或_*的方式匹配序列中的其它元素,這種方式為序列模式所獨(dú)有,例如:
object ExtractorSequence extends App{val list=List(List(1,2,3),List(2,3,4))list match {//_*表示匹配列表中的其它元素case List(List(one,two,three),_*) => println("one="+one+" two="+two+" three="+three)case _ => println("Other")}list match {//_表示匹配列表中的第一個(gè)元素//_*表示匹配List中的其它多個(gè)元素//這里采用的變量綁定的方式case List(_,x@List(_*),_*) => println(x)case _ => println("other list")} }4. scala中的占位符使用總結(jié)
scala作為一種函數(shù)式編程語言,有很多地方會讓初學(xué)者覺得困惑,其中占位符_的使用理解有一定的難度,本節(jié)將對其使用進(jìn)行總結(jié),本小節(jié)內(nèi)容來源http://my.oschina.net/leejun2005/blog/405305,感謝作者的無私奉獻(xiàn)。
1、存在性類型:Existential types def foo(l: List[Option[_]]) = ...2、高階類型參數(shù):Higher kinded type parameters case class A[K[_],T](a: K[T])3、臨時(shí)變量:Ignored variables val _ = 54、臨時(shí)參數(shù):Ignored parameters List(1, 2, 3) foreach { _ => println("Hi") }5、通配模式:Wildcard patterns Some(5) match { case Some(_) => println("Yes") } match {case List(1,_,_) => " a list with three element and the first element is 1"case List(_*) => " a list with zero or more elements "case Map[_,_] => " matches a map with any key type and any value type "case _ =>} val (a, _) = (1, 2) for (_ <- 1 to 10)6、通配導(dǎo)入:Wildcard imports import java.util._7、隱藏導(dǎo)入:Hiding imports // Imports all the members of the object Fun but renames Foo to Bar import com.test.Fun.{ Foo => Bar , _ }// Imports all the members except Foo. To exclude a member rename it to _ import com.test.Fun.{ Foo => _ , _ }8、連接字母和標(biāo)點(diǎn)符號:Joining letters to punctuation def bang_!(x: Int) = 59、占位符語法:Placeholder syntax List(1, 2, 3) map (_ + 2) _ + _ ( (_: Int) + (_: Int) )(2,3)val nums = List(1,2,3,4,5,6,7,8,9,10)nums map (_ + 2) nums sortWith(_>_) nums filter (_ % 2 == 0) nums reduceLeft(_+_) nums reduce (_ + _) nums reduceLeft(_ max _) nums.exists(_ > 5) nums.takeWhile(_ < 8)10、偏應(yīng)用函數(shù):Partially applied functions def fun = {// Some code } val funLike = fun _List(1, 2, 3) foreach println _1 to 5 map (10 * _)//List("foo", "bar", "baz").map(_.toUpperCase()) List("foo", "bar", "baz").map(n => n.toUpperCase())11、初始化默認(rèn)值:default value var i: Int = _12、作為參數(shù)名: //訪問map var m3 = Map((1,100), (2,200)) for(e<-m3) println(e._1 + ": " + e._2) m3 filter (e=>e._1>1) m3 filterKeys (_>1) m3.map(e=>(e._1*10, e._2)) m3 map (e=>e._2)//訪問元組:tuple getters (1,2)._213、參數(shù)序列:parameters Sequence _*作為一個(gè)整體,告訴編譯器你希望將某個(gè)參數(shù)當(dāng)作參數(shù)序列處理。例如val s = sum(1 to 5:_*)就是將1 to 5當(dāng)作參數(shù)序列處理。 //Range轉(zhuǎn)換為List List(1 to 5:_*)//Range轉(zhuǎn)換為Vector Vector(1 to 5: _*)//可變參數(shù)中 def capitalizeAll(args: String*) = {args.map { arg =>arg.capitalize} }val arr = Array("what's", "up", "doc?") capitalizeAll(arr: _*)總結(jié)
以上是生活随笔為你收集整理的Scala入门到精通——第二十五节 提取器(Extractor)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Scala入门到精通——第二十四节 高级
- 下一篇: geometry-api-java 学习