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”使用任何文本,则不使用默认值。如果你想要最大的咀嚼,这是正确的。

10-08 16:07