1.2 使用属性和约束

我们说过非正式的语言类别具有 属性 ;例如,名词具有复数的属性。让我们把这个弄的更明确:

  1. N[NUM=pl]

注意一个句法类别可以有多个特征,例如V[TENSE=pres, NUM=pl]。在一般情况下,我们喜欢多少特征就可以添加多少。

关于1.1的最后的细节是语句%start S。这个“指令”告诉分析器以S作为文法的开始符号。

一般情况下,即使我们正在尝试开发很小的语法,把产生式放在一个文件中我们可以编辑、测试和修改是很方便的。我们将1.1以 NLTK 的数据格式保存为文件'feat0.fcfg'。你可以使用nltk.data.load()制作你自己的副本进行进一步的实验。

1.2 说明了基于特征的语法图表解析的操作。为输入分词之后,我们导入load_parser函数[1],以语法文件名为输入,返回一个图表分析器cp [2]。调用分析器的parse()方法将迭代生成的分析树;如果文法无法分析输入,trees将为空,并将会包含一个或多个分析树,取决于输入是否有句法歧义。

  1. >>> tokens = 'Kim likes children'.split()
  2. >>> from nltk import load_parser ![[1]](/projects/nlp-py-2e-zh/Images/77460905bcad52d84e324fc4821ed903.jpg)
  3. >>> cp = load_parser('grammars/book_grammars/feat0.fcfg', trace=2) ![[2]](/projects/nlp-py-2e-zh/Images/07e7d99633e4a107388f7202380cce55.jpg)
  4. >>> for tree in cp.parse(tokens):
  5. ... print(tree)
  6. ...
  7. |.Kim .like.chil.|
  8. Leaf Init Rule:
  9. |[----] . .| [0:1] 'Kim'
  10. |. [----] .| [1:2] 'likes'
  11. |. . [----]| [2:3] 'children'
  12. Feature Bottom Up Predict Combine Rule:
  13. |[----] . .| [0:1] PropN[NUM='sg'] -> 'Kim' *
  14. Feature Bottom Up Predict Combine Rule:
  15. |[----] . .| [0:1] NP[NUM='sg'] -> PropN[NUM='sg'] *
  16. Feature Bottom Up Predict Combine Rule:
  17. |[----> . .| [0:1] S[] -> NP[NUM=?n] * VP[NUM=?n] {?n: 'sg'}
  18. Feature Bottom Up Predict Combine Rule:
  19. |. [----] .| [1:2] TV[NUM='sg', TENSE='pres'] -> 'likes' *
  20. Feature Bottom Up Predict Combine Rule:
  21. |. [----> .| [1:2] VP[NUM=?n, TENSE=?t] -> TV[NUM=?n, TENSE=?t] * NP[] {?n: 'sg', ?t: 'pres'}
  22. Feature Bottom Up Predict Combine Rule:
  23. |. . [----]| [2:3] N[NUM='pl'] -> 'children' *
  24. Feature Bottom Up Predict Combine Rule:
  25. |. . [----]| [2:3] NP[NUM='pl'] -> N[NUM='pl'] *
  26. Feature Bottom Up Predict Combine Rule:
  27. |. . [---->| [2:3] S[] -> NP[NUM=?n] * VP[NUM=?n] {?n: 'pl'}
  28. Feature Single Edge Fundamental Rule:
  29. |. [---------]| [1:3] VP[NUM='sg', TENSE='pres'] -> TV[NUM='sg', TENSE='pres'] NP[] *
  30. Feature Single Edge Fundamental Rule:
  31. |[==============]| [0:3] S[] -> NP[NUM='sg'] VP[NUM='sg'] *
  32. (S[]
  33. (NP[NUM='sg'] (PropN[NUM='sg'] Kim))
  34. (VP[NUM='sg', TENSE='pres']
  35. (TV[NUM='sg', TENSE='pres'] likes)
  36. (NP[NUM='pl'] (N[NUM='pl'] children))))

分析过程中的细节对于当前的目标并不重要。然而,有一个实施上的问题与我们前面的讨论语法的大小有关。分析包含特征限制的产生式的一种可行的方法是编译出问题中特征的所有可接受的值,是我们最终得到一个大的完全指定的(6)中那样的 CFG。相比之下,前面例子中显示的分析器过程直接与给定语法的未指定的产生式一起运作。特征值从词汇条目“向上流动”,变量值于是通过如{?n: 'sg', ?t: 'pres'}这样的绑定(即字典)与那些值关联起来。当分析器装配有关它正在建立的树的节点的信息时,这些变量绑定被用来实例化这些节点中的值;从而通过查找绑定中?n?t的值,未指定的VP[NUM=?n, TENSE=?t] -> TV[NUM=?n, TENSE=?t] NP[]实例化为VP[NUM='sg', TENSE='pres'] -> TV[NUM='sg', TENSE='pres'] NP[]

最后,我们可以检查生成的分析树(在这种情况下,只有一个)。

  1. >>> for tree in trees: print(tree)
  2. (S[]
  3. (NP[NUM='sg'] (PropN[NUM='sg'] Kim))
  4. (VP[NUM='sg', TENSE='pres']
  5. (TV[NUM='sg', TENSE='pres'] likes)
  6. (NP[NUM='pl'] (N[NUM='pl'] children))))