4

ここでの 4 番目の演習の一部として、 parsecreadsなど の型関数を使用したいと思います。readHexParser

これを行うために、私は関数を書きました:

liftReadsToParse :: Parser String -> (String -> [(a, String)]) -> Parser a
liftReadsToParse p f = p >>= \s -> if null (f s) then fail "No parse" else (return . fst . head ) (f s)

たとえばGHCIでは、次のように使用できます。

*Main Numeric> parse (liftReadsToParse (many1 hexDigit) readHex) "" "a1"
Right 161

次の点に関して、このアプローチの改善を誰でも提案できますか?

  • 用語がメモ化されるか、または が返さ(f s)れた場合に 2 回評価されますか? null (f s)False
  • 複数の成功した解析の処理、つまりlength (f s)が 1 より大きい場合、parsec がこれをどのように処理するかわかりません。
  • 解析の残りの処理、つまり(snd . head) (f s).

  • 4

    2 に答える 2

    3

    これはいい考えです。ReadSパーセクをParsecにうまく適合させるためのより自然なアプローチParser Stringは、タイプの先頭でを省略します。

    liftReadS :: ReadS a -> String -> Parser a
    liftReadS reader = maybe (unexpected "no parse") (return . fst) .
                       listToMaybe . filter (null . snd) . reader
    

    この「コンビネータ」スタイルは非常に慣用的なHaskellです。慣れると、関数定義がはるかに読みやすく、理解しやすくなります。

    liftReadS次に、単純なケースで次のように使用します。

    > parse (many1 hexDigit >>= liftReadS readHex) "" "a1"
    

    (モジュールlistToMaybe内にあることに注意してください。)Data.Maybe

    より複雑なケースでliftReadSは、Parsecdoブロック内で簡単に使用できます。

    他のいくつかの質問について:

    1. この機能readerは現在1回だけ適用されるため、「メモ化」するものはありません。
    2. ReadSほとんどの場合、パーサーの最初の解析を除くすべてを無視するのが一般的で受け入れられている方法なので、問題ありません。
    于 2010-08-25T23:15:56.400 に答える
    0

    質問の最初の部分に答えるには、no(f s)はメモされません。手動で行う必要があります。

    liftReadsToParse p f = p >>= \s -> let fs = f s in if null fs then fail "No parse"
                                                                  else (return . fst . head ) fs
    

    しかし、代わりにパターン マッチングを使用します。

    liftReadsToParse p f = p >>= \s -> case f s of
                                            []              -> fail "No parse"
                                            (answer, _) : _ -> return answer
    
    于 2010-08-25T19:03:42.390 に答える