3

Data.Attoparsec.Text出口takeWhiletakeWhile1

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满足 predicate 的字符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

它像宣传的那样工作,

λ> 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组合器?

4

1 回答 1

5

Parser是一个单子,所以你可以检查返回值,如果长度不正确,则失败:

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's 的长度更有效,因为compareLength可能会短路。

于 2015-06-30T20:05:34.127 回答