Extractorを使った正規表現ライブラリの簡易ラッパー
Scalaの正規表現ライブラリは、Javaの正規表現ライブラリのラッパーになっていて、Javaのものよりもだいぶ使いやすくはなっているが、パターンマッチに使う時は、以下のように必ずvalなどでいったん変数に代入しなければならないという欠点がある。
scala> val numPat = """[0-9]+""".r numPat: scala.util.matching.Regex = [0-9]+ scala> "123" match { case numPat() => println("matched") } matched
そこで、簡単な場合には、パターンにいちいち名前を付けずに済むようなライブラリをExtractorを使って書いてみた。これを使うと、たとえば上のコードは次のように書ける:
scala> ("123", """[0-9]+""") match { case Success() => println("matched") } matched
他にも、部分一致やプレフィクスに対するマッチを行うオプション、文字列から整数や浮動小数点数への変換を行うExtractorも用意してあり、以下のように書くこともできる。
scala> ("abc123", """([0-9]+)""", Part) match { | case Success(PInt(num)) => println(num + 1) | } 124 scala> ("1.23abc", """([0-9]+.[0-9]+)""", Pre) match { | case Success(PFloat(num)) => println(num - 2) | } -0.77
コードは、以下。まあ、何が言いたかったかというと、Extractorは使いようによっては結構便利なので、色々応用例を考えてみると良いのではないかということ。
object RegexPatterns { import scala.util.matching._ object PFloat { def unapply(s: String): Option[Float] = try { Some(s.toFloat) } catch { case _:NumberFormatException => None } } object PInt { def unapply(s: String): Option[Int] = try { Some(s.toInt) } catch { case _:NumberFormatException => None } } abstract sealed class MatchingMode case object Pre extends MatchingMode case object Part extends MatchingMode case object All extends MatchingMode object Success { def unapplySeq(arg: (String, String, MatchingMode)): Option[List[String]] = { val (str, pat) = (arg._1, arg._2.r) arg._3 match { case Pre => for(matched <- pat findPrefixMatchOf str) yield (1 to matched.groupCount) map (matched group _) toList case Part => for(matched <- pat findFirstMatchIn str) yield (1 to matched.groupCount) map (matched group _) toList case All => pat.unapplySeq(str) } } def unapplySeq(arg: (String, String)): Option[List[String]] = unapplySeq((arg._1, arg._2, All)) } }