12

これに関する情報を見つけることができなかったことに驚いています。困っているのは私だけでしょう。

それで、ダッシュカウンターがあるとしましょう。文字列内のダッシュの数を数えて、文字列を返したいです。parsec の状態処理を使用すると機能しない例を挙げたふりをします。したがって、これは機能するはずです:

dashCounter = do
  str <- many1 dash
  count <- get
  return (count,str)


dash = do
  char '-'
  modify (+1)

実際、これはコンパイルされます。さて、私はそれを使用しようとします:

:t parse dashCounter "" "----"
parse dashCounter "" "----"
  :: (Control.Monad.State.Class.MonadState
        t Data.Functor.Identity.Identity,
      Num t) =>
     Either ParseError (t, [Char])

わかりました、それは理にかなっています。状態と文字列を返す必要があります。涼しい。

>parse dashCounter "" "----"

<interactive>:1:7:
    No instance for (Control.Monad.State.Class.MonadState
                       t0 Data.Functor.Identity.Identity)
      arising from a use of `dashCounter'
    Possible fix:
      add an instance declaration for
      (Control.Monad.State.Class.MonadState
         t0 Data.Functor.Identity.Identity)
    In the first argument of `parse', namely `dashCounter'
    In the expression: parse dashCounter "" "----"
    In an equation for `it': it = parse dashCounter "" "----"

おっとっと。しかし、そもそもどうして機能することを期待できたのでしょうか? 初期状態を入力する方法はありません。

関数もあります:

>runPT dashCounter (0::Int) "" "----"

しかし、同様のエラーが発生します。

<interactive>:1:7:
    No instance for (Control.Monad.State.Class.MonadState Int m0)
      arising from a use of `dashCounter'
    Possible fix:
      add an instance declaration for
      (Control.Monad.State.Class.MonadState Int m0)
    In the first argument of `runPT', namely `dashCounter'
    In the expression: runPT dashCounter (0 :: Int) "" "----"
    In an equation for `it':
        it = runPT dashCounter (0 :: Int) "" "----"

その上でrunStateを実行する必要があるか、内部で既に実行している関数が必要なように感じますが、ここからどこに行くべきかわかりません。

編集:もっと明確に指定する必要がありました.parsecの状態処理を使用したくありませんでした. その理由は、バックトラックが、解決しようとしている問題で収集するものに影響を与えたくないという気持ちがあるからです。

ただし、McCann 氏はこれをどのように組み合わせるかを考え出しており、最終的なコードは次のようになります。

dashCounter = do
  str <- many1 dash
  count <- get
  return (count,str)

dash = do
  c <- char '-'
  modify (+1)
  return c

test = runState (runPT dashCounter () "" "----------") 0

どうもありがとう。

4

3 に答える 3

9

Parsec が組み込み機能として提供するユーザー状態コンポーネントを使用する場合は、getStateおよびmodifyStateモナド関数を使用できます。

dashreturn の使用は役に立たないように見えますが、私はあなたのサンプルプログラムに忠実であり続けようとしました。

import Text.Parsec

dashCounter :: Parsec String Int (Int, [()])
dashCounter = do
  str <- many1 dash
  count <- getState
  return (count,str)

dash :: Parsec String Int ()
dash = do
  char '-'
  modifyState (+1)

test = runP dashCounter 0 "" "---"

runPについてのあなたの懸念に実際に対処していることに注意してくださいrunState

于 2011-07-29T17:54:37.523 に答える
6

これらの回答はこの特定の問題を整理していますが、このようなアプローチではより深刻な根本的な問題を無視しています。この回答を見ている他の人のために、ここで説明したいと思います。

ユーザー状態と StateT トランスフォーマーの使用には違いがあります。内部ユーザー状態はバックトラッキングでリセットされますが、StateT はリセットされません。次のコードを検討してください。ダッシュがある場合はカウンターに 1 つ、プラスがある場合は 2 つ追加します。それらは異なる結果を生み出します。

ご覧のとおり、内部状態の使用と StateT トランスフォーマーの接続の両方で正しい結果が得られます。後者は、操作を明示的に持ち上げる必要があり、型にはより注意を払う必要があります。

import Text.Parsec hiding (State)
import Control.Monad.State
import Control.Monad.Identity

f :: ParsecT String Int Identity Int
f = do
  try dash <|> plus
  getState

dash = do
  modifyState (+1)
  char '-'
plus = do
  modifyState (+2)
  char '+'

f' :: ParsecT String () (State Int) ()
f' = void (try dash' <|> plus')

dash' = do
  modify (+1)
  char '-'

plus' = do
  modify (+2)
  char '+'

f'' :: StateT Int (Parsec String ()) ()
f'' = void (dash'' <|> plus'')

dash'' :: StateT Int (Parsec String ()) Char
dash'' = do
  modify (+1)
  lift $ char '-'

plus'' :: StateT Int (Parsec String ()) Char
plus'' = do
  modify (+2)
  lift $ char '+'

これは、f、f'、および f'' を実行した結果です。

*Main> runParser f 0 "" "+"
Right 2
*Main> flip runState 0 $ runPT f' () "" "+"
(Right (),3)
*Main> runParser (runStateT f'' 0) () "" "+"
Right ((),2)
于 2014-07-27T13:55:09.817 に答える