9.10 把正则表达式当做提取器

Scala正则表达式提供了一个买一送一的选择。创建一个正则表达式,就附送一个提取器。Scala的正则表达式就是提取器,所以,很容易就可以在case表达式里使用。Scala会把每个放在括号里的匹配都展开到一个模式变量里。比如说,"(S|s)cala".r有一个unapply()方法,它会返回Option[String]。另一方面,"(S|s)(cala)".runapply()会返回Option[String, String]。用个例子来说明这一点,假定我们想对“GOOG”这只股票进行模式匹配,获取其价格。下面就是用正则表达式实现的方式:

PatternMatching/MatchUsingRegex.scala

  1. def process(input : String) {
  2. val GoogStock = """^GOOG:(\d*\.\d+)""".r
  3. input match {
  4. case GoogStock(price) => println("Price of GOOG is " + price)
  5. case _ => println("not processing " + input)
  6. }
  7. }
  8. process("GOOG:310.84")
  9. process("GOOG:310")
  10. process("IBM:84.01")

这里创建了一个正则表达式匹配字符串,这个字符串以“GOOG:”开头,后面跟着一个正的带小数的十进制数。将其存到一个叫GoogStockval里。幕后,Scala为这个提取器创建了一个unapply()方法,返回匹配到的括号里模式的值——price

  1. Price of GOOG is 310.84
  2. not processing GOOG:310
  3. not processing IBM:84.01

刚刚创建的提取器并不是可重用的。它会寻找股票代码“GOOG”,但如果要找其他股票代码,它就没用了。重用它并不困难。

  1. def process(input : String) {
  2. val MatchStock = """^(.+):(\d*\.\d+)""".r
  3. input match {
  4. case MatchStock("GOOG", price) => println("Price of GOOG is " + price)
  5. case MatchStock("IBM", price) => println("IBM's trading at " + price)
  6. case MatchStock(symbol, price) => printf("Price of %s is %s\n", symbol,
  7. price)
  8. case _ => println("not processing " + input)
  9. }
  10. }
  11. process("GOOG:310.84")
  12. process("IBM:84.01")
  13. process("GE:15.96")

上面的例子里,这个正则表达式匹配一个字符串,这个字符串以任意字符或数字开头,跟着一个冒号,然后是一个正的带小数的十进制数。生成的unapply()方法会把“:”前面的部分和后面的部分作为两个单独模式变量返回。这样一来,既可以匹配特定的股票,比如GOOG和IBM,也可以接收传进来的任意股票代码,如上面的case表达式所示。上面代码的输出如下:

  1. Price of GOOG is 310.84
  2. IBM's trading at 84.01
  3. Price of GE is 15.96

我们看到,Scala在模式匹配里使用正则表达式毫不费力。

在本章里,我们见识到了Scala最强大的特性之一。它现成的功能可以匹配简单的字面量、类型、元组、list等。如果想对匹配有多一点的控制,可以使用case类,或是无比迷人的提取器。我们见识了如何把正则表达式当作提取器。如果只想匹配简单的字面量,match就够用了;但如果要匹配任意的模式,Scala提取器就会大开方便之门。接下来,我们会看到Scala如何在并发编程里有效利用这个特性的。