所以,我在问题表中有这个有趣的实践练习: 将整数(小于5000)转换为罗马数字。 这是我写的代码;但是,我在GHCI中加载脚本时遇到困难(输入`='时解析错误)。有什么想法吗?
units, tens, hundreds, thousands :: [String]
units=["I", "II", "III", "IV", "V", "VI", "VII", "IIX", "IX"]
tens=["X", "XX", "XXX", "XL", "L", "LX", "LXX", "XXC", "XC"]
hundreds=["C", "CC", "CCC", "CD", "D", "DC", "DCC", "CCM","CM"]
thousands=["M", "MM", "MMM", "MV", "V"]
combine :: (Int,Int,Int,Int) -> String
combine (0,0,0,u) = units !! u
combine (0,0,t+1,0) = tens !! t
combine (0,0,t+1,u) = tens !! t ++ units !! u
combine (0,h+1,0,0) = hundreds !! h
combine (0,h+1,t+1,0) = hundreds !! h ++ tens !! t
combine (0,h+1,t+1,u) = hundreds !! h ++ tens !! t ++ units !! u
combine (f+1,0,0,0) = thousands !! f
combine (f+1,h+1,0,0) = thousands !! f ++ hundreds !! h
combine (f+1,h+1,t+1,0) = thousands !! f ++ hundreds !! h ++ tens !! t
combine (f+1,h+1,t+1,u) = thousands !! f ++ hundreds !! h ++ tens !! t ++ units !! u
答案 0 :(得分:2)
这个程序实际上有几个语法错误(编辑:感谢@Lukasz的编辑,现在只有一个语法错误)。但是您要问的问题是由于您不能在ghci
中创建绑定。您编写的程序中的位置
a = 1
在ghci
中你必须写
let a = 1
否则您将收到parse error on input `='
错误。
我建议您将程序放在一个文件中并使用ghc
进行编译,或者使用runhaskell
进行编译,而不是插入let
,这对将来会更方便工作和错误修正。
答案 1 :(得分:0)
我编写了一个模块来处理整数和罗马数字之间的转换。但是,我的模块有3个缺点。
我的模块可以处理的最大罗马数字不会超过 超过4999,因为我认为最大的罗马单位是" M"和 " MMMM"根据规则不是有效的罗马数字。
我没有在函数中使用Maybe" findKey"避免意外的键,因为我没有很好地掌握applicative,functor和monad。
我不知道如何完成prettyRoman2,它将寻找组合" VIV"," LXL"和" DCD"在罗马数字的字符串中,用" IX"," XC"替换它们。和" CM"分别
module Roman
( roman2Dec
, dec2Roman
) where
import Data.List (isInfixOf)
-- The max number the program can deal with will not exceed than 4999
romanUnits :: [(Char, Int)]
romanUnits = [('I', 1), ('V', 5), ('X', 10), ('L', 50), ('C', 100), ('D', 500), ('M', 1000)]
romanDividers :: [Int]
romanDividers = reverse $ map snd romanUnits
romanDigits :: [Char]
romanDigits = reverse $ map fst romanUnits
-- short divide n by each of those in dividers
shortDivide :: Int -> [Int] -> [Int]
shortDivide n [] = []
shortDivide n dividers = let (quotient, remainder) = n `divMod` (head dividers)
in quotient : shortDivide remainder (tail dividers)
dec2Roman :: Int -> String
dec2Roman n = concat $ map prettyRoman1 (zipWith (\x y -> replicate x y) (shortDivide n romanDividers) romanDigits)
prettyRoman1 :: String -> String
prettyRoman1 roman
| roman == "IIII" = "IV"
| roman == "XXXX" = "XL"
| roman == "CCCC" = "CD"
| otherwise = roman
-- prettyRoman2: Replace VIV, LXL, DCD with IX, XC, and CM respectively.
-- After that, the dec2Roman will be modifed as dec2Roman' = prettyRoman2 dec2Roman
prettyRoman2 :: String -> String
prettyRoman2 = undefined
findKey :: Eq a => a -> [(a, b)] -> b
findKey key = snd . head . filter (\(k, v) -> k == key)
romanValue :: Char -> Int
romanValue c = findKey c romanUnits
roman2Dec :: String -> Int
roman2Dec [] = 0
roman2Dec [x] = romanValue x
roman2Dec (x:y:xs)
| romanValue x < romanValue y = (-1) * romanValue x + roman2Dec (y:xs)
| otherwise = romanValue x + roman2Dec (y:xs)
答案 2 :(得分:0)
有点晚了,但仅仅是为了记录,我已将我的代码从JS移植到Haskell。我相信它是最有效的整数罗马数字转换器之一。但截至目前,它只能达到3999美元。
import qualified Data.Map.Lazy as M
import Data.Bool (bool)
import Data.List (unfoldr)
numerals :: M.Map Int Char
numerals = M.fromList [(0,'I'),(1,'V'),(2,'X'),(3,'L'),(4,'C'),(5,'D'),(6,'M')]
toDigits :: Int -> [Int]
toDigits = unfoldr (\x -> bool Nothing (Just (rem x 10, div x 10)) (x > 0))
getFromMap :: Int -> M.Map Int Char -> Char
getFromMap = M.findWithDefault '_'
getNumeral :: (Int,Int) -> String
getNumeral t | td == 0 = ""
| td < 4 = replicate td $ getFromMap (2 * ti) numerals
| td == 4 = getFromMap (2 * ti) numerals : [getFromMap (2 * ti + 1) numerals]
| td < 9 = getFromMap (2 * ti + 1) numerals : replicate (td - 5) (getFromMap (2 * ti) numerals)
| otherwise = getFromMap (2 * ti) numerals : [getFromMap (2 * ti + 2) numerals]
where ti = fst t -- indices
td = snd t -- digits
dec2roman :: Int -> String
dec2roman = foldl (\r t -> getNumeral t ++ r) "" . zipWith (,) [0..] . toDigits
*Main> dec2roman 1453
"MCDLIII"