ローマ数字変換

えーと、じゃ、Scala で。
assert はテストとは違う気がするが、まぁいいか。

object RomanNumber {
  val romanDigits = List(
    ("M",  1000), ("CM",  900), ("D",   500),
    ("CD",  400), ("C",   100), ("XC",   90),
    ("L",    50), ("XL",   40), ("X",    10),
    ("IX",    9), ("V",     5), ("IV",    4),
    ("I",     1)
  )

  def parse(s: String): Int = {
    def parseImpl(s: String, digits: List[(String, Int)], result: Int) : Int =
      if (s.length == 0) 
        result
      else if (digits.isEmpty)
        throw new IllegalArgumentException()
      else if (s.startsWith(digits.head._1))
        parseImpl(s.substring(digits.head._1.length), digits, result + digits.head._2)
      else
        parseImpl(s, digits.tail, result)
    parseImpl(s.toUpperCase, romanDigits, 0)
  }

  def format(n: Int): String = {
    if (n < 1 || n > 3999)
      throw new IllegalArgumentException()

    def formatImpl(n: Int, digits: List[(String, Int)]): List[String] =
      if (n == 0)
        List()
      else if (n >= digits.head._2)
        digits.head._1 :: formatImpl(n - digits.head._2, digits)
      else
        formatImpl(n, digits.tail)
    
    def join(xs: List[Object]) = {
      val builder = new StringBuilder()
      for (x <- xs) {
        builder.append(x)
      }
      builder.toString()
    }

    join(formatImpl(n, romanDigits))
  }

  def main(args: Array[String]) {
    assert(format(11) == "XI")
    assert(parse("MDCCCLXXXVIII") == 1888)
    assert(parse("mdccclxxxviii") == 1888)
    assert(parse("McmXLv") == 1945)

    try {
      format(0)
      assert(false)
    }
    catch {
      case _: Throwable => Unit
    }

    try {
      parse("A")
      assert(false)
    }
    catch {
      case _: Throwable => Unit
    }
  }
}