2011-01-09 4 views
4

예상대로 작동하지 않는 이유는 게으름 질문이나 좀 코드의 거대한 조각 하스켈, 마침내 때문에 모나드 물건의 거대한 가지고 있지만, 작업은 간단하다 : 데이터 구조에 다음과 같은 문자열을 구문 분석 :이 모나드 여기

" hello (some, args) "-> [("fid ","hello "), ("sym ","("), ("args ","some, args "), ("sym " (-

"헬로 (일부 인수)"> [("FID" "") ("SYM", "("))] 나 기록

이지만, 코드는 다음과 생산 "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 

이것은 모나드의 기본 구조입니다. 모나 딕 결합자를 정의한다면, 실제로는 필요하지 않은 곳에 결합자를 갖는 것은 드문 일이 아니기 때문에,이 경우 컨버전은 ()으로 남겨 두는 것이 관례입니다.

사실 나는 당신의 코드가 매우 이상하다고 생각합니다. 마치 당신이 stateful 한 모나드의 요점을 움켜 잡지 못한 것처럼 보입니다.설명해 드리죠 :

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의 정의를 생각해 보자. 대신 의 State 모나드를 사용하여 파서를 다시 형식화하는 것이 좋습니다. 위의 모나드는 기본적으로 동일하다는 것을 알 수 있습니다.


사실 실제로는 기존의 잘 알려진 도구를 사용하는 대신 자신의 파서를 사용하는 것이 가장 쉽습니다. 여기

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",")")] 

여기가 Parsec, 구문 분석에 대한 기술이 라이브러리의 상태로 만들 필요가 무엇인지에 대한 파서입니다 구문 분석 된 함수, 이것은 일을 더 쉽게 만들 수 있습니다.

+0

답장을 보내 주셔서 감사합니다.하지만 문제는 많이 해결되지만 여전히 문제는 바인드 방법입니다. 메모리를 계속 유지하는 것이 어렵습니다. 파서 측면에서 변수 이름을 지정할 수 있습니까? 또한 (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의 두 번째 매개 변수가 수행해야하는 역할을 결정해야합니다. 그 순간에는 주로 유용하게 보이지 않는 "???"가 포함되어 있기 때문입니다.

+0

그래,이 또 다른 이상한 지점 내가 놓친, 내가 그것에 대해 생각하고 그 결과 _newparser_ 일이 있기 때문에 _bindparser _ 입력을 무시해야 결론에 왔고 이전 바인딩에서 오는 : _newparser _ = (btok ++ tok, rest) _. 그러나 _bindparser_ 입력이 분석되지 않은 채 통과하기 때문에 결과는 전혀 예상치 못했지만 여전히 잘못되었습니다. 두 번째 매개 변수는 마지막 파싱 루틴의 결과 여야한다고 생각하고 _fid <- parseFid_를 말할 수 있습니다. – Dfr

+0

그리고 실제 구문 분석기는 fparser이며, _bindparser_가 결과를 전달하는 동안 모든 구문 분석을 수행합니다. 이는 아이디어였습니다. – Dfr