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:如果它只应该返回最大的munch(就像Double实例那样)那么你如何让ReadP这样做呢?
答案 0 :(得分:1)
我已经决定最大的咀嚼是正确的事情。鉴于" 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的解析。
我写了一个不同版本的&#34;可选&#34;使用左偏选择运算符:
myOpt d p = p <++ return d
如果解析器&#34; p&#34;使用任何文本,然后不使用默认值。如果你想要最大的咀嚼,这就是正确的事。
答案 1 :(得分:0)
对于#2,您可以更改科学包以使用根据旧语法定义的解析器:scientificPmaxmuch = scientificP <* eof :: ReadP Scientific
。
我认为#1的约定并不多:对于使用read
或Text.Read.readMaybe
的人来说,它没有什么区别。 readS_to_P reads :: ReadP Double
可能比readS_to_P reads :: ReadP Scientific
更快,但如果效率很重要,那么您可以将所有内容保留为ReadP
直到结束。