7

パフォーマンスを向上させるために、Parsec を使用する機能している Haskell コードを、代わりに Attoparsec を使用するように変換しています。変更を行い、すべてがコンパイルされましたが、パーサーが正しく機能しません。

1 行に 1 つずつ、さまざまなレコード タイプで構成されるファイルを解析しています。レコードまたはコメントを解析するための個々の関数はそれぞれ正しく機能しますが、一連のレコードをコンパイルする関数を作成しようとすると、パーサーはより多くの入力を期待しているため、常に部分的な結果を返します。

これらは、私が試した 2 つの主なバリエーションです。どちらも同じ問題を抱えています。

items :: Parser [Item]
items = sepBy (comment <|> recordType1 <|> recordType2) endOfLine

この 2 番目の例では、行末文字を使用するようにレコード/コメント パーサーを変更しました。

items :: Parser [Item]
items = manyTill (comment <|> recordType1 <|> recordType2) endOfInput

私のアプローチに何か問題がありますか?私が試みていることを達成する他の方法はありますか?

4

3 に答える 3

6

失敗する前にできるだけ多くの入力を消費する attoparsec パーサーを作成する場合、入力の最後に到達したときに部分的な結果の継続を通知する必要があります。

于 2010-06-07T21:01:56.517 に答える
3

私は以前にこの問題に遭遇しましたが、私の理解では<|>、 の定義で機能する方法が原因であると理解していますsepBy:

sepBy1 :: Alternative f => f a -> f s -> f [a]
sepBy1 p s = scan
    where scan = liftA2 (:) p ((s *> scan) <|> pure [])

これは、失敗した場合にのみ移動しpure []ます(s *> scan)。これは、入力の最後にいるからといって発生するわけではありません。

私の解決策は、返されfeedた でemptyByteString を使用して呼び出すことだけでした。これは一種のハックかもしれませんが、問題を処理する方法でもあるようです。Resultparseattoparsec-iteratee

f k (EOF Nothing)  = finalChunk $ feed (k S.empty) S.empty

私が知る限り、これがattoparsec-iterateeここで機能する唯一の理由であり、普通の古いparseものは機能しません。

于 2010-06-07T14:08:31.490 に答える
0

あなたはほとんど情報を提供しないので、私はあなたに良い助けを与えるのは難しいと思います。しかし、私が伝えたいコメントがいくつかあります。

  • おそらく、パーサーは入力が行われたことに気づかず、EOLを取得するか別のレコードを取得するかにかかっています。したがって、部分的な結果を要求します。それがそれを強制することを期待して、それにEOLと同等のものを供給してみてください。
  • コードを思い出せませんが、Alternativeインスタンスを使用すると、解析のパフォーマンスが低下する可能性があります。その場合は、commentとrecordTypesを大文字にすることをお勧めします。
  • 私は多くのバイナリ解析にシリアルを使用していますが、これも非常に高速です。attoparsecは、テキストパーセクとしては優れているようです。あなたは間違いなくオプションを検討する必要があります。
  • もう1つのオプションは、長期的にはiterateeベースのIOを使用することです。John Latoは、最新のモナドリーダーで反復に関する優れた記事を作成しました(問題#16だと思います)。行末条件は、信号を繰り返すことです。iterateeタイプは非常に困難であり、慣れるのに時間がかかることに注意してください。
于 2010-06-07T13:36:19.290 に答える