3

Data.Attoparsec.TextエクスポートtakeWhiletakeWhile1:

takeWhile :: (Char -> Bool) -> Parser Text

述語が を返す限り入力を消費しTrue、消費された入力を返します。

このパーサーは失敗しません。False入力の最初の文字で述語が返される場合、空の文字列が返されます。

[...]

takeWhile1 :: (Char -> Bool) -> Parser Text

述語が を返す限り入力を消費しTrue、消費された入力を返します。

このパーサーは、入力の少なくとも 1 文字で成功する述語を必要とします。述語が返されない場合、Trueまたは入力が残っていない場合、失敗します。

attoparsecのドキュメンテーションは、ユーザーに

の代わりに、Text可能な限り 指向のパーサーを使用してください。2 種類のパーサーのパフォーマンスには約 100 倍の違いがあります。takeWhile1many1 anyChar

takeWhile1これらの 2 つのパーサーは非常に便利ですが、より一般的なバージョンの、より具体的には、仮説パーサーの必要性を常に感じています。

takeWhileLo :: (Char -> Bool) -> Int -> Parser Text
takeWhileLo f lo = undefined

predicate を満たす文字を少なくとも 解析します。ここで、 は任意の非負の整数です。loflo

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

compareLengthtextパッケージからです。短絡する可能性がある textため、 の長さを比較するよりも効率的です。compareLength

于 2015-06-30T20:05:34.127 に答える