Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

表达式的优先级与结合性

复习

  • 递归下降分析:每个非终结符对应一个函数
  • 上下文无关文法:产生式与推导
  • 语法树:结构的树形表示

TL;DR

  • 优先级决定谁的运算先结合,结合性决定同级如何结合
  • 文法可以通过分层来表达优先级
  • 1 + 2 * 3 应解析为 1 + (2 * 3)
  • 1 - 2 - 3 通常解析为 (1 - 2) - 3(左结合)

正文

  表达式是程序里最常见的结构,也是语法分析里最讲究细节的地方。原因在于,1 + 2 * 3 这种写法,光看词序列,还不足以确定运算顺序。 这就要靠优先级和结合性来约定。

优先级:谁先算

  数学里我们都知道“先乘除、后加减”。1 + 2 * 3 应该算成 1 + (2 * 3) = 7,而不是 (1 + 2) * 3 = 9

  这种约定叫优先级(precedence):不同运算符有不同的“级别”,级别高的先结合。

  怎样让文法体现优先级?办法是分层。不要用一条规则把所有运算符都塞在一起,而是按优先级分成好几层:

表达式 → 项 加减
项     → 因子 乘除
因子   → 数字

  这样,乘法在更低的“项”这一层就被结合好了,再往上才轮到加法。文法的层次,直接对应了运算的优先级。

结合性:同级怎么算

  当同一优先级的运算符连在一起时,还有第二个约定:结合性(associativity)。

  看 1 - 2 - 3。它既可以算成 (1 - 2) - 3 = -4,也可以算成 1 - (2 - 3) = 2。数学约定是从左到右,也就是左结合,答案是 -4。

  在文法里,结合性体现在递归展开的方向上:

  • 左结合:规则形如 加减 → 加减 + 项,从左往右累积
  • 右结合:规则形如 幂 → 因子 ^ 幂,从右往左累积

  这两种写法,会让语法树长成不同的形状——左结合长成往左倾斜,右结合长成往右倾斜。

结构决定了运算顺序

  说到底,优先级和结合性,都是在确定这棵语法树到底长什么样。树一定,运算顺序就定了:

  • 越深的子树越先算
  • 同级的按左右顺序算

  所以,语法分析不只是“认出这是个表达式”,更要准确地还原它的层次。优先级分层、结合性定向,正是为了把这件事做对。而如果文法没把这些约定表达清楚,就会产生“一棵树还是另一棵树都行”的麻烦——这就是下一章的歧义。

思考题 1

  怎样用文法分层来表达“乘法优先于加法”?

思考题 2

  左结合和右结合,在语法树上分别表现为什么形状?

小结

知识点

  • 优先级决定不同运算符的结合顺序
  • 文法分层可以表达优先级
  • 结合性决定同级运算符的结合方向
  • 递归展开的方向决定左结合或右结合

参考资料

  1. Wikipedia(zh):运算次序:运算符的优先级与结合性
  2. Wikipedia(zh):上下文无关文法:用分层表达优先级

思考题答案(仅供参考)

思考题 1

  把加法放在“表达式”这一层、乘法放在更低的“项”这一层:表达式由项和加减构成,项由因子和乘除构成。乘法在更低层先结合,因而优先级更高,加法到上层才处理。

思考题 2

  左结合(如 加减 → 加减 + 项)会让语法树向左倾斜,先结合左边的运算;右结合(如 幂 → 因子 ^ 幂)会让语法树向右倾斜,先结合右边的运算。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