Data.Attoparsec.Text
导出takeWhile
和takeWhile1
:
takeWhile :: (Char -> Bool) -> Parser Text
只要谓词返回
True
,就消耗输入,并返回消耗的输入。此解析器不会失败。如果谓词在输入的第一个字符上返回
False
,它将返回一个空字符串。[...]
takeWhile1 :: (Char -> Bool) -> Parser Text
只要谓词返回
True
,就消耗输入,并返回消耗的输入。此解析器要求谓词在至少一个输入字符上成功:如果谓词永远不会返回
True
或者没有输入,则它将失败。
attoparsec
的文档鼓励用户
尽可能使用面向
Text
的解析器,例如takeWhile1
代替many1 anyChar
。两种解析器之间的性能差异大约为100倍。
这两个解析器非常有用,但我一直觉得需要更通用的takeWhile1
版本,更具体地说,是一些假设的解析器
takeWhileLo :: (Char -> Bool) -> Int -> Parser Text
takeWhileLo f lo = undefined
将解析至少 lo
个满足谓词f
的字符,其中lo
是任意非负整数。
我查看了takeWhile1
's implementation,但它使用了一堆私有的Data.Attoparsec.Text.Internal
函数,并且似乎不易推广。
我想出了以下应用实现:
{-# LANGUAGE OverloadedStrings #-}
import Prelude hiding ( takeWhile )
import Control.Applicative ( (<*>) )
import Data.Text ( Text )
import qualified Data.Text as T
import Data.Attoparsec.Text
takeWhileLo :: (Char -> Bool) -> Int -> Parser Text
takeWhileLo f lo =
T.append . T.pack <$> count lo (satisfy f) <*> takeWhile f
按宣传方式运作,
λ> parseOnly (takeWhileLo (== 'a') 4) "aaa"
Left "not enough input"
λ> parseOnly (takeWhileLo (== 'a') 4) "aaaa"
Right "aaaa"
λ> parseOnly (takeWhileLo (== 'a') 4) "aaaaaaaaaaaaa"
Right "aaaaaaaaaaaaa"
但是需要打包count
返回的中间结果列表让我担心,特别是对于lo
很大的情况......这似乎违背了建议
尽可能使用面向
Text
的解析器[...]
我错过了什么吗?是否有更有效/惯用的方式来实现这样的takeWhileLo
组合器?
答案 0 :(得分:5)
Parser
是一个monad,所以你可以检查返回值,如果长度不正确则会失败:
takeWhileLo :: (Char -> Bool) -> Int -> Parser Text
takeWhileLo f lo = do
text <- takeWhile f
case T.compareLength text lo of
LT -> empty
_ -> return text
compareLength
来自text
包。它比比较text
的长度更有效,因为compareLength
可能会短路。