戦略 Scala 日記

素人プログラマの思考のセンス

Scalaで全角数字を半角数字に変換。その再帰、たたみ込める?

全角数字を半角数字に置き換えるというよくある処理。
再帰関数を自分で定義して書くなら、次のようになります。

def fullWidthNumberToHalfWidthNumber(str: String): String = {
  val fullWidthNumbers = List("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")

  @scala.annotation.tailrec
  def convert(str: String, acc: Int): String =
    if (acc > 9) str else convert(str.replaceAll(fullWidthNumbers(acc), acc.toString), acc + 1)

  convert(str, 0)
}

再帰関数を定義している分、冗長になるためもう少し、シンプルに記述したいところです。
そこで、foldLeftを使って、これを書き直すと下記のように記述できます。

def fullWidthNumberToHalfWidthNumber(str: String): String = {
  val fullWidthNumbers = List("0", "1", "2", "3", "4", "5", "6", "7", "8", "9")

  fullWidthNumbers.zipWithIndex.foldLeft(str){ case (x, (c, i)) => x.replaceAll(c, i.toString) }
}

一度、fullWidthNumbers.zipIndexとすることで、List(("1", 1), ("2", 2), ...)のようなインデックス付きのリストを作っています。

また、foldLeftに渡す関数の引数についても{ case (x, (c, i)) => }このような省略をできるのもScalaの便利なところ。
省略をしなければ次のような意味を持ちます。これを、caseから書き始められるのは非常に便利です。

(str, tuple) => (str, tuple) match {
  case (x, (c, i)) => // ...
}

再帰関数を定義しようとするときは、いったんfoldLeftなどたたみ込み関数で代用出来ないか考えてみると、よいかもしれません。