解析 – Text.ParserCombinators.ReadP中的最大咀嚼

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这样做呢?

解决方法

我已经决定最大的咀嚼是正确的事情.给定“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”使用任何文本,则不使用认值.如果你想要最大的咀嚼,这就是正确的事情.

相关文章

vue阻止冒泡事件 阻止点击事件的执行 &lt;div @click=&a...
尝试过使用网友说的API接口获取 找到的都是失效了 暂时就使用...
后台我拿的数据是这样的格式: [ {id:1 , parentId: 0, name:...
JAVA下载文件防重复点击,防止多次下载请求,Cookie方式快速简...
Mip是什么意思以及作用有哪些