Double 的 Read 实例的行为非常简单:
reads "34.567e8 foo" :: [(Double, String)] = [(3.4567e9," foo")]
但是 Scientific 的 Read 实例做了一些不同的事情:
reads "34.567e8 foo" :: [(Scientific, String)] =
[(34.0,".567e8 foo"),(34.567,"e8 foo"),(3.4567e9," foo")]
严格来说这是正确的,因为它呈现了输入的可能解析列表。事实上,它同样可以在列表中包含 (3.0, "4.567e8 foo") 以及其他一些。然而,在这种情况下(Double 实例遵循)的通常行为是“maximal munch ”,这意味着最长的有效前缀被解析。
我正在更新我的 Decimal 库,它具有类似的行为,我想知道这里有什么正确的东西。 Scientific 和 Decimal 都在使用 Text.ParserCombinators.ReadP,它旨在使编写 Read 实例变得容易,这似乎是 ReadP 解析器的一个特性。
所以我的问题:
1:在这些情况下,“读取”返回的正确做法是什么?我应该为 Data.Scientific 提交错误吗?
2:如果它应该只返回最大的咀嚼(就像 Double 实例那样)那么你如何让 ReadP 做到这一点?
最佳答案
我已经决定最大限度地咀嚼是正确的事情。给定“1.23”,返回 1 的解析器是错误的。我自己也被这个绊倒了,因为我曾经试图写一个“maybeRead”,如下所示:
maybeRead :: (Read a) => String -> Maybe a
maybeRead str = case reads str of
[v, ""] -> Just v
_ => Nothing
这对 Double 工作正常,但对 Decimal 和 Scientific 失败。 (显然可以修复处理多个返回结果,但我没想到需要这样做)。
问题原来是 Text.ParserCombinators.ReadP 中“可选”的实现。这使用对称选择运算符“+++”,它返回带有和不带有可选组件的解析。因此,当我写下类似的东西时
expPart <- optional "" $ do {...}
结果包括没有 expPart 的解析。
我使用左偏选择运算符编写了不同版本的“可选”:
myOpt d p = p <++ return d
如果解析器“p”使用任何文本,则不使用默认值。如果你想要最大的咀嚼,这是正确的。