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
, но он использует набор функций, закрытых для 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
?