解释器模式中终结符表达式和非终结符表达式有什么区别?
简化版
终结符表达式表示语法中不可再拆分的基本单元,如变量、数字、常量;非终结符表达式表示由其他表达式组合出来的语法规则,如加法、比较、与或逻辑。终结符是叶子节点,非终结符是中间节点。
详细版
终结符表达式:
- 语法树叶子节点。
- 不再包含子表达式。
- 通常从 Context 中取值或返回常量。
- 例子:变量
age、数字18、布尔值true。
非终结符表达式:
- 语法树中间节点。
- 包含一个或多个子表达式。
- 组合子表达式结果。
- 例子:
age > 18、A AND B、x + y。
解释器模式通过这两类表达式组合出完整语法树。
完整版教学
一、终结符为什么叫终结
终结符来自形式语言概念,意思是语法推导到这里就结束了,不能再拆。
在表达式树中,它就是叶子节点。比如数字 10 不需要再解释成更小的表达式,变量 age 也通常只是从上下文取一个值。
二、非终结符负责组合
非终结符表达式代表一种组合规则。比如 AndExpression 由左右两个布尔表达式组成:
return left.interpret(context) && right.interpret(context);
它自己不直接给出结果,而是依赖子表达式结果再组合。
三、复杂表达式如何形成树
例如:
age > 18 AND vip = true
可以拆成:
age和18是终结符。age > 18是非终结符。vip = true是非终结符。- 两个条件再组成 AND 非终结符。
最终根节点解释结果就是整个表达式结果。
四、常见误区与工程判断
不要把所有逻辑都塞进一个 TerminalExpression。终结符应该尽量简单,复杂组合逻辑应该放到非终结符里。
工程中如果发现某个表达式类既解析字符串、又查变量、又做组合判断,职责已经混乱。表达式节点应该只负责自己的语法语义。
五、终结符和非终结符对应语法树的叶子与分支
理解终结符和非终结符,可以把表达式树想成一棵真实的树。终结符表达式通常是叶子节点,不能继续拆,比如变量 age、常量 true、数字 10。非终结符表达式通常是分支节点,内部还包含其他表达式,比如 A && B、A || 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 步复核:
- 先说明“终结符表达式”的核心机制:不再包含该文法的子规则;再交代边界:Number、Variable。
- 接着分析“非终结符表达式”:引用一个或多个子表达式;不能遗漏对应代价或结果:Add、And、Not。
- 最后用“树位置”检查方案:叶子与分支的常见映射;验收时确认并非按 Java 基本类型划分。
这三项构成完整判断链:先讲清终结符表达式,再说明非终结符表达式,最后用树位置检验实现是否越界。
面试中若能给出违反“并非按 Java 基本类型划分”的反例,再说明修正办法,答案就从模式定义落到了可验证的工程决策。
七、常见误区与追问
- 误区:只看到“终结符表达式”就认为方案成立。 必须同时说明核心机制“不再包含该文法的子规则”和工程边界“Number、Variable”。
- 误区:把“非终结符表达式”当成无条件结论。 只有在“引用一个或多个子表达式”成立时,才能据此讨论“Add、And、Not”。
- 追问:变量是终结符吗? 在常见表达式文法中 Variable 可作为终结符表达式,值由 Context 提供。
- 追问:括号要成为 AST 节点吗? 通常括号只影响解析分组,AST 可不保留括号节点。
- 追问:一元 Not 是非终结符吗? 是,它组合一个子表达式并形成新语义。
- 追问:终结符一定没有代码依赖吗? 不是,它仍可能读取 Context,只是不再递归解释子表达式。
- 追问:字面量字符串就是终结符吗? 只有文法把它定义为不可再推导的符号时才是。
八、加强记忆
记忆时抓住这条主线:终结符是语法树叶子节点;非终结符是组合规则节点;复杂表达式由两者递归组合;终结符取值,非终结符组合结果。面试回答先给出模式意图,再用调用链或数据流说明角色协作,最后主动交代适用边界与工程代价。