Haskell Parsec跳过所有未预定义的单词

时间:2012-10-26 13:55:56

标签: haskell parsec

学习使用Parsec库,作为家庭作业的一部分。

编辑:欢迎使用其他库的建议,重点是解析。

我想要的是用大写字母提取所有单词,并从任何句子中提取四个罗盘方向。例如:“比利时完全位于荷兰南部。”应找到并返回“比利时南荷兰”。

我无法想象的是如何忽略(吃掉)任何不是指南针方向的输入。 我希望找到符合

的内容
'many (not compassDirection >> space)'

但g(h)oogle并没有帮助我。

以下代码显然停留在'many'函数上。

readExpr :: String -> String
readExpr input = case parse (parseLine) "" input of
    Left err -> "No match: " ++ show err
    Right val -> "Found: " ++ showVal val

parseLine :: Parser GraphValue
parseLine = do
            x <- parseCountry
            space
            many ( some (noneOf " ") >> space )
            y <- parseCompass
            space
            many ( some (noneOf " ") >> space )
            z <- parseCountry
            return $ Direction [x,y,z]

compassDirection :: Parser String
compassDirection = string "north" <|>
                   string "south" <|>
                   string "east" <|>
                   string "west"

parseCountry :: Parser GraphValue
parseCountry = do 
                c <- upper 
                x <- many (lower)
                return $ Country (c:x)

parseCompass :: Parser GraphValue
parseCompass = do 
                x <- compassDirection
                return $ Compass x

3 个答案:

答案 0 :(得分:4)

我不会详细说明,因为这是家庭作业,OP说“重要的是解析”。


我解决这个问题的方法:

  • 标记输入。把它分解成文字;这将使真正的解析步骤免于担心令牌定义(即“是%#@ [单词的一部分?”)或空格。这可以像words一样简单,也可以使用Parsec进行标记化。然后,您将[Token](或[String],如果您愿意的话)。

  • 罗盘方向的解析器。你已经有了这个(干得好),但如果输入是[String]而不是String,则必须修改一下。

  • 以大写字母开头的单词解析器。

  • 其他所有内容的解析器,只要它看到一个不是指南针方向的标记或一个以大写字母开头的单词就会成功。

  • 一个解析器,可以处理任何令牌,但可以区分好东西和坏东西,也许使用代数数据类型。

  • 适用于许多令牌的解析器

希望这一点很清楚,而不是清楚;例如,你仍然需要担心什么时候丢弃垃圾。基本思路是将问题分解为许多小问题,解决子问题,然后将这些解决方案粘合在一起。

答案 1 :(得分:3)

我将告诉你我将如何开始,然后就如何继续进行建议。

我的基础是一个抽象的数据结构 - 当你添加额外的单词时,你可以更仔细地对它们进行分类:

data Word = Country String | Direction NSEW | Unclassified String
data NESW = North | East | South | West

所以我对你如何跳过你不知道的单词的答案就是你不需要 - 将它们保留为未分类。

应用风格比monadic风格更好。

我认为compassDirection应该允许大写:

compassDirection :: Parser NESW
compassDirection = north <|> south <|> east <|> west where
    north = North <$ (string "north" <|> string "North")
    east = ...

您可以使用country

定义Country <$> ((:) <$> upper <*> many lower)

然后你可以拥有一个全能Unclassified <$> many letter

您的单词解析器目前可以

word = compassDirection <|> country <|> unclassified

但请注意compassDirection必须在country之前出现,否则country将匹配North

你可以做到

words = word `sepBy1` space

目前没关系,但是当你正确地分析句子时,你必须绝对不能使用wordwords,因为你无法控制这个词是什么。此时,您需要nounadjectivenounPhraseverbadjectiveadjectivalPhrase等来处理句子结构。你无法解析的句子意味着你需要在你的语法中添加新的结构。

值得让单词解析器吞下它们之前(或之前)的空格,或者使用从空格和标点符号中分割单词的预处理器进行重构。如果您是英国人,请考虑使用fullStop解析器,如果您是美国人,请考虑使用period解析器。在创建句子解析器时使用它。

使用applicative和更高阶函数会使你的语法写得更清晰,因为你不会用monadic符号弄乱它,它看起来像句子。 示例:如果要使用主要的抽象数据结构(AST)方法,每个语法对象使用一个构造函数,则可以执行nvn = NVN <$> noun <*> verb <*> noun。 如果您只是想在所有相同类型中放置一堆单词,则可以执行nvn = sequence [noun,verb,noun]

大多数计算机语言都是使用AST方法解析的,但除了我从妻子的语言学学位中获得的二手资料之外,我没有自然语言解析的直接经验。

如果你坐下来写下如何将单词,短语,从句和句子的类别组合在一起,你就能够很快地编写解析器。

答案 2 :(得分:0)

您不能将字符串拆分为wordsfilter以大写字母开头或指南针方向,然后unwords将它们重新组合在一起吗?无需拔出Parsec枪。