2011-01-09 9 views
4

は、コードのちょっと巨大な作品であるとして、それは最終的理由モナドもので、巨大だが、タスクは簡単です:データ構造に次の文字列を解析:Haskellの怠惰質問や、なぜこのモナドが動作していないここに期待

」 hello "、" hello "、" hello "、" hello "、" sym "、"( ")、" args "、" some、args " 、( -

"ハロー(一部、引数)"> [( "FID"、 "")、( "SYM"、 "("))] iが書き込ま

が、コードは、次の生成します"args"、 "")、( "sym"、 ")")]

'args'と 'fid'の値が途中で失われているように、私はコンパイラが神秘的な理由でそれらを計算しないことにしました。

私も、私はさんと私には役に立たないように見える部分がマークされますが、コンパイラは:)場所でそれらを残して

私を強制的にそしてここでのコードで、コードが完全に悪いと思い、「?」:

type PStream = String 
type PToken a = (String, a) 
data Pstate a = Pstate (String -> ([PToken String], PStream)) a 

instance Monad Pstate where 
    return x = Pstate (\_ -> ([("start", "")], "?")) x -- not used ? 
    (Pstate bindparser v) >>= f = Pstate newparser fv 
     where 
      Pstate fparser fv = f v 
      (btok, brest) = bindparser "this string also not used" 
      (tok, rest) = fparser brest 
      newparser _ = (btok ++ tok, rest) 

-- parsers 
parseFid :: Pstate String 
parseFid = Pstate parser "???" 
    where parser r = let (fid, rest) = span (/= '(') r in ([("fid", fid)],rest) 

parseSym :: Char -> Pstate String 
parseSym c = Pstate parser "???" 
    where parser r = let rest = parseOne c r in ([("sym", [c])],rest) 

parseOne s (h:t) = if h == s then t else error $ "symbol not match:" ++ [h] ++ " /= " ++ [s] 
parseOne s [] = [] 

parseArgs :: Pstate String 
parseArgs = Pstate parser "???" 
    where parser r = let (args,rest) = span (/=')') r in ([("args", args)],rest) 

-- util 
load :: String -> Pstate String 
load s = Pstate (\ls -> ([("load", "")],ls)) "???" 

runP :: Pstate String -> ([PToken String], PStream) 
runP (Pstate fparser fv) = fparser "???" 

-- combined parser 
parseFunction :: String -> Pstate String 
parseFunction s = do 
    load s --- should be 'return' here ? 
    parseFid 
    parseSym '(' 
    parseArgs 
    parseSym ')' 

main = putStrLn $ show $ runP $ parseFunction "hello(a b c)" 
+0

このケースでは、モナドが何をしているのか分からないようです。私の答えがあなたが探しているものでない場合、このコードについて少し詳しく教えてもらえますか? – fuz

答えて

3

まず、"???"については、そこを出なければなりませんでした。あなたのデータコンストラクタは次の型を持っていることを、

data Pstate a = Pstate (String -> ([PToken String], PStream)) a 

これは意味します:Pstateのあなたの定義を考えてみましょう

Pstate :: (String -> ([PToken String], PStream)) -> a -> Pstate a 

これがモナドのデフォルト構築物です。モナドコンビネータを定義する場合、実際にはコンビネータを必要としない場所に配置することは珍しくありません。そのためコンベンションでは()のままにしておきます。

実際、私はあなたのコードが非常に奇妙だと思うが、あなたはステートフルなモナドのポイントをつかんでいないようだ。私に説明してみましょう:

data MyState a = MyState (TypeOfState -> (a, TypeOfState)) 

これはあなたのモナドアクションが実際の計算のいくつかの種類であることを、意味し、(あなたの状態の一部で可能)何かを行います。

通常、ステートフルな計算は、このタイプを持っています結果と新しい状態を返します。状態はモナドに包まれているので、それについて考える必要はありません。

コードでは、同じパターンを使用していますが、多少異なります。計算の結果を[PToken String]に固定したようです。私は少しあなたの定義を修正してみましょう:

data Pstate a = Pstate (PStream -> (a, PStream)) 

今、あなたはこのようになりコンビネータ、適用することによって、あなたの計算の戻り値を取得するので:今

