【scala 笔记(3)】 控制结构 -- 模式匹配
2017-10-10 16:14
435 查看
scala 有一个十分强大的模式匹配机制, 可以应用在很多场合中, switch语句、 类型查询 , 以及 “析构”(获取复杂表达式中的不同部分)。 除此之外, scala 还提供了样例类, 对模式匹配进行了优化。
守卫可以是任意Boolean条件。
注意: 变量模式可能会与常量表达式有冲突, 那么scala 是如何判断常量和变量呢? 默认命名规则是 变量必须是小写字母开头; 如果你有一个小写字母的常量,则需要将它包含在反引号中。
unapply方法用于提取固定数量的对象;
unapplySeq方法用于提取一个序列;
自定义一个Postion实现提取函数:
正则表达式是另一个使用提取器的场景, 例如:
Regix 源码中定义的 unapplySeq
- Some(x) 的形式, 其中x 为实际值;
- None 对象, 代表缺失的值;
例如: Scala的 Map 的get 方法会在查找到key 的情况下返回 Some(value), 在没有找到key的时候返回None。
更好的switch
类似 C 风格的switch语法, 与default等效的是以 case _ 捕获所有情况, 若未进行 case _ 处理,在未能进行匹配到的情况下会抛出 MatchError异常。与switch不同的是你再也不用在每个分支后面添加一个break,以免掉入下一个分支scala> val func = (x:Int, y:Int, option:Char) => { | option match { | case '+' => x+y | case '-' => x-y | case '*' => x*y | case '/' => x/y | case _ => "unknow option" | } | } func: (Int, Int, Char) => Any = $$Lambda$1075/2124448375@6401d0a0 scala> func(10, 20, '+') res0: Any = 30
守卫
在一些情况下,我们可能希望match 像switch一样可以在匹配到特定的几个值的时候,处理同一个方法, 不用像在 match 中重复调用多次相同的代码等, 这时候我们可以为模式 添加一个守卫,该守卫可以处理更多的可能情况, 十分强大。守卫可以是任意Boolean条件。
scala> val func = (x:Int) => { | x match { | case 1 => | println("x = 1") | case 2 => | println("x = 2") | case _ if (x>2 && x<5) => | println( 4000 "x range (2, 5)") | case _ => | println("x >= 5") | } | } func: Int => Unit = $$Lambda$1095/1939970407@5e746d37 scala> func(3) x range (2, 5)
模式中的变量
如果case后面跟着是一个变量名,那么匹配的表达式会赋值给那个变量。注意: 变量模式可能会与常量表达式有冲突, 那么scala 是如何判断常量和变量呢? 默认命名规则是 变量必须是小写字母开头; 如果你有一个小写字母的常量,则需要将它包含在反引号中。
scala> val month = 10 month: Int = 10 scala> val func = (x:Int) => { | x match { | case 1 => | println("x = 1") | case `month` => | println("x = %d".format(month)) | case v if (v>2 && v<5) => | println("x range (2, 5)") | case _ => | println("x >= 5") | } | } func: Int => Unit = $$Lambda$1238/1843885967@367d34c0 scala> func(3) x range (2, 5) scala> func(10) x = 10 scala> func(11) x >= 5
for 表达式中的模式
在for推导式中,失败的匹配将被安静的忽略scala> val options = Map(1 -> "cpp", 2->"scala", 3-> "java", 4-> "scala") options: scala.collection.immutable.Map[Int,String] = Map(1 -> cpp, 2 -> scala, 3 -> java, 4 -> scala) scala> for ((k, "scala") <- options){ | println("key = %d".format(k)) | } key = 2 key = 4
类型模式
在scala 中进行类型判断时, 更倾向于使用模式匹配,而不是 isInstanceOf 操作符scala> val func = (obj:Any) => { | obj match { | case x: Int => | println("Int x = %d".format(x)) | case s: String => | println("String s = %s".format(s)) | case _ => | println("unknow obj ...") | } | } func: Any => Unit = $$Lambda$1249/1483228092@17134190 scala> func(10) Int x = 10 scala> func("10") String s = 10
匹配数组、列表和元组
匹配符合一定条件的数组、列表或元组, 如下样例:数组
scala> val func = (arr: Array[Int]) => { | arr match { | case Array(0, y) => | println("Int y = %d".format(y)) | case Array(x, 0) => | println("Int x = %d".format(x)) | case Array(1, _*) => // 匹配以1开始的数组 | println("array (1 ...)") | case _ => | println("other") | } | } func: Array[Int] => Unit = $$Lambda$1342/341315292@3f2a7ca0 scala> func(Array(0,10)) Int y = 10 scala> func(Array(12,0)) Int x = 12 scala> func(Array(1,0,1)) array (1 ...)
列表
scala> val func = (lst: List[Int]) => { | lst match { | case 0::Nil => | println("list(0)") | case 1::y::Nil => | println(y) | case 2::tail => | println(tail) | case _ => | println("other") | } | } func: List[Int] => Unit = $$Lambda$1390/1363130483@2a4a95c4 scala> func(0::Nil) list(0) scala> func(1::3::Nil) 3 scala> func(2::3::4::Nil) List(3, 4)
元组
scala> (0, 1) match { | case (0, _) => | println("0 ...") | case (_, 0) => | println("... 0") | case _ => | println("neither is 0") | } 0 ...
提取器
看到上面模式对数组、列表、元组的匹配,那么它是如何进行匹配的呢? 这些功能的背后是提取器(extractor)机制 – 带有从对象中提取值的 unapply或unapplySeq方法 的对象。unapply方法用于提取固定数量的对象;
// tuple2 的源码 final case class Tuple2[@specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T1, @specialized(Int, Long, Double, Char, Boolean/*, AnyRef*/) +T2](_1: T1, _2: T2) extends Product2[T1, T2] { override def toString() = "(" + _1 + "," + _2 + ")" /** Swaps the elements of this `Tuple`. * @return a new Tuple where the first element is the second element of this Tuple and the * second element is the first element of this Tuple. */ def swap: Tuple2[T2,T1] = Tuple2(_2, _1) } object Product2 { def unapply[T1, T2](x: Product2[T1, T2]): Option[Product2[T1, T2]] = Some(x) }
unapplySeq方法用于提取一个序列;
// list 源码 object List extends SeqFactory[List]{ ... } /** A template for companion objects of Seq and subclasses thereof. * * @since 2.8 */ abstract class SeqFactory[CC[X] <: Seq[X] with GenericTraversableTemplate[X, CC]] extends GenSeqFactory[CC] with TraversableFactory[CC] { /** This method is called in a pattern match { case Seq(...) => }. * * @param x the selector value * @return sequence wrapped in an option, if this is a Seq, otherwise none */ def unapplySeq[A](x: CC[A]): Some[CC[A]] = Some(x) }
自定义一个Postion实现提取函数:
class Position(val x:Int , val y: Int){ } object Position{ def apply(x: Int, y: Int): Position = new Position(x, y) // unapply 和 apply 恰恰相反 unapply接受参数是 Position对象, apply 接受的是初始化成员变量 def unapply(arg: Position): Option[(Int, Int)] = Some((arg.x,arg.y)) } object Test { def main(args: Array[String]): Unit = { val A = Position(10, 20) A match { case Position(x, y) => println("x = %s, y = %s ".format(x,y)) case _ => println("can't match! ") } } } // output: x = 10, y =20
正则表达式是另一个使用提取器的场景, 例如:
scala> val ipMatch = "([0-9]+).([0-9]+).([0-9]+).([0-9]+)".r ipMatch: scala.util.matching.Regex = ([0-9]+).([0-9]+).([0-9]+).([0-9]+) scala> scala> "192.186.1.10" match { | case ipMatch(v1, v2, v3, v4) => | println("%s:%s:%s:%s".format(v1, v2, v3, v4)) | case _ => | println("不能识别 ip 地址") | } 192:186:1:10
Regix 源码中定义的 unapplySeq
/** Tries to match a [[java.lang.CharSequence]]. * * If the match succeeds, the result is a list of the matching * groups (or a `null` element if a group did not match any input). * If the pattern specifies no groups, then the result will be an empty list * on a successful match. * * This method attempts to match the entire input by default; to find the next * matching subsequence, use an unanchored `Regex`. * * For example: * * {{{ * val p1 = "ab*c".r * val p1Matches = "abbbc" match { * case p1() => true // no groups * case _ => false * } * val p2 = "a(b*)c".r * val p2Matches = "abbbc" match { * case p2(_*) => true // any groups * case _ => false * } * val numberOfB = "abbbc" match { * case p2(b) => Some(b.length) // one group * case _ => None * } * val p3 = "b*".r.unanchored * val p3Matches = "abbbc" match { * case p3() => true // find the b's * case _ => false * } * val p4 = "a(b*)(c+)".r * val p4Matches = "abbbcc" match { * case p4(_*) => true // multiple a962 groups * case _ => false * } * val allGroups = "abbbcc" match { * case p4(all @ _*) => all mkString "/" // "bbb/cc" * case _ => "" * } * val cGroup = "abbbcc" match { * case p4(_, c) => c * case _ => "" * } * }}} * * @param s The string to match * @return The matches */ def unapplySeq(s: CharSequence): Option[List[String]] = s match { case null => None case _ => val m = pattern matcher s if (runMatcher(m)) Some((1 to m.groupCount).toList map m.group) else None }
模拟枚举
注意: 超类被声明为 sealed, 让编译器可以帮我们检查match 语法的完整性。 枚举实现主要依赖样例类scala> sealed abstract class Color defined class Color scala> case object Red extends Color defined object Red scala> case object Green extends Color defined object Green scala> case object Yellow extends Color defined object Yellow scala> val color:Color = Green color: Color = Green scala> color match { | case Red => | println("红色") | case Green => | println("蓝色") | case Yellow => | println("黄色") | case _ => | println("other") | } 蓝色
Option 类型
scala 标准库中的 Option类型用样例类来表示那种可能存在、也可能不存在的值。 它有两种表达形式:- Some(x) 的形式, 其中x 为实际值;
- None 对象, 代表缺失的值;
例如: Scala的 Map 的get 方法会在查找到key 的情况下返回 Some(value), 在没有找到key的时候返回None。
scala> val options = Map(1 -> "cpp", 2->"scala", 3-> "java") options: scala.collection.immutable.Map[Int,String] = Map(1 -> cpp, 2 -> scala, 3 -> java) scala> val func = (key :Int) => { | options.get(key) match { | case Some(v) => | println("value = " + v) | case _ => | println("key not exist") | } | } func: Int => Unit = $$Lambda$1349/71599579@1f6f0fe2 scala> func(3) value = java scala> func(12) key not exist
相关文章推荐
- Scala学习笔记——内建控制结构
- Scala学习教程笔记三之函数式编程、集合操作、模式匹配、类型参数、隐式转换、Actor、
- 快学Scala学习笔记及习题解答(12-14高阶函数、集合、模式匹配和样例类)
- Scala学习笔记-控制结构和函数(1)
- 【scala 笔记(4)】 控制结构 -- 自定义控制结构
- Scala入门学习笔记二-基本数据类型、程序控制结构
- Scala中模式匹配入门实战详解之Scala学习笔记-17
- 表示-抽象-控制——系统结构模式学习笔记
- Scala学习笔记20【Scala 模式匹配之case class实战】
- scala快速学习笔记(二):控制结构,类和对象
- Scala学习笔记--模式匹配
- 表示-抽象-控制——系统结构模式学习笔记
- scala学习笔记之模式匹配
- scala学习笔记(十三) 模式匹配与样例类
- Scala学习教程笔记三之函数式编程、集合操作、模式匹配、类型参数、隐式转换、Actor、
- Scala学习笔记19【Scala模式匹配入门实战】
- Scala学习笔记-控制结构和函数(2)
- 第74讲:从Spark源码的角度思考Scala中的模式匹配学习笔记
- Scala学习笔记21【Scala List之模式匹配实战】
- Programming In Scala笔记-第七章、Scala中的控制结构