Haskell:整数->;罗马数字

Haskell:整数->;罗马数字,haskell,roman-numerals,Haskell,Roman Numerals,所以,我有一个有趣的问题单实践练习: 将整数(小于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", "

所以,我有一个有趣的问题单实践练习: 将整数(小于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

这个程序实际上有几个语法错误(Edit:多亏@Lukasz的编辑,现在只有一个语法错误)。但您所问的问题是,您不能仅在
ghci
中创建绑定。您在程序中的什么位置编写

a = 1
ghci
中,必须写入

let a = 1
否则,您将在输入“=”时得到
解析错误
错误


我建议您将程序放在一个文件中,用
ghc
编译它,或者用
runhaskell
运行它,而不是插入
let
s,这样对以后的工作和错误修复会更方便

我编写了一个模块来处理整数和罗马数字之间的转换。然而,我的模块有3个缺点

  • 我的模块可以处理的最大罗马数字将不会超过 因为我假设最大的罗马单位是“M”和 根据规则,“MMMM”不是有效的罗马数字

  • 我没有在函数“findKey”中使用Maybe来避免意外的键,因为我没有很好地掌握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代)
    ,12月2日,阿曼
    )在哪里
    导入数据列表(isInfixOf)
    --程序可以处理的最大数量将不超过4999
    罗马单位::[(字符,整数)]
    罗马单位=[('I',1),('V',5),('X',10),('L',50),('C',100),('D',500),('M',1000)]
    罗马分隔符::[Int]
    romanDividers=反向$map snd罗马单位
    罗马数字::[Char]
    罗马数字=反向$map fst罗马单位
    --短除以n除以每一个除以器中的值
    shortDivide::Int->[Int]->[Int]
    短除法n[]=[]
    短除法n除法器=let(商,余数)=n`divimod`(头除法器)
    商中:短除余数(尾除数)
    dec2Roman::Int->String
    dec2Roman n=concat$map prettyroman 1(zipWith(\x y->replicate x y)(短除法n个罗马除法器)罗马数字)
    prettyroman 1::String->String
    古罗马
    |罗马文==“IIII”=“IV”
    |罗马==“XXXX”=“XL”
    |罗马文==“CCCC”=“CD”
    |否则=罗马
    --Prettyroman 2:将VIV、LXL、DCD分别替换为IX、XC和CM。
    --在此之后,dec2Roman将被修改为dec2Roman'=prettyRoman2 dec2Roman
    prettyroman 2::String->String
    prettyroman 2=未定义
    芬德基:等式a=>a->[(a,b)]->b
    findKey=snd。头。过滤器(\(k,v)->k==键)
    romanValue::Char->Int
    romanValue c=findKey c romanUnits
    roman2Dec::String->Int
    roman2Dec[]=0
    roman2Dec[x]=romanValue x
    roman2Dec(x:y:xs)
    |romanValue x

  • 有点晚了,但为了记录在案,我将代码从移植到了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"
    
    将限定的Data.Map.Lazy导入为M
    导入数据.Bool(Bool)
    导入数据列表(展开器)
    数字::M.Map Int Char
    数字=M.fromList[(0,'I'),(1,'V'),(2,'X'),(3,'L'),(4,'C'),(5,'D'),(6,'M')]
    toDigits::Int->[Int]
    toDigits=展开器(\x->bool Nothing(Just(rem x 10,div x 10))(x>0))
    getFromMap::Int->M.Map Int Char->Char
    getFromMap=M.findWithDefault'\
    GetNumeric::(Int,Int)->字符串
    GetNumeric t | td==0=“”
    |td<4=复制td$getFromMap(2*ti)数字
    |td==4=getFromMap(2*ti)数字:[getFromMap(2*ti+1)数字]
    |td<9=getFromMap(2*ti+1)数字:复制(td-5)(getFromMap(2*ti)数字)
    |否则=getFromMap(2*ti)数字:[getFromMap(2*ti+2)数字]
    其中ti=fst——指数
    td=snd t——数字
    dec2roman::Int->String
    dec2roman=foldl(\r t->getnumeric t++r)“。zipWith(,)[0..]。托迪吉特人
    *Main>dec2roman 1453
    “麦克德利”
    
    与大多数情况一样,行号似乎完全没有意思。。。。这也解释了为什么你不能自己解决这个问题。或者我记性不好,或者没有
    IIX
    ,有
    VIII
    ,等等。如果你在这方面得到很多分数,我会感到惊讶。我打赌设置问题的人期望的是一个带有签名的函数
    ::Int->String
    ,或者可能是
    ::Int->可能是String
    ,但不是您所做的。取四个独立的
    int
    也会让这一切变得更加脆弱。你没有意识到把一个Int转换成一个Int的列表是很容易的,每个Int都在0到9之间吗?
    digitalise i=foldr(flip(++)”“$map(\(ns,d)->ns!!(read d))$zip numerics$map(:[])$reverse$show i
    -现在,我想知道
    numerics
    可能是什么?仍然支持n+k模式吗?还有,你打算如何转换1001?我想你从来都不希望像
    VIV
    这样的东西以am中间形式出现。当你到达那里的时候,你可能已经输了。我认为你的评论没有任何意义,因为如果一个罗马人写他们特定的十进制数,就会遇到和我一样的问题。假设他遇到4个相同的罗马数字,他将使用一个较大的单位来替换最后3个单位,如IIII到IV。之后,他会发现新生产的较大单位可以与之前的相同单位组合,如VIV、LXL等。我认为你无法找到避免这种情况的方法。