65.9K
CodeProject 正在变化。 阅读更多。
Home

Haskell 中阿拉伯数字到罗马数字的转换器 (第 1 部分:将阿拉伯数字转换为罗马数字)

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2014 年 6 月 8 日

CPOL

3分钟阅读

viewsIcon

15416

downloadIcon

5

Haskell 中阿拉伯数字到罗马数字的转换器 (第 1 部分:将阿拉伯数字转换为罗马数字)

引言

提供的代码片段可用于将阿拉伯数字转换为罗马数字。目前,支持最大到 3999 的数字。 如果您想转换更大的数字,请参阅 #使用代码。

背景

在最近一次公司的编码道场中,我们致力于创建一个 JavaScript 函数来将阿拉伯数字转换为罗马数字(原始任务:http://codingdojo.org/cgi-bin/index.pl?KataRomanNumerals)。

为了重温我的函数式编程技能,我决定用 Haskell 为这个问题编写一个解决方案。

Using the Code

要自己测试代码,只需下载并解压提供的文件,然后加载该文件或将代码片段复制到您自己的文件中(注意:在这种情况下,您必须在第一行之前添加 'import Data.Map'),就像在 ghci 中一样。然后只需调用 numToRoman 函数。

我们在道场中采用的方法是遍历输入中的所有数字,同时获取其当前幂,以获取下一个要附加到结果字符串的罗马文字。 因此,给定 513,我们将首先考虑数字 500,它转换为 D,然后是 10,它转换为 X,最后是 3,它转换为 III

为了表示所有必需的文字,我创建了以下字典/映射

romans :: Map Int [Char]
romans = 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")]

注意:为了简化减法规则(例如,40 变为 XL 而不是 XXXX),我还将以 49 开头的数字的组合数字添加到映射中。 目前,可以转换最大到 3999 的数字。 要转换更大的数字,只需添加相应的文字即可。 因此,下一步将是为 4000、5000、900010000 添加文字。

为了获得单个数字,我添加了以下辅助函数

digits :: Integral x => x -> [x]
digits 0 = []
digits x = digits (x `div` 10) ++ [x `mod` 10]

因此,调用 digits 513 将返回 [5,1,3]。

有了这些,我们现在可以开始转换过程了

numToRoman :: Int -> [Char]
numToRoman x = resolveRoman digs (len-1)
               where
                   digs = digits x
                   len = length digs
                   replicateStr amount str = concat $ replicate amount str
                   resolveRoman xs l = 
                        if (l < 0) then ""
                        else (getNextLiteral (head xs) (10^l)) 
                              ++ (resolveRoman (drop 1 xs) (l-1))
                             where getNextLiteral x pwr = 
                                if (x < 1) then ""
                                else if (x < 4) then replicateStr x (romans ! (pwr))
                                else if (x > 5 && x < 9) then (romans ! (5*pwr)) 
                                         ++ replicateStr (x-5) (romans ! (pwr))
                                else romans ! (x*pwr)

首先,我们声明字段 digs 和 len 以保存我们的输入(作为数组)及其长度(对于 312digs = [3,1,2]len = 3。 我们还定义了 replicateStr 辅助函数,以便我们可以轻松地复制字符串(例如,replicateStr 2 "I" 给出 "II")。

removeRoman 函数中,我们首先检查给定的长度(表示幂)是否小于 0,如果是,我们想结束执行。 否则,我们通过使用 where 子句中定义的 getNextLiteral 函数来计算下一个文字,并使用剩余的数组递归调用它。 因此,对于 312,我们首先将 300 转换为 "CCC",然后将其附加到转换 12 的结果,依此类推(在每一步中递减长度/幂,直到达到 0)。

getNextLiteral 函数根据数字的值及其幂返回下一个需要的文字。 对于小于 1 的值,我们只需返回 ""(罗马数字系统中没有 0 文字)。 对于其他数字 < 4,我们调用 replicateStr 函数,以便获得所需数量的文字,其中文字本身由幂决定。 因此,对于数字 3 和幂 2,我们必须将 "C" 复制三次,这会给出 "CCC"。 其他边缘情况也相应地工作。

关注点

总而言之,这是一项非常有趣且有意义的作业。 由于我已经有一段时间没有接触 Haskell 了,因此可能存在更方便和更短的解决方案,所以请随时提供任何提示或代码段或提出问题 :)。

历史

到目前为止,我只介绍了从阿拉伯数字到罗马数字的转换。 在几周内,我计划撰写另一篇文章来转换罗马数字到阿拉伯数字。

© . All rights reserved.