0%

Automata Week 3 笔记

Introduction to context-free grammars

定义

  • Context-free grammar 是一个用来描述 language 的标记
  • 相比有限自动机和 RE 更加的强大,不过仍然不能表示所有 language
  • 对于像括号匹配和编程语言的 nested structures(?) 十分有用
  1. Terminals = 定义的 symbol, 类似于 alphabet
  2. Variables (aka. nonterminals) = 符号的有限集合,用来表示语言
  3. Start symbol = 类似于 start state
  4. Production 类似于 transition, 形如 variables(head) -> string of varibles and terminals(body)

    例如: $\lbrace 0^{n}1^{n}\mid n\geq0 \rbrace$ 其中

    • Terminals = $\lbrace 0,1 \rbrace$
    • Variables = $\lbrace S \rbrace$
    • Start symbol = S
    • Productions =
      • S -> 01
      • S -> 0S1
  5. Derivations 从 start symbol 开始,重复的使用 production 的 body 替换掉 head

    在刚才的例子中,S => 0S1 => 00S11 => 000111

  6. Iterated Derivation = =>* 就是进行0次或者更多的 derivation
  7. Sentential Forms 从 start symbol 开始推导所得到的 form

BNF(Backus-Naur Form) Notation

编程语言的语法通常会写成 BNF

  • Variables 写成 <…> 如: {statement}
  • Terminals 通常是粗体或者是带有下划线的 如 ${\bf WHILE}\ or\ \underline{WHILE}$
  • ::= 用来代替 ->
  • | 用来表示或

    如 S -> 0S1 | 01

  • … 用来表示一个或者多个

    如:
      {digit} ::= 0|1|2|3|4|5|6|7|8|9
      {unsigned integer} ::= {digit}

  • 条件:

    {statement} ::= if {condition} then {statement} [; else {statement}]

  • Grouping 用 {} 来代表需要被解释为整个 unit 的 symbol

Leftmost and Rightmost Derivations

就是从左还是从右开始 derivation

$\alpha =>_{lm} \beta$ 表示 leftmost

如:
S -> SS | (S) | ()
S =>lm SS =>lm (S)S =>lm (())S =>lm (())() 就是 leftmost
S =>rm SS =>rm S() =>rm (S)() =>rm (())() 则是 rightmost

Parse tree

定义

Parse tree 就是使用 symbols 来表示的特定的 CFG(Context-free grammar)

  • Leaves: 树叶标记着 terminal 或者是 $\epsilon$
  • Interior nodes: 内部节点标记着 variable
  • Root: 根节点必须是 start symbol

  • yield: 将叶子结点从左到右连接起来叫做 yield

然后就是两个证明:

  1. 对于给定的 parse tree 根节点是 A 并且 yield 是 w,则 $A =>_{lm} w$
  2. 如果 $A =>_{lm} w$ 则存在一个 parse tree 且它的根节点是 A 且能够 yield w

Ambiguous Grammars

如果一个 string 可以由两个或者更多的 parse tree 所得到,那么称这个 CFG 是 ambiguous

如图中的,这一个括号序列可以由两个 tree 得到

我们可以对括号序列的 grammar 进行修正

B -> (RB | ε R -> ) | (RR

需要注意的是,并不是所有的 ambiguous grammar 都可以进行修正(fix),这些不能修正的称为 inherently ambiguous,例如 $\lbrace 0^i1^j2^k \mid i = j or j = k \rbrace$

Normal forms for context-free grammars

这一节讲的是 CFG 的正则化,就是通过一些方法来化简 CFG

Variables That Derive Nothing

定义

有些时候,variables 不能完全的转化为 terminals,比如下面的例子:

S -> AB, A -> aA|a, B -> AB
虽然 A 能够推导出 a 的所有 string,但是 B 出现了循环引用,所以 S 无法转化为只有 terminals 的状态

Algorithm to Eliminate Variables That Derive Nothing

  1. 搜索(Discovery)所有能够推导出 terminal 的 variables
  2. 去除掉所有不存在于这个集合的 production(???)

总之,最后的效果是这样的

Unreachable Symbols

所谓 unreachable symbols,就是这个 symbol 不能通过推导所到达

Eliminating Useless Symbols

useful 是能够从 start symbol 通过某种 derivation 得到特定的 terminal
否则,就是 useless

清除 Useless Symbols 需要遵循一定的顺序

  1. Eliminate symbols that derive no terminal string
  2. Eliminate unreachable symbols

注意这个顺序

S -> AB, A -> C, C -> c, B -> bB
如果先是去除 unreachable symbols,会发现所有的都是可以 reachable 的

Epsilon Productions

有时候我们要避免出现 A -> ε (叫做 ε-productions)的情况

Nullable Symbols

要清除 ε-productions,我们首先需要搜索 nullable symbols

S -> AB, A -> aA | ε, B -> bB | A
A 是一个 nullable,所以 B 也是
因为 S -> AB 所以 S 也是 nullable

Eliminating ε-Productions

直接上例子

S -> ABC, A -> aA | ε, B -> bB | ε, C -> ε
A, B, C 和 S 是 nullable
S -> ABC | AB | AC | BC | A | B | C
A -> aA | a
B -> bB | b

Unit Productions

unit production 就是某个 production 的 body 中只含有一个 varible

去除掉 unit productions 也比较简单

如果 A => B 且 b -> $\alpha$ 则增加一个 production A -> $\alpha$
最后扔掉其他的 production

Cleaning Up a Grammar

要清理一个 CFG,需要按照以下步骤:

  1. Eliminate ε-productions.
  2. Eliminate unit productions.
  3. Eliminate variables that derive no
    terminal string.
  4. Eliminate variables not reached from the
    start symbol.

注意第一步必须在前边

Chomsky Normal Form

如果一个 CFG 的 production 只有两种形式

  1. A -> BC (2个 variables)
  2. A -> a (1个 terminal)

那么这个 CFG 被称为 Chomsky Normal Form

Pushdown automata

PDA(Pushdown automata) 可以直观的理解为带有一个栈的 ε-NFA,不同于 NFA 的是,NFA 的转移只依赖于当前的状态和输入,而 PDA 还依赖于栈顶的元素

比如之前讨论过的 $\lbrace 0^{n}1^{n}\mid n\geq0 \rbrace$ 可以使用 PDA 来表示:

  • δ(q, 0, Z0) = {(q, XZ0)}
  • δ(q, 0, X) = {(q, XX)}
    以上两条规则遇到输入为0时将 X 压入栈中
  • δ(q, 1, X) = {(p, ε)} 当遇到 1 时,弹出栈顶元素,并且转移到 p
  • δ(p, 1, X) = {(p, ε)} 遇到 1 时,出栈
  • δ(p, ε, Z0) = {(f, Z0)}

如果 P 是一个 PDA,

  1. L(P) 是能够转移到末状态的 string
  2. N(P) 是能够使栈中元素为空的 string