如何在Haskell中将小数分数解析为有理数?

12

我正在参加一个编程竞赛其中一个问题的输入数据包括十进制格式的分数:0.75是其中一个例子。

将其解析为Double很简单(我可以使用read),但精度损失令人痛苦。需要非常小心地处理Double比较(我没有这样做),这似乎是多余的,因为在Haskell中有Rational数据类型。

当尝试使用它时,我发现要读取Rational,必须提供以下格式的字符串:numerator % denominator,而我显然没有这个。

那么,问题来了:

将分数的十进制表示解析为Rational的最简单方法是什么?

还应考虑外部依赖项的数量,因为我无法向在线评测器安装其他库。

3个回答

20

您想要的函数是 Numeric.readFloat

Numeric Data.Ratio> fst . head $ readFloat "0.75" :: Rational
3 % 4

8
如果您想读取负数,可以添加 readSignedfst . head $ readSigned readFloat "-3.14" :: Rational - newacct

3
以下是(GHCi 会话)的内容:

以下是(GHCi 会话)的内容:

> :m + Data.Ratio
> approxRational (read "0.1" :: Double) 0.01
1 % 10

当然,您必须适当选择epsilon。

这是一个好主意!我认为在大多数情况下应该使用它,而不是toRational - Rotsor
不幸的是,在这里选择 epsilon 并不明显。例如,approxRational 0.999 0.0001 得到的结果是 909 % 910,这并不是我想要的。在这种情况下应该使用的正确 epsilon 是 0.000001(精度的平方?) - Rotsor

2
也许你在比赛中自己实现它会得到额外的分数:
import Data.Ratio ( (%) )

readRational :: String -> Rational
readRational input = read intPart % 1 + read fracPart % (10 ^ length fracPart)
  where (intPart, fromDot) = span (/='.') input
        fracPart           = if null fromDot then "0" else tail fromDot

我不这么认为。在这样的比赛中,只有提交时间和正确性才是最重要的。不过这个解决方案很好,足够简短,在紧急情况下编码也没问题。 - Rotsor

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