如何根据简化的语法解析单词列表?

Ben*_*Ben 8 lisp algorithm haskell nlp

只是为了澄清,这不是功课.我已被要求提供帮助,但我无法做到,所以它变成了解决它的个人追求.

想象一下,你有一个像这样的英语句子的语法:

S => NP VP | VP
NP => N | Det N | Det Adj N
VB => V | V NP
N => i you bus cake bear
V => hug love destroy am
Det => a the
Adj => pink stylish
Run Code Online (Sandbox Code Playgroud)

我搜索了好几个小时,真的没有想法.我发现文章谈论浅层解析,深度优先回溯和相关的事情,虽然我熟悉它们中的大多数,但我仍然不能将它们应用于这个问题.我标记了Lisp和Haskell,因为这些是我计划实现的语言,但我不介意你在回复中使用其他语言.

我很欣赏一些提示,好文章和一切.

Dan*_*ner 4

这是一个有效的 Haskell 示例。事实证明,在让它发挥作用之前,需要学习一些技巧!要做的第零件事是样板文件:关闭可怕的单态限制,导入一些库,并定义一些不在库中(但应该在库中)的函数:

{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Applicative ((<*))
import Control.Monad
import Text.ParserCombinators.Parsec

ensure p x = guard (p x) >> return x
singleToken t = tokenPrim id (\pos _ _ -> incSourceColumn pos 1) (ensure (==t))
anyOf xs = choice (map singleToken xs)
Run Code Online (Sandbox Code Playgroud)

现在第零件事已经完成......首先,我们为抽象语法树定义一个数据类型。我们可以在这里遵循语法的形式。然而,为了使它更方便,我考虑了一些语法规则;特别是这两条规则

NP => N | Det N | Det Adj N
VB => V | V NP
Run Code Online (Sandbox Code Playgroud)

当实际编写解析器时,这样写会更方便:

NP => N | Det (Adj | empty) N
VB => V (NP | empty)
Run Code Online (Sandbox Code Playgroud)

任何一本关于解析的好书都会有一章来解释为什么这种因式分解是一个好主意。所以,AST 类型:

data Sentence
    = Complex NounPhrase VerbPhrase
    | Simple VerbPhrase
data NounPhrase
    = Short Noun
    | Long Article (Maybe Adjective) Noun
data VerbPhrase
    = VerbPhrase Verb (Maybe NounPhrase)
type Noun      = String
type Verb      = String
type Article   = String
type Adjective = String
Run Code Online (Sandbox Code Playgroud)

然后我们就可以制作我们的解析器了。这个更加严格地遵循(因式分解)语法!这里的一个问题是我们总是希望我们的解析器消耗整个句子,所以我们必须通过要求一个“eof”——或“文件”结尾来明确要求它做到这一点。

s   = (liftM2 Complex np vp <|> liftM Simple vp) <* eof
np  = liftM Short n <|> liftM3 Long det (optionMaybe adj) n
vp  = liftM2 VerbPhrase v (optionMaybe np)
n   = anyOf ["i", "you", "bus", "cake", "bear"]
v   = anyOf ["hug", "love", "destroy", "am"]
det = anyOf ["a", "the"]
adj = anyOf ["pink", "stylish"]
Run Code Online (Sandbox Code Playgroud)

最后一部分是分词器。对于这个简单的应用程序,我们将仅根据空格进行标记,因此内置words函数可以正常工作。我们来尝试一下吧!在 ghci 中加载整个文件:

*Main> parse s "stdin" (words "i love the pink cake")
Right (Complex (Short "i") (VerbPhrase "love" (Just (Long "the" (Just "pink") "cake"))))
*Main> parse s "stdin" (words "i love pink cake")
Left "stdin" (line 1, column 3):
unexpected "pink"
expecting end of input
Run Code Online (Sandbox Code Playgroud)

这里,Right表示解析成功,Left表示解析错误。由于我们计算 中源位置的方式,错误中报告的“列”号实际上是发生错误的字号singleToken