模式匹配

模式匹配

switch语句


//switch
var sign = 0
for (i <- "abcdf") {
  i match {
    case 'a' => sign = 1
    case 'b' => sign = 2
    case 'c' => sign = 3
    case 'd' => sign = 4
    case _ => sign = -1
  }

  println(sign)
}

for (i <- "abce") {
  sign = i match {
    case 'a' => 1
    case 'b' => 2
    case 'c' => 3
    case _ => -1
  }

  println(sign)
}

import java.awt._
val color = SystemColor.textText
color match {
  case Color.RED => "Text is red"
  case Color.BLACK => "Text is Black"
  case _ => "Not red or Black"
}

守卫

像if表达式一样,match也提供守卫功能,守卫可以是任何Boolean条件。

//如果匹配,则把字符转换成10进制。

for (ch <- "+-3!") {
  var sign = 0
  var digit = 0

  ch match {
    case '+' => sign = 1
    case '-' => sign = -1
    //      判断是否是数字
    case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
    case _ => sign = 0
  }

  println(ch + " " + sign + " " + digit)
}

模式中的变量

val str = "+-3!"

// 返回str的一组索引Range 
for (i <- str.indices) {
  var sign = 0
  var digit = 0

  str(i) match {
    case '+' => sign = 1
    case '-' => sign = -1
    case ch if Character.isDigit(ch) => digit = Character.digit(ch, 10)
    case _ =>
  }

  println(str(i) + " " + sign + " " + digit)
}

import scala.math._

val x = random
x match {
  case Pi => "It's Pi"
  case _ => "It's not Pi"
}

// 变量必须以小写字母开头,常量用大写字母,如果常量用小写字母开头需要加反引号。
import java.io.File._

str match {
  case `pathSeparator` => "It's the path separator"
  case _ => "It's not the path separator"
}

类型模式

判断参数的类型


for (obj <- Array(42, "42", BigInt(42), BigInt, 42.0)) {

  val result = obj match {
    case x: Int => x
    case s: String => s.toInt
    case _: BigInt => Int.MaxValue
    case BigInt => -1
    case _ => 0
  }

  println(result)
}

// Map(42 -> "Fred")也映射到Map[String, Int],显然不对,运行期已经没有类型信息
for (obj <- Array(Map("Fred" -> 42), Map(42 -> "Fred"), Array(42), Array("Fred"))) {

  val result = obj match {
    case m: Map[String, Int] => "It's a Map[String, Int]"
    // Warning: Won't work because of type erasure
    case m: Map[_, _] => "It's a map"
    case a: Array[Int] => "It's an Array[Int]"
    case a: Array[_] => "It's an array of something other than Int"
  }

  println(result)
}

匹配数组、列表、元组

// 匹配数组
for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0))) {

  val result = arr match {
    case Array(0) => "0"
    case Array(x, y) => x + " " + y
    case Array(0, _*) => "0 ..."
    case _ => "something else"
  }

  println(result)
}

// 匹配列表
for (lst <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0))) {

  val result = lst match {
    case 0 :: Nil => "0"
    case x :: y :: Nil => x + " " + y
    case 0 :: tail => "0 ..."
    case _ => "something else"
  }

  println(result)
}

// 匹配元组
for (pair <- Array((0, 1), (1, 0), (1, 1))) {
  val result = pair match {
    case (0, _) => "0 ..."
    case (y, 0) => y + " 0"
    case _ => "neither is 0"
  }

  println(result)
}

提取器

模式如何匹配数组、列表和元组的,这个背后的机制叫做提取器,一个对象如果带有从对象中提取值的unapply和unapplySeq方法,apply方法接受构造参数,生成对象,而unapply方法接受对象,提取值,是一个反向操作。


val arr = Array(0, 1)

val Array(x,y) = arr

val Array(z,_*) = arr

arr match {
  case Array(0, x) => x
}

Array.unapplySeq(arr)

// 正则表达式对象
val pattern = "([0-9]+) ([a-z]+)".r

"99 bottles" match {
  case pattern(num, item) => (num.toInt, item)
}
// 直接unapplySeq
pattern.unapplySeq("99 bottles")


// unapply 提取值
object Name {
  def unapply(input: String) : Option[(String,String)] = {
    val pos = input.indexOf(" ")
    if (pos == -1) None
    else Some((input.substring(0, pos), input.substring(pos + 1)))
  }
}

val author = "Cay Horstmann"

val Name(first, last) = author // calls Name.unapply(author)
first
last

// 匹配上了
Name.unapply(author)
// 没有匹配上
Name.unapply("Anonymous")

// 单个提取
object Number {
  def unapply(input: String): Option[Int] =
    try {
      Some(input.trim.toInt)
    } catch {
      case ex: NumberFormatException => None
    }
}

val Number(n) = "1729"

// boolean测试,判断Horstmann
object IsCompound {
  def unapply(input: String) = {println(input); !input.contains(" ")}
}

author match {
  case Name(first, IsCompound()) => println("compound")
  // Matches if the author is Peter van der Linden
  case Name(first, last) => println("simple")
}

// Use @ to bind an identifier to the match
// 相当于一个守卫
author match {
  case Name(first, last @ IsCompound()) => println(last);last.split("\s+").length
  // Matches if the author is Peter van der Linden
  case Name(first, last) => 1
}

