Scalaで「アラビア数字・ローマ数字変換」
お題はこちら。
お題:アラビア数字・ローマ数字変換 - No Programming, No Life
テストは割愛。
ご覧のとおり、
- アラビア数字→ローマ数字の場合は、大きい単位から順に元の数字から引いていって、引けた記号を積むだけ
- ローマ数字→アラビア数字の場合は、左側から大きい順に並んでいると仮定して、その分の数字を加算していくだけ
という単純なもの。減算則はについては、該当する数字(900とか400とか)を二文字で一つの記号として扱うことで解決。
ようやく再帰を使ったコーディングにも慣れてきたけど、あんまり面白いコードは思いつかないなあ。
object ArabicRoman { val codeTable = List( (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), (50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")) def arabicToRoman(src: Int): String = { require((src >= 1) && (src <= 3999)) def convert(left: Int, cont: String = "", code: List[(Int, String)] = codeTable): String = { val (unitVal, unitChar) = code.head left - unitVal match { case n if (n == 0) => cont + unitChar case n if (n > 0) => convert(n, cont + unitChar, code) case _ => convert(left, cont, code.tail) } } convert(src) } def romanToArabic(src: String): Int = { require(src != null && src.nonEmpty) val p = src.toUpperCase() require("""[^MDCLXVI]""".r.findFirstMatchIn(p) == None) def convert(left: String, cont: Int = 0, code: List[(Int, String)] = codeTable): Int = { val (unitVal, unitChar) = code.head left.splitAt(unitChar.length) match { case ("", _) => cont case (`unitChar`, tail) => convert(tail, cont + unitVal, code) case _ => convert(left, cont, code.tail) } } convert(p) } }
(8/24 追記)
もしかして、引数の領域が定められていて、領域外の場合はエラー、みたいな場合は、アサーションにかけるよりPartialFunctionにすべきなのかも。
ということでPartialFunction版。ガードが長いとちと汚い。
object ArabicRoman { type =?>[A, B] = PartialFunction[A, B] val codeTable = List( (1000, "M"), (900, "CM"), (500, "D"), (400, "CD"), (100, "C"), (90, "XC"), (50, "L"), (40, "XL"), (10, "X"), (9, "IX"), (5, "V"), (4, "IV"), (1, "I")) val arabicToRoman: (Int) =?> String = { case src if (src >= 1 && src <= 3999) => { def convert(left: Int, cont: String = "", code: List[(Int, String)] = codeTable): String = { val (unitVal, unitChar) = code.head left - unitVal match { case n if (n == 0) => cont + unitChar case n if (n > 0) => convert(n, cont + unitChar, code) case _ => convert(left, cont, code.tail) } } convert(src) } } val romanToArabic: (String) =?> Int = { case src if (Option(src).exists{s => {s.nonEmpty && ("""[^MDCLXVI]""".r.findFirstMatchIn(s.toUpperCase) == None)}}) => { def convert(left: String, cont: Int = 0, code: List[(Int, String)] = codeTable): Int = { val (unitVal, unitChar) = code.head left.splitAt(unitChar.length) match { case ("", _) => cont case (`unitChar`, tail) => convert(tail, cont + unitVal, code) case _ => convert(left, cont, code.tail) } } convert(src.toUpperCase()) } } }