【scala 笔记(3)】 控制结构 -- 模式匹配

scala 有一个十分强大的模式匹配机制, 可以应用在很多场合中, switch语句、 类型查询 , 以及 “析构”(获取复杂表达式中的不同部分)。 除此之外, scala 还提供了样例类, 对模式匹配进行了优化。

更好的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("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 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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值