在英语中总是能够通过从句将一个句子添加到另一个句子中使得句子结构变得更加复杂。列入下面的四个句子
a. Usain Bolt broke the 100m record.
b. The Jamaica Observer reported that Usain Bolt broke the 100m record.
c. Andre said The Jamaica Observer reported that Usain Bolt broke the 100m re
cord.
d. I think Andre said the Jamaica Observer reported that Usain Bolt broke the 1
00m record.
然而不可否定句子中存在句子的语法,使用这些语法构造出来的句子即使再复杂,使用这种语言的人也能够明白句子的意思。基于这样的思想产生了文法,如下程序所示:
import nltk
from nltk import CFG
#文法
grammar = nltk.CFG.fromstring("""
S -> NP VP
PP -> P NP
NP -> Det N | Det N PP | 'I'
VP -> V NP | VP PP
Det -> 'an' | 'my'
N -> 'elephant' | 'pajamas'
V -> 'shot'
P -> 'in'
""")
#句子
sent = 'I shot an elephant in my pajamas'.split()
parser = nltk.RecursiveDescentParser(grammar)
for i in parser.parse(sent):
print(i)

S -> NP VP 定义一个句子的组成包括 NP块和VP块,然后又递推定义了NP,VP,PP等,递推最后是句子的单词,能够句子I shot an elephant in my pajamas反推出S。如果某个句子不能使用这些文法推出来说明句子不满足此文法。
从运行结果可以看到句子存在歧义,有两种理解方式。使用draw()方法绘制树形结构。

第一种结果shot可以理解为射击因为 in my pajamas修饰的是elephant
第二种shot可以理解为拍照,in my pajamas修饰shot
文法的作用
and 连接的两个部分是相同的,如NP and NP组成一个NP块结构。不能连接两个不同的块。使用文法能使难于理解的句子简单一点。句子中独立的块可以被相同的其它块或者单词替换句子不会出现语法错误。如下面的句子: the little bear saw the fine fat trout in the brook.(trout:鲑鱼,brook:小溪)

替换之后也不会出现语法错误。最后一个NP + VP就是句子结构,使用s表示句子,提出句子的文法S - >NP VP, NP -> Det Nom,.......可以得到句子的文法,树结构如图:

上下文无关文法
我的理解是形如上面这样的句子中的成分可以被任意替换不用考虑词位置的文法就是上下文无关文法(context-free grammar,CFG)。下面就是一个上下文无关文法。| 两边的都可以任意替换不会出现语法错误。
import nltk
grammar1 = nltk.CFG.fromstring("""
S -> NP VP
VP -> V NP | V NP PP
PP -> P NP
V -> "saw" | "ate" | "walked"
NP -> "John" | "Mary" | "Bob" | Det N | Det N PP
Det -> "a" | "an" | "the" | "my"
N -> "man" | "dog" | "cat" | "telescope" | "park"
P -> "in" | "on" | "by" | "with"
""")
sent = "the dog saw a man in the park".split()
#递归下降分析器
rd_parser = nltk.RecursiveDescentParser(grammar1)
for tree in rd_parser.parse(sent):
#输出文本树结构
print(tree)
#画出树形图
tree.draw()
写自己的文法
介于下面的这个文法是我瞎写的,只能是看看如何实现自定义的文法。程序没错,句子也是文法的句子。
文法(mygrammar.cfg):
S -> NP V PP
NP -> "I" | "student"
PP -> N NP
V -> "am"
N -> "a"
import nltk
grmmar = nltk.data.load("file:mygrammar.cfg")
sent = "I am a student".split()
rd_parser = nltk.RecursiveDescentParser(grmmar)
for tree in rd_parser.parse(sent):
print(tree)
tree.draw()

编写的时候如果句子不能使用文法产生,程序运行不会有结果。
句法结构中的递归
当产生式两边都出现文法类型的时候文法被认为是递归的。如Nom -> Adj Nom,这种称为直接递归,另外也存在间接递归。

RecursiveDescentParser是无法处理X -> X Y这样的左递归文法的。
网友评论