<jupyter_start><jupyter_text>上下文无关文法 一种简单的文法上下文无关文法（context-free grammar，CFG）通常由形如 V -> w 的产生规则组成，取名为“上下文无关”的原因是因为字符 V 总可以被字串 w 自由替换，而无需考虑字符 V 出现的上下文。在 NLTK 中，上下文无关文法定义在 nltk.grammar 模块中，按照惯例，第一条产生式的左端是文法的**开始符号**，通常是 S，所有符合语法规则的树都必须有这个符号作为它们的根标签。<jupyter_code>import nltk

grammar1 = nltk.CFG.fromstring("""
    S -> NP VP
    VP -> V NP | V NP PP
    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 = '<NAME> Bob'.split()
rd_parser = nltk.RecursiveDescentParser(grammar1)
for tree in rd_parser.parse(sent):
    print(tree)<jupyter_output>(S (NP Mary) (VP (V saw) (NP Bob)))<jupyter_text>文法字符串中的每一行代表一条产生式，产生式中的 | 符号表示“或”的意思，其中涉及到的各种句法类型可以参照下表：| 符号 | 意思     | 例子             ||------|----------|------------------|| S    | 句子     | the man walked   || NP   | 名词短语 | a dog            || VP   | 动词短语 | saw a park       || PP   | 介词短语 | with a telescope || Det  | 限定词   | the              || N    | 名词     | dog              || V    | 动词     | walked           || P    | 介词     | in               |我们还可以使用图形界面 [nltk.app.rdparser](https://www.nltk.org/_modules/nltk/app/rdparser_app.htmlapp) 查看文法分析的全过程：<jupyter_code>nltk.app.rdparser()<jupyter_output><empty_output><jupyter_text>![parse_rdparsewindow.png](resources/parse_rdparsewindow.png)  写你自己的文法可以将上面的文法字符串保存在文件中，然后通过 [nltk.data.load()](http://www.nltk.org/_modules/nltk/data.htmlload) 方法加载，需要注意的是确保文法文件名后缀为 .cfg ：<jupyter_code>grammar1 = nltk.data.load('file:mygrammar.cfg')
sent = 'Mary saw Bob'.split()
rd_parser = nltk.RecursiveDescentParser(grammar1)
for tree in rd_parser.parse(sent):
    print(tree)<jupyter_output>(S (NP Mary) (VP (V saw) (NP Bob)))<jupyter_text>句法结构中的递归一个文法被认为是递归的，如果文法类型出现在产生式左侧也出现在右侧。在下面的文法中，产生式 Nom -> Adj Nom（其中 Nom 是名词性的类别）包含 Nom 类型的**直接递归**，而产生式 S -> NP VP 和 VP -> V S 的组合构成了关于 S 的**间接递归**：<jupyter_code>grammar2 = nltk.CFG.fromstring("""
    S -> NP VP
    NP -> Det Nom | PropN
    Nom -> Adj Nom | N
    VP -> V Adj | V NP | V S | V NP PP
    PP -> P NP
    PropN -> 'Buster' | 'Chatterer' | 'Joe'
    Det -> 'the' | 'a'
    N -> 'bear' | 'squirrel' | 'tree' | 'fish' | 'log'
    Adj -> 'angry' | 'frightened' | 'little' | 'tall'
    V -> 'chased' | 'saw' | 'said' | 'thought' | 'was' | 'put'
    P -> 'on'
""")

rd_parser = nltk.RecursiveDescentParser(grammar2)

for tree in rd_parser.parse('the angry bear chased the frightened little squirrel'.split()):
    tree.draw()
    
for tree in rd_parser.parse('Chatterer said Buster thought the tree was tall'.split()):
    tree.draw()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.17552395209580837, 'fraction_numerical': 0.002245508982035928, 'mean_word_length': 3.0934150076569678, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 38, 'https://': 1, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '367876', 'license': 'MIT', 'n_tokens_mistral': 1346, 'n_tokens_neox': 1382, 'n_words': 232, 'path': '8.3-context-free-grammar.ipynb', 'repo_name': 'pen-x/natural-language-processing-with-python', 'size': '2674'}