2

以下のコードは出力Right ["1<!>2<!>3"]しますが、必要Right ["1", "2", "3"]です。

import Text.ParserCombinators.Parsec

response = contents :: CharParser () [String]
  where
    contents = sepBy content contentDelimiter
    contentDelimiter = string "<!>"
    content = many anyChar

main = do
  putStrLn $ show $ parse response "Response" "1<!>2<!>3"

ここでの問題は、contentパーサーsepByが区切り文字をテストする前にすべての入力を消費することだと思います。だから、私の質問は次のとおりです。

  1. 私は自分の仮定で正しいですか?そうでない場合、私が犯した間違いは何ですか?

  2. このような問題に対してどのような解決策をお勧めしますか?(Parsecを使用)

* content区切り文字を含まない文字列と一致する必要があります。これ1<!>2<!>3は単なる例でありdslkf\n><!>dsf<!>3

4

2 に答える 2

8

最初の例では、次のように置き換えます

content = many anyChar

content = many digit

コンテンツのパーサーがセパレーターと誤って一致しないようにします。

単に数字以上のものを一致させたいと思うかもしれませんが、そうであっても、 s の間で何有効であるかを注意深く考え、それを行う<!>パーサーを作成することをお勧めします。

なんで?
コンテンツ用の本当に優れたパーサーを手に入れたら、応答の定義は完璧になります。このようにmystring = "hello<!>mum"して、最上位のパーサーによって切り刻まれることなくコンテンツを含めることができます。低レベルのstringLiteralパーサーは全体を食べて"hello<!>mum"しまい、最上位のパーサーはその<!>中に正しく無邪気に含まれているものを見ることはありません。

一般に、...
ほとんどの解析状況では、コンテンツで何が許可されているかを本当に明確にし、それのみを解析することが最善です。これには次の 3 つの理由があります。

  • 再利用性 (これをより大きなパーサー内で使用できます)
  • 正しさ
  • 通常は効率的です。先読みを避けすぎると、パーサーの実行速度が速くなります。

再利用性は重要です。現時点では、分割し<!>て他のすべてを食べるパーサーを使用すると、入力全体を食べることが保証され、それ以上の解析を行うことができなくなります。

ボトムアップ
パーサーはゼロから機能する必要があります。これについて、コメントで「パーサーを特定のものから一般的なものにスタックする」と非常によく説明しました。

テストを容易にするためにこの順序で記述するのが最も簡単なので、最初に a stringCharthen stringLiteralbefore memberbefore arraybefore before objectbefore jsonbefore contentthenに一致するものを記述しresponseます。途中で相互に再帰的に呼び出すことができます。parseTestその後、一緒に行うように、それぞれの小さなものをテストするために使用できます。parseTest response "1<!>2<!>3"main を書き直してコンパイルするよりも、ghci に入力する方が速いです。

トップダウン?
パーサーをトップダウンで書くのは間違っていませんが、難しいだけです。あなたは書ける

response = many $ content `sepBy` contentSeparator
content = json <|> somethingElse
json = object <|> array
array = ...

しかし、非常に小さなパーサーを作成するまでは、何もテストできません。

于 2012-11-17T14:43:27.353 に答える
0

noneOf'<' と '!' も '>' もコンテンツの一部ではないという条件で、を使用した解決策をお勧めします。

import Text.ParserCombinators.Parsec

response = contents :: CharParser () [String]
 where
   contents = sepBy content contentDelimiter
   contentDelimiter = string "<!>"
   content = many (noneOf ['<','!','>']) 

main = print $ parse response "Response" "1<!>2<!>3"
于 2012-11-17T15:03:43.580 に答える