Scalaプログラムのリファクタリング
Scalaで、「ふつうのHaskell」に出てくるプログラムをScalaで作ってみたりしているけど、
こんな感じのプログラムをつくってみた
scala> expandCharClass("abc[123].mpg") res37: List[String] = List(abc1.mpg, abc2.mpg, abc3.mpg)
つまり、[]でかこっている部分を展開して文字列のリストを作るというイメージのコード。
バリバリ「オブジェクト脳」もしくは「手続き脳」の私が最初に書いた激ショボのコードは次の通り。
しかも、[が片方だけだと、例外が発生してしまうしょぼさ。さて、このしょぼいコードを
リファクタリングしてみる。
def expandCharClass(x:String):List[String] = x.indexOf('[') match { case -1 => List(x) case _ => expandChar(x) } } def expandChar(x:String):List[String] = { val start = x.indexOf('[') val end = x.indexOf(']') val partString = x.slice(start + 1 , end) val startString = x.slice(0, start) val endString = x.slice(end+1, x.length) partString.toList.map(y => startString + y + endString) }
特にexpandCharClassのパターンマッチも中途半端だし、
expandCharの関数でも、ダサい代入がある。このあたりを、Scalaの強力な正規表現とextractorを
使って書き直してみると下記の感じ
val pattern = """(.*)\[(.*)\](.*)""".r def expandCharClass(x:String):List[String] = pattern.unapplySeq(x) match { case Some(y) => expandChar(x) case None => List() } def expandChar(x:String):List[String] = { val pattern(start, ext, end) = x ext.toList.map(y => start + y + end) }
おお、かなりすっきり。特にexpandChar関数の中で、start, ext, end変数の束縛が
1行で済んでいるのが結構おしゃれですなぁ。
私も勉強中なので、もっとおしゃれにかける方法を知ってる人はコメント下さいね。
ちなみに、Scalaは、静的言語でかつ、遅延評価がデフォルトではない関数型言語
と、オブジェクト指向が融合したマルチパラダイムな言語です。こういう言語での
リファクタリングのパターンなんかができると、みんなもっとScalaらしいコードが
かきやすくなるかもですね。
20009年10月16日追記
Ha2さんがさらにおしゃれなコードにリファクタリングしてくれました。
ローカル関数を使うと、さらにイイ感じです。
def expandCharClass(x:String):List[String] = { val pattern = """(.*)\[(.*)\](.*)""".r def expandChar(x:String):List[String] = { val pattern(start, ext, end) = x ext.toList.map(y => start + y + end) } pattern.unapplySeq(x) match { case Some(y) => expandChar(x) case None => List() } }