jub*_*0bs 3 haskell parser-combinators attoparsec
Data.Attoparsec.Text出口takeWhile和takeWhile1:
Run Code Online (Sandbox Code Playgroud)takeWhile :: (Char -> Bool) -> Parser Text只要谓词返回
True就消耗输入,并返回消耗的输入.此解析器不会失败.如果谓词
False在输入的第一个字符上返回,它将返回一个空字符串.[...]
Run Code Online (Sandbox Code Playgroud)takeWhile1 :: (Char -> Bool) -> Parser Text只要谓词返回
True就消耗输入,并返回消耗的输入.此解析器要求谓词在至少一个输入字符上成功:如果谓词永不返回
True或者没有输入,则它将失败.
attoparsec的文档鼓励用户
Text尽可能使用面向对象的解析器,例如takeWhile1代替many1 anyChar.两种解析器之间的性能差异大约为100倍.
这两个解析器非常有用,但我一直觉得需要更通用的版本takeWhile1,更具体地说,是一些假设的解析器
takeWhileLo :: (Char -> Bool) -> Int -> Parser Text
takeWhileLo f lo = undefined
Run Code Online (Sandbox Code Playgroud)
这将解析至少 lo满足谓词的字符f,其中lo是任意非负整数.
我看了一下它takeWhile1的实现,但是它使用了一堆私有的函数,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
Run Code Online (Sandbox Code Playgroud)
它像宣传的那样工作,
?> parseOnly (takeWhileLo (== 'a') 4) "aaa"
Left "not enough input"
?> parseOnly (takeWhileLo (== 'a') 4) "aaaa"
Right "aaaa"
?> parseOnly (takeWhileLo (== 'a') 4) "aaaaaaaaaaaaa"
Right "aaaaaaaaaaaaa"
Run Code Online (Sandbox Code Playgroud)
但需要打包返回的中间结果列表count让我担心,特别是对于lo大的情况...这似乎违背了建议
Text尽可能使用面向对象的解析器[...]
我错过了什么吗?是否有更有效/惯用的方式来实现这样的takeWhileLo组合器?
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
Run Code Online (Sandbox Code Playgroud)
compareLength来自text包裹.它比比较text长度更有效,因为compareLength可能会短路.