// unapplySeq 提取序列
object NameSeq {
  def unapplySeq(input: String): Option[Seq[String]] =
    if (input.trim == "") None else Some(input.trim.split("\s+"))
}

val authorseq = "Peter van der Linden"

// 将提取的序列与模式进行数量和字段上的比较。
authorseq match {
  case NameSeq(first, last) => authorseq
  case NameSeq(first, middle, last) => first + " " + last
  case NameSeq(first, "van", "der", last) => "Hello Peter!"
}

注意

如果要提取单个值,则应该返回一个目标类型的Option,例如Option[Int],而不是Option[(Int)];无参数的提取器可以用于boolean检查、

变量声明中的模式

//x=1 y=2
val (x, y) = (1, 2)
//q=3 r = 1
val (q, r) = BigInt(10) /% 3
val arr = Array(1, 7, 2, 9)
//first= 1 second = 7
val Array(first, second, _*) = arr

for表达式中的模式

import scala.collection.JavaConverters._
// Converts Java Properties to a Scala map—just to get an interesting example
for ((k, v) <- System.getProperties.asScala)
  println(k + " -> " + v)

// 忽略匹配失败的项目,打出所有value为空的条目
for ((k, "") <- System.getProperties.asScala)
  println(k)

// 通过守卫提取所有V等于空的属性。
for ((k, v) <- System.getProperties.asScala if v == "") 
  println(k)

样例类


abstract class Amount
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount

case object Nothing extends Amount

val dollar = Dollar(1000.0)

dollar.value

dollar.toString

for (amt <- Array(Dollar(1000.0), Currency(1000.0, "EUR"), Nothing)) {
  val result = amt match {
    case Dollar(v) => "$" + v
    case Currency(_, u) => "Oh noes, I got " + u
    case Nothing => ""
  }
  // Note that amt is printed nicely, thanks to the generated toString
  println(amt + ": " + result)
}

Copy方法和带名参数

copy创建一个与现有对象值相同的新对象,并可以通过带名参数来修改某些属性


abstract class Amount
case class Dollar(value: Double) extends Amount
case class Currency(value: Double, unit: String) extends Amount

case object Nothing extends Amount

val amt = Currency(29.95, "EUR")
val price = amt.copy(value = 19.95)
println(price)
println(amt.copy(unit = "CHF"))

Case语句的中置表达式


case class Currency(value: Double, unit: String)

val amt = Currency(1000.0, "EUR")

// 中置表达式
amt match { case a Currency u => a + " " + u }

val lst = List(1, 7, 2, 9)
lst match { 
  case h :: t => h + t.length 
  case _ => 0
} 

lst match { 
  case ::(h, t) => println(h); println(t)
  case _ => 0
}  

// :: 将元素添加到List最前面

// 从前往后匹配
List(1, 7, 2, 9) match { 
  case first :: second :: rest => println(first); println(second); println(rest)
  case _ => 0
}

List(1, 7, 2, 9) match { 
  case ::(first, ::(second, rest)) => println(first); println(second); println(rest)
  case _ => 0
}

List(List(1, 7), List(2, 9)) match { 
  case (first :: second) :: rest1 => println(first); println(second); println(rest1)
  case _ => 0
}

// Infix notation works with any binary unapply--doesn't have to 
// come from case class

case object +: {
  def unapply[T](input: List[T]) = 
    if (input.isEmpty) None else Some((input.head, input.tail))
}

1 +: 7 +: 2 +: 9 +: Nil match { 
  case first +: second +: rest => println(first); println(second); println(rest)
}

匹配嵌套结构

abstract class Item
case class Article(description: String, price: Double) extends Item
case class Bundle(description: String, discount: Double, items: Item*) extends Item

val special = Bundle("Father's day special", 20.0,
       Article("Scala for the", 39.95),
       Bundle("Anchor Distillery Sampler", 10.0,
              Article("Old Potrero Straight Rye Whiskey", 79.95),
              Article("Junípero Gin", 32.95)))

special match {
  case Bundle(_, _, Article(descr, _), _*) => descr
}

special match {
  case Bundle(_, _, art @ Article(_, _), rest @ _*) => (art, rest)
}

special match {
  case Bundle(_, _, art @ Article(_, _), rest) => (art, rest)
}

// 计算物品价格
def price(it: Item): Double = it match {
  case Article(_, p) => p
  case Bundle(_, disc, its @ _*) => its.map(price _).sum - disc
}

price(special)


密封类

如果想让case类的多有子类都必须在申明该类的相同文件中定义,可以将样例类的通用超类声明为sealed,叫做密封类。密封就是外部用户不能再其他文件中定义子类

模拟枚举


sealed abstract class TrafficLightColor

case object Red extends TrafficLightColor

case object Yellow extends TrafficLightColor

case object Green extends TrafficLightColor

for (color <- Array(Red, Yellow, Green))
  println(
    color match {
      case Red => "stop"
      case Yellow => "hurry up"
      case Green => "go"
    })

本博客仅为博主学习总结,感谢各大网络平台的资料。蟹蟹!!

原文地址:https://www.cnblogs.com/shaofeer/p/11154285.html