第13回Ruby勉強会@関西の演習課題をHaskellで解いてみました。そのに

id:ha-tan:20061218:1166371729の続きで整数をローマ数字の文字列に変換する関数を書いてみました。解答例のRubyのプログラムも結構いけてました。そのまま移植しても徳の高いHaskellのプログラムになりました。

module Main (main) where

import System (getArgs)
import System.IO (hPutStr, stderr)

oldRomanUnit :: [(Int, String)]
oldRomanUnit = 
  zip [1000, 500, 100, 50, 10, 5, 1] ["M", "D", "C", "L", "X", "V", "I"]

romanUnit :: [(Int, String)]
romanUnit =
  zip [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
      ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]

showRoman' :: [(Int, String)] -> Int -> String
showRoman' [] _            = []
showRoman' ((d, s) : xs) n = (concat $ replicate d' s) ++ showRoman' xs n'
  where
    (d', n') = n `divMod` d

showOldRoman :: Int -> String
showOldRoman =  showRoman' oldRomanUnit

showRoman :: Int -> String
showRoman =  showRoman' romanUnit

main :: IO ()
main = do
  args <- getArgs
  case args of
    ("-o" : xs) -> mapM_ (print . showOldRoman . read) xs
    ("-n" : xs) -> mapM_ (print . showRoman    . read) xs
    _           -> usage
  where
    usage = hPutStr stderr $ "usage: roman [OPTION] numbers...\n" ++ 
                             "\t-o old style.\n" ++
                             "\t-n new style.\n"

実行例:

$ ./roman.exe -o 1654
"MDCLIIII"
$ ./roman.exe -n 1654
"MDCLIV"

BUGS: showRomanで最短の文字列が返るとは限りません(999を与えても"IM"になりません。"CMXCIX"になります)。