zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Scala入门到精通——第二十五节 提取器(Extractor)

scala入门 精通 提取
2023-09-14 09:00:25 时间

apply方法我们已经非常熟悉了,它帮助我们无需new操作就可以创建对象,而unapply方法则用于析构出对象,在模式匹配中特别提到,如果一个类要能够应用于模式匹配当中,必须将类声明为case class,因为一旦被定义为case class,scala会自动帮我们生成相应的方法,这些方法中就包括apply方法及unapply方法。本节将从提取器(也称析构器)的角度对unapply方法进行介绍。先看下面的这个例子(来源于programmin in scala)


//apply方法用于无new构造对象 def apply(user: String, domain: String) = user + "@" + domain //unapply方法用于在模式匹配中充当extractor def 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") //下面的匹配会导致调用EMail.unapply(email) case EMail(user,domain) = println("user="+user+" domain="+domain) }

在上述的代码中,我们将unapply方法定义为:
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2) Some(parts(0), parts(1)) else None
}

这是有道理的,原因在于可能会有不合法的email用于模式匹配,例如:


//下面的匹配会导致调用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("摇摆少年梦") }

从构造与析构的角度来看,apply方法也被称为injection(注入),unapply方法也被称为提取器,这两个方法就像孪生兄弟一样,经常在类或对象中被定义。以前我们在用类进行模式匹配的时候都必须要将类声明为case class,今天我们将不通过case class,而是定义一个普通的类实现自己的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
//析构出firstName,secondeName case Person(firstName,secondName) = println("firstName="+firstName+" secondName="+secondName) case _ = println("null object")

上一节讲的模式模式匹配绑定的是两个变量,它可以扩展到任意变量维度,这一节中我们对零变量和单个变量绑定的特殊情况进行介绍,我们来看下面的这个例子,该例子来源于 programmin in scala


val 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 { //下面的代码相当于执行了下面这条语句 //UpperCase.unapply(Twich.unapply(EMail.unapply(s))) case EMail(Twice(x @ UpperCase()), domain) = "match: " + x + " in domain " + domain case _ = "no match" val email=EMail("摇摆少年梦摇摆少年梦","sina.com") println(userTwiceUpper(email)) }

代码中的EMail(Twice(x @ UpperCase()),其执行顺序是先调用EMail的unapply方法,然后再调用Twice中的unapply方法,最后调用UpperCase的unapply方法,如果返回true,则将Twice 中返回的字符串赋值给x。

3. 提取器与序列模式

List伴生对象具有下列定义形式:


def apply[T](elems: T*) = elems.toList def unapplySeq[T](x: List[T]): Option[Seq[T]] = Some(x) }

从上面的代码来看,与一般的提取器不同的是,序列模式采用unapplySeq代替unapply方法,并且返回的类型是Option[Seq[T]] ,在讲模式匹配的时候我们提到过,序列模式中的匹配经常会使用占位符_或_*的方式匹配序列中的其它元素,这种方式为序列模式所独有,例如:


scala作为一种函数式编程语言,有很多地方会让初学者觉得困惑,其中占位符_的使用理解有一定的难度,本节将对其使用进行总结,本小节内容来源http://my.oschina.net/leejun2005/blog/405305,感谢作者的无私奉献。


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、通配导入:Wildcard imports import java.util._ 7、隐藏导入: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、连接字母和标点符号:Joining letters to punctuation def bang_!(x: Int) = 5 9、占位符语法: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、偏应用函数: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、初始化默认值:default value var i: Int = _ 12、作为参数名: //访问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)._2 13、参数序列:parameters Sequence _*作为一个整体,告诉编译器你希望将某个参数当作参数序列处理。例如val s = sum(1 to 5:_*)就是将1 to 5当作参数序列处理。 //Range转换为List List(1 to 5:_*) //Range转换为Vector Vector(1 to 5: _*) //可变参数中 def capitalizeAll(args: String*) = { args.map { arg = arg.capitalize val arr = Array("whats", "up", "doc?") capitalizeAll(arr: _*)

添加公众微信号,可以了解更多最新Spark、Scala相关技术资讯
这里写图片描述


十分钟带汝入门大数据开发语言Scala Scala是一门多范式的编程语言,一种类似Java的编程语言 ,设计初衷是实现可伸缩的语言 、并集成面向对象编程和函数式编程的各种特性。目前最主流的大数据开发框架Spark的实现就是通过Scala去实现的。Scala可以与Java互操作。它用scalac这个编译器把源文件编译成Java的class文件(即在JVM上运行的字节码),也可以从Scala中调用所有的Java类库,也同样可以从Java应用程序中调用Scala的代码。
Scala入门系列终章:类与对象 截至本篇,Scala入门系列即将告一段落,先后用7篇文章将Scala语言的四梁八柱进行了简要介绍,虽然阅读量相较于其他文章更为惨淡,但相信对于有一定编程语言基础又想快速入门Scala的读者来说还是会有一定收获和借鉴的。 本文作为该系列的最后一篇,将重点介绍Scala中类和对象的概念,毕竟Scala也是一门面向对象的编程语言。 坦白讲,个人在学习Scala中这一部分内容时其实也是有些凌乱的,一直觉得未能理解到Scala中类和对象的精髓,所以当下完成此文也仅仅是出于系列内容的完整性,后续也将适时推出其他分享。
Flink 项目Scala模板入门 Flink大数据项目可以通过Scala语言进行构建,本文主要介绍一下如何通过Maven命令来快速生成Scala语言的Flink初始项目,并给出一个启动示例项目。