Haskell 中是否有 "continue" 呢?

4
我最近开始学习Haskell,并正在尝试编写基本的Haskell函数。我写了一个名为“intToRoman”的函数,它应该将整数转换为罗马数字。它将数字分成整数列表(1400-> [1,4,0,0]),并将每个数字转换为罗马数字,考虑到列表长度来确定是否为千或百。但是,它不会停止并检查零。例如,数字1400将返回:
 MCD** Exception: Map.!: given key is not an element in the map
 CallStack (from HasCallStack)

这里是代码本身:
mapInttoRoman:: M.Map Int String
mapInttoRoman = M.fromList
          [(1,"I"),(4,"IV"),(5,"V"),(9,"IX"),(10,"X"),(40,"XL")
          ,(50,"L"),(100,"C"),(400,"CD"),(900,"CM"),(500,"D"),(1000,"M")]

listOfInt :: Int -> [Int]
listOfInt 0 = [ ]
listOfInt c = listOfInt(c`div`10) ++ [c`mod`10]

duplicate :: String -> Int -> String
duplicate string n = concat $ replicate n string

intToRoman :: Int -> String
intToRoman 0 = ""
intToRoman c = createInt x (len-1)
 where x = listOfInt c
       len = length x
       createInt y xz = findRoman (head y) xz ++ createInt(drop 1 y)(xz-1)
        where findRoman b l
               | l < 1      = mapInttoRoman M.! b
               | b == 0     = " "
               | l == 3     = duplicate (mapInttoRoman M.! (10^l)) b
               | b == 1     = mapInttoRoman M.! (10^l)
               | otherwise  = mapInttoRoman M.! (b*10^l)

4
你的 | b == 0 = " " 分支无法捕获此情况,因为 | l < 1 = .. 分支首先被检查。你可以改变它们的顺序。(然后它会失败,因为你尝试获取一个空列表的头部,所以你需要为 createInt [] _ = "" 添加一个基本情况。) - that other guy
我理解您可能是Haskell的新手,但请考虑阅读风格指南。这不是好的代码。 - AJF
10
@AJFarmar 我认为当你学习一个有许多新概念的新语言并编写仅供自己教育的程序时,担心这个并不是正确的事情。样式指南对需要维护的程序很重要。 - amalloy
2个回答

0

我认为你使用的算法不正确,而且你把它弄得比必要的复杂。

此外,列表不完整,我添加了90和900个案例。

看看这段代码...我认为它非常简单,你会很容易理解。

import qualified Data.Map as M

    mapInttoRoman:: M.Map Int String
    mapInttoRoman = M.fromList
              [(1,"I"),(4,"IV"),(5,"V"),(9,"IX"),(10,"X"),(40,"XL")
              ,(50,"L"),(90,"XC"),(100,"C"),(400,"CD"),(500,"D"),(900,"CM"),(1000,"M")]

    intToRoman :: Int -> String
    intToRoman 0 = ""
    intToRoman n | M.member n mapInttoRoman = mapInttoRoman M.! n
                 | otherwise = val ++ intToRoman (n-key) 
                 where
                    keys = reverse (M.keys mapInttoRoman)
                    key  = maxValidKey n keys
                    val  = mapInttoRoman M.! key
                    maxValidKey n (k:ks) 
                      | n >= k = k
                      | otherwise = maxValidKey n ks 

测试:

enter image description here


0
这个怎么样?内联解释。
import qualified Data.List as L

{-
1. Find the floor of 'n' in the mapping, i.e. the highest
   decimal value 'v' that is less than or equal to 'n'.
2. Add the corresponding string to the answer, and subtract
   'v' from 'n'.
3. Repeat until n = 0.

<$> from Functor, fmap
(<$>) :: Functor f => (a -> b) -> f a -> f b

<> from Monoid, mappend
(<>) :: Monoid m => m -> m -> m

>>= from Monad, bind
(>>=) :: m a -> (a -> m b) -> m b
-}
numerals :: Integer -> Maybe String
numerals n
  | n > 3000 || n <= 0 = Nothing
  | otherwise = (snd <$> x) <> (x >>= (\y -> numerals (n - fst y)))
  where
    x = L.find (\(i, _) -> i <= n) xs
    xs =
      [ (1000, "M"),
        (900, "CM"),
        (500, "D"),
        (400, "CD"),
        (100, "C"),
        (90, "XC"),
        (50, "L"),
        (40, "XL"),
        (10, "X"),
        (9, "IX"),
        (5, "V"),
        (4, "IV"),
        (1, "I")
      ]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接