在Haskell中读取罗马数字

时间:2012-02-01 11:42:26

标签: haskell

过去几天我一直在使用Haskell数据类型,使用自定义类型处理罗马数字:

data RomanNumeral = I | IV | V | IX | X | XL | L | XC | C | CD | D | CM | M deriving (Eq, Ord, Show, Read)

stringToRomanNumeral :: String -> Maybe [RomanNumeral]
stringToRomanNumeral romString
    | unRoman = Nothing
    | otherwise = Just $ map read $ map (\x -> [x]) romStringUpper
    where 
        romStringUpper = map C.toUpper romString
        unRoman = any (`notElem` "MDCLXVI") romStringUpper

这样可以正常工作,但只捕获1个字符数字(因此我必须稍后计算IV,IX等的值)。

有没有办法让read(或reads)输入字符串,以使Maybe [RomanNumeral]的返回值也包含2个字符数字?我尝试了模式匹配,但我似乎无法正确使用类型。

2 个答案:

答案 0 :(得分:3)

使用reads效果不佳,因为它预计令牌,它不会分裂,例如"XIV" "X" "IV"module Roman where import Data.Char as C data RomanNumeral = I | IV | V | IX | X | XL | L | XC | C | CD | D | CM | M deriving (Eq, Ord, Show, Read) stringToRomanNumeral :: String -> Maybe [RomanNumeral] stringToRomanNumeral = fmap collate . sequence . map (toRom . C.toUpper) where romDict = zip "IVXLCDM" [I,V,X,L,C,D,M] toRom = flip lookup romDict collate :: [RomanNumeral] -> [RomanNumeral] collate (x:ys@(y:zs)) = case lookup (x,y) collationDict of Just v -> v : collate zs Nothing -> x : collate ys collate xs = xs collationDict :: [((RomanNumeral,RomanNumeral),RomanNumeral)] collationDict = [ ((I,V),IV) , ((I,X),IX) , ((X,L),XL) , ((X,C),XC) , ((C,D),CD) , ((C,M),CM) ] 获取两个可解析部分,它将整个char序列视为一个标记,因为它们属于同一个字符类。您可以为罗马数字编写自己的解析器(您应该尝试编写解析器很有趣)处理特殊序列。

一种简单的方法是

Nothing

它也不是很灵活,任何不良角色都会导致catMaybes结果,但这很容易修改(可以使用sequence代替{{1}}来简单地忽略无效字符,例如)。并且它不检查一般(现代)'递减值'规则(这使得将'IX'解释为9而不是11可能)。然而,在解析之后可以检查有效性。

答案 1 :(得分:0)

我认为您使用RomanNumeral数据类型的当前方式本质上是一个坏主意。

您还应该覆盖show / read,而不是依赖于默认设置。

也许

-- underlying datatype as int
data RomanNumeral = RomanNumeral Int

instance Show RomanNumeral where
    show (RomanNumeral x) = -- your code for converting a roman numeral to a string

instance Read RomanNumeral where
    readsPred d r = -- your code for reading a string into an integer and then return a RomanNumeral