Task

Create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer.

You don't need to validate the form of the Roman numeral.

Modern Roman numerals are written by expressing each decimal digit of the number to be encoded separately,

starting with the leftmost decimal digit and skipping any 0s   (zeroes).

1990 is rendered as   MCMXC     (1000 = M,   900 = CM,   90 = XC)     and

2008 is rendered as   MMVIII       (2000 = MM,   8 = VIII).

The Roman numeral for 1666,   MDCLXVI,   uses each letter in descending order.




 
module Main where
 
------------------------
--  DECODER FUNCTION  --
------------------------
 
decodeDigit :: Char -> Int
decodeDigit 'I' = 1
decodeDigit 'V' = 5
decodeDigit 'X' = 10
decodeDigit 'L' = 50
decodeDigit 'C' = 100
decodeDigit 'D' = 500
decodeDigit 'M' = 1000
decodeDigit _ = error "invalid digit"
 
--  We process a Roman numeral from right to left, digit by digit, adding the value.
--  If a digit is lower than the previous then its value is negative.
--  The first digit is always positive.
 
decode roman = decodeRoman startValue startValue rest
  where
    (first:rest) = reverse roman
    startValue = decodeDigit first
 
decodeRoman :: Int -> Int -> [Char] -> Int
decodeRoman lastSum _ [] = lastSum
decodeRoman lastSum lastValue (digit:rest) = decodeRoman updatedSum digitValue rest
  where
    digitValue = decodeDigit digit
    updatedSum = (if digitValue < lastValue then (-) else (+)) lastSum digitValue
 
------------------
--  TEST SUITE  --
------------------
 
main = do
  test "MCMXC" 1990
  test "MMVIII" 2008
  test "MDCLXVI" 1666
 
test roman expected = putStrLn (roman ++ " = " ++ (show (arabic)) ++ remark)
  where
    arabic = decode roman
    remark = " (" ++ (if arabic == expected then "PASS" else ("FAIL, expected " ++ (show expected))) ++ ")"
 