第9章——模式匹配和正则表达式
模式匹配(pattern matching)在 Scala 被广泛使用的特性中排在第二位,仅次于函数值和 闭包。Scala 对于模式匹配的出色支持意味着,在并发编程中在处理 Actor 接收到的消息时, 将会大量地使用它。在本章中,我们将学到 Scala 的模式匹配的机制、case 类和提取器,以 及如何创建和使用正则表达式。
9.1 模式匹配综述
PatternMatching/MatchLiterals.scala
def activity(day: String): Unit = {
day match {
case "Sunday" => print("Eat, sleep, repeat... ")
case "Saturday" => print("Hang out with friends... ")
case "Monday" => print("...code for fun...")
case "Friday" => print("...read a good book...")
}
}
List("Monday", "Sunday", "Saturday").foreach { activity }
运行结果
...code for fun...Eat, sleep, repeat... Hang out with friends...
PatternMatching/Wildcard.scala
object DayOfWeek extends Enumeration {
val SUNDAY: DayOfWeek.Value = Value("Sunday")
val MONDAY: DayOfWeek.Value = Value("Monday")
val TUESDAY: DayOfWeek.Value = Value("Tuesday")
val WEDNESDAY: DayOfWeek.Value = Value("Wednesday")
val THURSDAY: DayOfWeek.Value = Value("Thursday")
val FRIDAY: DayOfWeek.Value = Value("Friday")
val SATURDAY: DayOfWeek.Value = Value("Saturday")
}
def activity(day: DayOfWeek.Value): Unit = {
day match {
case DayOfWeek.SUNDAY => println("Eat, sleep, repeat...")
case DayOfWeek.SATURDAY => println("Hang out with friends")
case _ => println("...code for fun...")
}
}
activity(DayOfWeek.SATURDAY)
activity(DayOfWeek.MONDAY)
运行结果
Hang out with friends
...code for fun...
PatternMatching/MatchTuples.scala
def processCoordinates(input: Any): Unit = {
input match {
case (lat, long) => printf("Processing (%d, %d)...", lat, long)
case "done" => println("done")
case _ => println("invalid input")
}
}
processCoordinates((39, -104))
processCoordinates("done")
运行结果
Processing (39, -104)...done
PatternMatching/MatchList.scala
def processItems(items: List[String]): Unit = {
items match {
case List("apple", "ibm") => println("Apples and IBMs")
case List("red", "blue", "white") => println("Stars and Stripes...")
case List("red", "blue", _*) => println("colors red, blue,... ")
case List("apple", "orange", otherFruits @ _*) =>
println("apples, oranges, and " + otherFruits)
}
}
processItems(List("apple", "ibm"))
processItems(List("red", "blue", "green"))
processItems(List("red", "blue", "white"))
processItems(List("apple", "orange", "grapes", "dates"))
运行结果
Apples and IBMs
colors red, blue,...
Stars and Stripes...
apples, oranges, and List(grapes, dates)
PatternMatching/MatchTypes.scala
def process(input: Any): Unit = {
input match {
case (_: Int, _: Int) => print("Processing (int, int)... ")
case (_: Double, _: Double) => print("Processing (double, double)... ")
case msg: Int if msg > 1000000 => println("Processing int > 1000000")
case _: Int => print("Processing int... ")
case _: String => println("Processing string... ")
case _ => printf(s"Can't handle $input... ")
}
}
process((34.2, -159.3))
process(0)
process(1000001)
process(2.2)
运行结果
Processing (double, double)... Processing int... Processing int > 1000000
Can't handle 2.2...
9.2 case 表达式中的模式变量和常量
PatternMatching/MatchWithField.scala
class Sample {
val max = 100
def process(input: Int): Unit = {
input match {
case max => println(s"You matched max $max")
}
}
}
val sample = new Sample
try {
sample.process(0)
} catch {
case ex: Throwable => println(ex)
}
sample.process(100)
运行结果
You matched max 0
You matched max 100
PatternMatching/MatchWithField1.scala
case this.max => println(s"You matched max $max")
运行结果
scala.MatchError: 0 (of class java.lang.Integer)
You matched max 100
PatternMatching/MatchWithField2.scala
case `max` => println(s"You matched max $max")
运行结果
scala.MatchError: 0 (of class java.lang.Integer)
You matched max 100
PatternMatching/MatchWithValsOK.scala
class Sample {
val MAX = 100
def process(input: Int): Unit = {
input match {
case MAX => println("You matched max")
}
}
}
val sample = new Sample
try {
sample.process(0)
} catch {
case ex: Throwable => println(ex)
}
sample.process(100)
运行结果
scala.MatchError: 0 (of class java.lang.Integer)
You matched max
9.3 使用 case 类进行模式匹配
PatternMatching/TradeStock.scala
trait Trade
case class Sell(stockSymbol: String, quantity: Int) extends Trade
case class Buy(stockSymbol: String, quantity: Int) extends Trade
case class Hedge(stockSymbol: String, quantity: Int) extends Trade
PatternMatching/TradeStock.scala
object TradeProcessor {
def processTransaction(request: Trade): Unit = {
request match {
case Sell(stock, 1000) => println(s"Selling 1000-units of $stock")
case Sell(stock, quantity) =>
println(s"Selling $quantity units of $stock")
case Buy(stock, quantity) if quantity > 2000 =>
println(s"Buying $quantity (large) units of $stock")
case Buy(stock, quantity) =>
println(s"Buying $quantity units of $stock")
}
}
}
PatternMatching/TradeStock.scala
TradeProcessor.processTransaction(Sell("GOOG", 500))
TradeProcessor.processTransaction(Buy("GOOG", 700))
TradeProcessor.processTransaction(Sell("GOOG", 1000))
TradeProcessor.processTransaction(Buy("GOOG", 3000))
运行结果
Selling 500 units of GOOG
Buying 700 units of GOOG
Selling 1000-units of GOOG
Buying 3000 (large) units of GOOG
PatternMatching/ThingsAcceptor.scala
case class Apple()
case class Orange()
case class Book()
object ThingsAcceptor {
def acceptStuff(thing: Any): Unit = {
thing match {
case Apple() => println("Thanks for the Apple")
case Orange() => println("Thanks for the Orange")
case Book() => println("Thanks for the Book")
case _ => println(s"Excuse me, why did you send me $thing")
}
}
}
PatternMatching/ThingsAcceptor.scala
ThingsAcceptor.acceptStuff(Apple())
ThingsAcceptor.acceptStuff(Book())
ThingsAcceptor.acceptStuff(Apple)
运行结果
Thanks for the Apple
Thanks for the Book
Excuse me, why did you send me Apple
PatternMatching/ThingsAcceptor2.scala
abstract class Thing
case class Apple() extends Thing
object ThingsAcceptor {
def acceptStuff(thing: Thing) {
thing match {
//...
case _ =>
}
}
}
ThingsAcceptor.acceptStuff(Apple) //error: type mismatch;
9.4 提取器和正则表达式
PatternMatching/Extractor1.scala
StockService.process("GOOG")
StockService.process("IBM")
StockService.process("ERR")
PatternMatching/Extractor1.scala
object StockService {
def process(input: String): Unit = {
input match {
case Symbol() => println(s"Look up price for valid symbol $input")
case _ => println(s"Invalid input $input")
}
}
}
PatternMatching/Extractor1.scala
object Symbol {
def unapply(symbol: String): Boolean = {
// you'd look up a database... here only GOOG and IBM are recognized
symbol == "GOOG" || symbol == "IBM"
}
}
运行结果
Look up price for valid symbol GOOG
Look up price for valid symbol IBM
Invalid input ERR
PatternMatching/Extractor.scala
object StockService {
def process(input: String): Unit = {
input match {
case Symbol() => println(s"Look up price for valid symbol $input")
case ReceiveStockPrice(symbol, price) =>
println(s"Received price $$$price for symbol $symbol")
case _ => println(s"Invalid input $input")
}
}
}
PatternMatching/Extractor.scala
object ReceiveStockPrice {
def unapply(input: String): Option[(String, Double)] = {
try {
if (input contains ":") {
val splitQuote = input.split(":")
Some((splitQuote(0), splitQuote(1).toDouble))
} else {
None
}
} catch {
case _: NumberFormatException => None
}
}
}
PatternMatching/Extractor.scala
StockService.process("GOOG")
StockService.process("GOOG:310.84")
StockService.process("GOOG:BUY")
StockService.process("ERR:12.21")
运行结果
Look up price for valid symbol GOOG
Received price $310.84 for symbol GOOG
Invalid input GOOG:BUY
Received price $12.21 for symbol ERR
PatternMatching/Extractor2.scala
case ReceiveStockPrice(symbol @ Symbol(), price) =>
println(s"Received price $$$price for symbol $symbol")
运行结果
Look up price for valid symbol GOOG
Received price $310.84 for symbol GOOG
Invalid input GOOG:BUY
Invalid input ERR:12.21
PatternMatching/RegularExpr.scala
val pattern = "(S|s)cala".r
val str = "Scala is scalable and cool"
println(pattern.findFirstIn(str))
PatternMatching/RegularExpr.scala
println(pattern.findAllIn(str).mkString(", "))
PatternMatching/RegularExpr.scala
println("cool".r.replaceFirstIn(str, "awesome"))
运行结果
Some(Scala)
Scala, scala
Scala is scalable and awesome
PatternMatching/MatchUsingRegex.scala
def process(input: String): Unit = {
val GoogStock = """^GOOG:(\d*\.\d+)""".r
input match {
case GoogStock(price) => println(s"Price of GOOG is $$$price")
case _ => println(s"not processing $input")
}
}
process("GOOG:310.84")
process("GOOG:310")
process("IBM:84.01")
运行结果
Price of GOOG is $310.84
not processing GOOG:310
not processing IBM:84.01
PatternMatching/MatchUsingRegex2.scala
def process(input: String): Unit = {
val MatchStock = """^(.+):(\d*\.\d+)""".r
input match {
case MatchStock("GOOG", price) => println(s"We got GOOG at $$$price")
case MatchStock("IBM", price) => println(s"IBM's trading at $$$price")
case MatchStock(symbol, price) => println(s"Price of $symbol is $$$price")
case _ => println(s"not processing $input")
}
}
process("GOOG:310.84")
process("IBM:84.01")
process("GE:15.96")
运行结果
We got GOOG at $310.84
IBM's trading at $84.01
Price of GE is $15.96