instance Monad Pstate where 
    -- return should just wrap the computation up, so no changes 
    return x = Pstate (\p -> (x,p)) 
    parser1 >>= parser2 = Pstate $ \input -> let 
    Pstate parser1' = parser1 
    Pstate parser2' = parser2 
    (output, rest) = parser1' input 
    result = parser2' output rest 
    in result 

を、あなたが見ることができますあなたのパーサーのタイプシグネチャは、次のようなものでなければなりません:parseFid :: Pstate [PToken PStream]。これは、パーサーがいくつかの入力を消費し、解析されたものを[PToken PStream]として返し、新しい入力を残っているものに設定することを意味します。

parseFid :: Pstate [PToken PStream] 
parseFid = Pstate $ \r -> let (fid, rest) = span (/= '(') r in ([("fid", fid)],rest) 

残りは読者への課題として残されている:それはのように見えることができる方法についてparseFidのこの定義を考えてみましょう。代わりにControl.Monad.State.StrictStateモナドを使用してパーサーを再フォーマットすることをお勧めします。上記のモナドは基本的に同じであることがわかります。


実際には、独自のパーサーをロールダウンするのではなく、既存のよく知られているツールに頼るのが最も簡単です。ここでParsecで作成され、あなたが必要なもののためのパーサ、解析のためのアートライブラリーの状態です:

import Text.Parsec 

parseFunction = do name <- parseName 
        obrace <- parseOpeningBrace 
        args <- parseArguments 
        cbrace <- parseClosingBrace 
        return [name,obrace,args,cbrace] 

parseName   = many (noneOf "(") >>= \name -> return ("fid",name) 
parseOpeningBrace = char '(' >> return ("sym","(") 
parseArguments = many (noneOf ")") >>= \name -> return ("args",name) 
parseClosingBrace = char ')' >> return ("sym",")") 

main = case parse parseFunction "" "hello(some, args)" of 
    Left error -> print error 
    Right result -> print result 

ここでは出力です:

[("fid","hello"),("sym","("),("args","some, args"),("sym",")")] 

私は実際にあなたがいくつかのより良い表現を考えることを示唆しています解析された関数、これは物事をより簡単にするかもしれません。

+0

このような返事をいただき、ありがとうございます。しかし、問題はバインドメソッドだけですが、これらの_k a s 'etc_はメモリ内に保持するのが難しいと思います。パーサの観点から変数に名前を付けることは可能ですか?また、(m s)は奇妙に見えますが、Pstate型に値を適用していますが、最初に解凍してはいけませんか? – Dfr

+0

@Dfr、はい。まったく。私は実際に 'Control.Monad.State.Lazy'のコードをコピーし、それを私の必要性に変更しました。明らかに間違ったことが起こった。私はすぐにこれを修正するつもりです。 – fuz

+0

@Dfr:問題を修正しました。もう一度見てみましょう! – fuz

2

掲載されますが、コードを実行した場合、あなたはこの出力を得るよう、"this string also not used"文字列は、実際に使用されていることがわかります。

([("load",""),("fid","this string also not used"),("sym","("),("args","this string also not used"),("sym",")")],"") 

文字列は基本的にINPとして使用されている事実utをすべてのパーサに渡します。 >>=の定義では、文字列はbindparserへの入力として与えられます。このパーサーは、それを入力として受け取り、そこからトークンを作成します。たとえば、parseFidはトークン("fid","this string also not used")を生成します。

>>=で構成されたnewparserは、後で受け取る可能性のある入力を無視し、"this string also not used"の解析結果を返します。同様に、returnで作成されたパーサーは、返される値を無視します。

バインドで作成されたパーサーは、解析が正しく機能するように入力を無視/上書きしないでください。

また、Pstateの第2パラメータが果たすべき役割を決定する必要があります。これは、現時点では大部分が「???」を含んでおり、特に有用に見えません。

+0

はい、これは私が逃した別の奇妙なスポットです、私はそれについて考えていたと結論に_bindparser_は、その入力を無視する必要があります_newparser_戻り値から作成され、それは以前のバインドから来る:_newparser _ =(btok ++トック、休憩)_。しかし、_bindparser_入力が解析されずに通過するため、結果はまったく予期しないものであり、依然として間違っています。 2番目のパラメータは最後の解析ルーチンの結果でなければならないと思っており、_fid < - parseFid_と言うことができます。 – Dfr

+0

そして、実際のパーサはfparserです。これは、_bindparser_が結果を渡すだけですが、それはアイデアです。 – Dfr

関連する問題