在Haskell的解析器为三倍

时间:2017-07-04 14:06:57

标签: parsing haskell functional-programming

我目前正在做关于Haskell解析的任务,但我正在努力解决一些基础问题。

作业: 我应该创建一个函数,将一个字符串解析为三元组列表。 以便: A,B,C   ,E,D

会导致

Triples [("A","B","C"), ("A","E","D")]

输入字符串将包含; \ n作为新Triple的开头的指示。字符串将以点结尾。 三元组的元素可以是字母或数字或组合, 例如abc,a,1,abc121。

因此,

"a,b,c;\n d,e;\n f,g;\n h,i."  

会导致:

Triples [("a","b","c"),("a","d","e"),("a","f","g"),("a","h","i")]

我目前的解决方案:

parseTriplesD :: Parser Triples
parseTriplesD = parseTriples 
            >>= \rs -> return (Triples rs)

这个功能非常简单和正确。获取字符串并使用parseTriples创建的三元组列表返回newtype Triples的对象。

parseTriples :: Parser [Triple]
parseTriples = parseTriple 
            >>= \r -> ((string ";\n" >> parseTriples >>= \rs -> return (r:rs)) 
            P.<|>(return[r]))

这个功能需要一些工作。我的想法是我使用另一个函数创建一个带有树的三元组输入字符串的元素,忽略/ n并在将创建的三元组添加到返回列表时递归调用它。如果这不起作用,因为它只能创建一个Triple,它将返回一个包含Triple的列表。 我不知何故需要创建第一个Triple,然后使用这个三元组的第一个元素作为其他元素的第一个元素。

问题1 如何创建第一个Triple并将Triple的第一个元素用于其他三元组?

parseTriple :: Parser Triple
parseTriple = P.many (letter<|>digit) >>= \a -> P.char ','
            >> P.many (letter<|>digit)>>= \b -> P.char ','
            >> P.many (letter<|>digit)>>= \c -> return ((a,b,c))

这个功能非常简单,但我不确定它是否正确。 我的想法是它需要字符串的前几个字符,可以是字母或数字,直到逗号&#34;,&#34; ,并将这些字符保存在一个。 重复3次,创建并返回带有三个元素的Triple。

问题2 在逗号之前,如何只使用字符串中的几个字符(字母或数字EDIT:或空格字符)? P.many(字母&lt; |&gt;数字)是否正确?

我们得到了什么:

三元组数据结构:

newtype Triples = Triples [Triple] deriving (Show,Eq)
type Triple = (String, String, String) 

进口:

import Test.HUnit (runTestTT,Test(TestLabel,TestList),(~?=))
import qualified Text.Parsec as P (char,runP,noneOf,many,(<|>),eof)
import Text.ParserCombinators.Parsec
import Text.Parsec.String 
import Text.Parsec.Char
import Data.Maybe

测试用例

runParsec :: Parser a -> String -> Maybe a
runParsec parser input = case P.runP parser () "" input of
    Left  _ -> Nothing
    Right a -> Just a

-- | Tests the implementations of 'parseScore'.
main :: IO ()
main = do
    testresults <- runTestTT tests
    print testresults

-- | List of tests for 'parseScore'.
tests :: Test
tests = TestLabel "parseScoreTest" (TestList [
    runParsec parseTriplesD "0,1,2;\n2,3."   ~?= Just (Triples [("0","1","2"),("0","2","3")]),
    runParsec parseTriplesD "a,bcde ,23." ~?= Just (Triples [("a","bcde ","23")]),
    runParsec parseTriplesD "a,b,c;\n d,e;\n f,g;\n h,i." ~?= Just (Triples [("a","b","c"),("a","d","e"),("a","f","g"),("a","h","i")]),
    runParsec parseTriplesD "a,bcde23." ~?= Nothing,
    runParsec parseTriplesD "a,b,c;d,e;f,g;h,i." ~?= Nothing,
    runParsec parseTriplesD "a,b,c;\nd;\nf,g;\nh,i." ~?= Nothing
    ])

1 个答案:

答案 0 :(得分:2)

你能做的是:

  1. 解析第一个字符
  2. 解析成对列表
  3. 将第一个字符添加到每个对中以创建三元组
  4. 使用do表示法会使您的代码更具可读性 您可以使用alphaNum作为letter <|> digit的简写。

    parseTriplesD :: Parser Triples
    parseTriplesD = Triples <$> parseTriples
    
    parseTriples :: Parser [Triple]
    parseTriples = do
        a <- parseString
        char ','
        pairs <- parsePair `sepBy1` string ";\n"
        char '.'
        eof
        return (map (\(b, c) -> (a, b, c)) pairs)
    
    parsePair :: Parser (String, String)
    parsePair = do
        first <- parseString
        char ','
        second <- parseString
        return (first, second)
    
    parseString :: Parser String
    parseString = many (char ' ') >> many (alphaNum <|> char ' ')