模式匹配
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"
})
本博客仅为博主学习总结,感谢各大网络平台的资料。蟹蟹!!