2012-02-01 46 views
1
讀羅馬數字

我一直在玩的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

如果您只有數據類型中的實際數字,則表示I,V,X,C,D,M--您可以將整數作爲這些數字的列表。這會簡化一些事情。 – Sarah 2012-02-01 12:22:06

+0

我的原始方法僅限於I,V,X,C,D,M,並且stringToRomanNumeral函數返回這些項目。但是將IV,IX等作爲獨立的類型構造函數大大簡化了十進制數的轉換(此處未顯示)。 – janeden 2012-02-01 12:58:08

回答

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:[email protected](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) 
    ] 

它不是很靈活無論是任何性質惡劣會導致Nothing結果,但這是很容易改變的(一個可以使用catMaybes而不是sequence簡單地忽略例如無效字符)。它不檢查一般(現代)「遞減價值」規則(這使得'IX'解釋爲9而不是11)。檢查有效性可以在解析後完成。

+0

集成電路和XD按照慣例是不允許的,並被寫入XCIX和CDXC。 – danr 2012-02-01 15:48:34

+0

隔天學習新東西。謝謝。 – 2012-02-01 16:27:05

+0

有人告訴羅馬人嗎? – 2012-02-01 16:32:55

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 
+0

我明白了,但我仍然需要將數字符號映射到它們各自的十進制值。有一個包含一個Int值的RomanNumeral項目不能解決這個問題(或者說:我看不出它是怎麼做的)。 – janeden 2012-02-01 13:18:00