← 返回题目列表

解释器模式中终结符表达式和非终结符表达式有什么区别?

高频 中等 第 8 / 25 题 更新于 2026/07/28
解释器模式终结符非终结符语法树

简化版

终结符表达式表示语法中不可再拆分的基本单元,如变量、数字、常量;非终结符表达式表示由其他表达式组合出来的语法规则,如加法、比较、与或逻辑。终结符是叶子节点,非终结符是中间节点。

详细版

终结符表达式:

  • 语法树叶子节点。
  • 不再包含子表达式。
  • 通常从 Context 中取值或返回常量。
  • 例子:变量 age、数字 18、布尔值 true

非终结符表达式:

  • 语法树中间节点。
  • 包含一个或多个子表达式。
  • 组合子表达式结果。
  • 例子:age > 18A AND Bx + y

解释器模式通过这两类表达式组合出完整语法树。

完整版教学

一、终结符为什么叫终结

终结符来自形式语言概念,意思是语法推导到这里就结束了,不能再拆。

在表达式树中,它就是叶子节点。比如数字 10 不需要再解释成更小的表达式,变量 age 也通常只是从上下文取一个值。

二、非终结符负责组合

非终结符表达式代表一种组合规则。比如 AndExpression 由左右两个布尔表达式组成:

return left.interpret(context) && right.interpret(context);

它自己不直接给出结果,而是依赖子表达式结果再组合。

三、复杂表达式如何形成树

例如:

age > 18 AND vip = true

可以拆成:

  • age18 是终结符。
  • age > 18 是非终结符。
  • vip = true 是非终结符。
  • 两个条件再组成 AND 非终结符。

最终根节点解释结果就是整个表达式结果。

四、常见误区与工程判断

不要把所有逻辑都塞进一个 TerminalExpression。终结符应该尽量简单,复杂组合逻辑应该放到非终结符里。

工程中如果发现某个表达式类既解析字符串、又查变量、又做组合判断,职责已经混乱。表达式节点应该只负责自己的语法语义。

五、终结符和非终结符对应语法树的叶子与分支

理解终结符和非终结符,可以把表达式树想成一棵真实的树。终结符表达式通常是叶子节点,不能继续拆,比如变量 age、常量 true、数字 10。非终结符表达式通常是分支节点,内部还包含其他表达式,比如 A && BA || B!A

执行时,叶子节点直接从 Context 取值或返回固定值;分支节点先解释子表达式,再按照自己的语法规则组合结果。比如 AndExpression 会先解释 left 和 right,再做逻辑与;AddExpression 会先解释左右操作数,再做加法。

面试时容易混淆的一点是:终结符不等于“字符串里的最后一个字符”,非终结符也不等于“不能执行”。它们是语法分析里的概念,代表规则是否还能继续展开。讲清这个层次,答案就不会停留在字面。

六、用工程约束检验答案

在文法 Expr -> Expr + Number | Number 中,数字 token 是终结符,Expr 是可继续展开的非终结符。对应 AST 时,Number(3) 是叶子,Add(left,right) 是分支;“终结符”不是指字符串类型,而是语法推导是否继续。

检查项核心判断工程含义
终结符表达式不再包含该文法的子规则Number、Variable
非终结符表达式引用一个或多个子表达式Add、And、Not
树位置叶子与分支的常见映射并非按 Java 基本类型划分

把关键关系压缩成一条可复述的路径:

Add
├─ Number(2)   terminal
└─ Multiply    nonterminal
   ├─ Number(3) └─ Number(4)

要从形式文法解释“终结/非终结”,再映射到叶子与组合节点,避免只背节点位置。

落地前可以再按下面 3 步复核:

  1. 先说明“终结符表达式”的核心机制:不再包含该文法的子规则;再交代边界:Number、Variable。
  2. 接着分析“非终结符表达式”:引用一个或多个子表达式;不能遗漏对应代价或结果:Add、And、Not。
  3. 最后用“树位置”检查方案:叶子与分支的常见映射;验收时确认并非按 Java 基本类型划分。

这三项构成完整判断链:先讲清终结符表达式,再说明非终结符表达式,最后用树位置检验实现是否越界。

面试中若能给出违反“并非按 Java 基本类型划分”的反例,再说明修正办法,答案就从模式定义落到了可验证的工程决策。

七、常见误区与追问

  • 误区:只看到“终结符表达式”就认为方案成立。 必须同时说明核心机制“不再包含该文法的子规则”和工程边界“Number、Variable”。
  • 误区:把“非终结符表达式”当成无条件结论。 只有在“引用一个或多个子表达式”成立时,才能据此讨论“Add、And、Not”。
  • 追问:变量是终结符吗? 在常见表达式文法中 Variable 可作为终结符表达式,值由 Context 提供。
  • 追问:括号要成为 AST 节点吗? 通常括号只影响解析分组,AST 可不保留括号节点。
  • 追问:一元 Not 是非终结符吗? 是,它组合一个子表达式并形成新语义。
  • 追问:终结符一定没有代码依赖吗? 不是,它仍可能读取 Context,只是不再递归解释子表达式。
  • 追问:字面量字符串就是终结符吗? 只有文法把它定义为不可再推导的符号时才是。

八、加强记忆

记忆时抓住这条主线:终结符是语法树叶子节点;非终结符是组合规则节点;复杂表达式由两者递归组合;终结符取值,非终结符组合结果。面试回答先给出模式意图,再用调用链或数据流说明角色协作,最后主动交代适用边界与工程代价。