解释器模式练习

解释器模式练习——表达式树、上下文与布尔表达式

按《Python 设计模式——解释器模式》的建议,通过三道练习巩固:① 简单算术表达式树(数字、加、乘、减);② 带上下文的变量表达式;③ 布尔表达式(与、或、非)。每步都有完整可运行代码和验证要点。


练习一:简单算术表达式树(数字、加、乘、减)

目的

体会文法→类句子→树解释→eval 递归:每种运算一种类,表达式是对象组合成的树,求值就是对根调用 eval()

要求

  • 实现:数字(终结符)、加法乘法减法(非终结符)。
  • 手动建一棵树,表示 10 - 2 * 3(即 10 – (2*3) = 4),并调用 eval() 得到 4。

参考答案

from abc import ABC, abstractmethod

class Expr(ABC):
    @abstractmethod
    def eval(self) -> int:
        pass

class Number(Expr):
    def __init__(self, value: int):
        self.value = value

    def eval(self) -> int:
        return self.value

class Add(Expr):
    def __init__(self, left: Expr, right: Expr):
        self.left = left
        self.right = right

    def eval(self) -> int:
        return self.left.eval() + self.right.eval()

class Mul(Expr):
    def __init__(self, left: Expr, right: Expr):
        self.left = left
        self.right = right

    def eval(self) -> int:
        return self.left.eval() * self.right.eval()

class Sub(Expr):
    def __init__(self, left: Expr, right: Expr):
        self.left = left
        self.right = right

    def eval(self) -> int:
        return self.left.eval() - self.right.eval()

# 10 - 2*3 的树:Sub(Number(10), Mul(Number(2), Number(3)))
tree = Sub(Number(10), Mul(Number(2), Number(3)))
print(tree.eval())  # 应为 4

验证要点

  • tree.eval() 输出 4
  • 理解:Sub 的右子先 eval 得到 6,再 10-6=4;乘法优先是由树的结构决定的(乘在下面),不是由 eval 里的顺序决定的。

可选

再建一棵树表示 (1+2) * 3,即 Mul(Add(Number(1), Number(2)), Number(3)),验证 eval() 为 9。


练习二:带上下文的变量表达式

目的

在算术表达式基础上加变量(终结符),解释时从上下文取变量值;体会“上下文在整棵树解释过程中传递”。

要求

  • 实现 Context:支持 set(name, value)get(name)
  • 实现 Variable(终结符):eval(ctx) 时返回 ctx.get(变量名)
  • 修改 Number、Add、Mulevaleval(self, ctx: Context),并在递归时把 ctx 传给子节点。
  • 建树表示 *x 2 + 1,在上下文中设 x=5,求值得到 11**。

参考答案

class Context:
    def __init__(self):
        self.vars = {}

    def set(self, name: str, value: int):
        self.vars[name] = value

    def get(self, name: str) -> int:
        return self.vars.get(name, 0)

class Expr(ABC):
    @abstractmethod
    def eval(self, ctx: Context) -> int:
        pass

class Number(Expr):
    def __init__(self, value: int):
        self.value = value

    def eval(self, ctx: Context) -> int:
        return self.value

class Variable(Expr):
    def __init__(self, name: str):
        self.name = name

    def eval(self, ctx: Context) -> int:
        return ctx.get(self.name)

class Add(Expr):
    def __init__(self, left: Expr, right: Expr):
        self.left = left
        self.right = right

    def eval(self, ctx: Context) -> int:
        return self.left.eval(ctx) + self.right.eval(ctx)

class Mul(Expr):
    def __init__(self, left: Expr, right: Expr):
        self.left = left
        self.right = right

    def eval(self, ctx: Context) -> int:
        return self.left.eval(ctx) * self.right.eval(ctx)

# x * 2 + 1,即 Add(Mul(Variable("x"), Number(2)), Number(1))
ctx = Context()
ctx.set("x", 5)
tree = Add(Mul(Variable("x"), Number(2)), Number(1))
print(tree.eval(ctx))  # 应为 11

验证要点

  • ctx.set("x", 5) 后,tree.eval(ctx)11(5*2+1)。
  • 修改 ctx.set("x", 10),同一棵树应得到 21,说明“句子”是树,“解释”依赖上下文。

练习三:布尔表达式(与、或、非)

目的

用解释器模式表示布尔表达式:终结符为 True/False,非终结符为 And、Or、Not;对根调用 eval() 得到布尔值。

要求

  • 实现 TrueLiteralFalseLiteral(终结符)。
  • 实现 AndOrNot(非终结符,可只做二元 And/Or 和一元 Not)。
  • 建树表示 not (False and True),即 Not(And(FalseLiteral(), TrueLiteral())),求值应为 True

参考答案

class BoolExpr(ABC):
    @abstractmethod
    def eval(self) -> bool:
        pass

class TrueLiteral(BoolExpr):
    def eval(self) -> bool:
        return True

class FalseLiteral(BoolExpr):
    def eval(self) -> bool:
        return False

class And(BoolExpr):
    def __init__(self, left: BoolExpr, right: BoolExpr):
        self.left = left
        self.right = right

    def eval(self) -> bool:
        return self.left.eval() and self.right.eval()

class Or(BoolExpr):
    def __init__(self, left: BoolExpr, right: BoolExpr):
        self.left = left
        self.right = right

    def eval(self) -> bool:
        return self.left.eval() or self.right.eval()

class Not(BoolExpr):
    def __init__(self, expr: BoolExpr):
        self.expr = expr

    def eval(self) -> bool:
        return not self.expr.eval()

# not (False and True)  ->  not(And(False, True))  ->  True
tree = Not(And(FalseLiteral(), TrueLiteral()))
print(tree.eval())  # True

验证要点

  • tree.eval()True(False and True = False,not False = True)。
  • 可再建一棵 (True or False) and Not(False),验证为 True。

三步汇总与自检

练习 重点 关键点
算术表达式树 终结符 Number,非终结符 Add/Mul/Sub;树结构决定运算优先级。
变量 + 上下文 Variable 从 Context 取值;eval 沿树传递 ctx。
布尔表达式 终结符 True/False,非终结符 And/Or/Not;同一套“解释=递归 eval”。

自检问题

  1. 终结符非终结符在解释时有什么区别?
    终结符直接返回值(或从上下文取值),不依赖子表达式;非终结符先对子节点 eval,再按规则组合结果。

  2. 上下文的作用是什么?
    在整棵树的解释过程中传递共享信息(如变量表),供终结符(如 Variable)或非终结符按需读写。

  3. 为什么说“运算优先级由树的结构决定”?
    谁在树的更下层,谁先被递归到;例如 Sub(10, Mul(2,3)) 里乘在下面,先算 2*3,再算 10-6。

做完以上三道练习,再对照文档中的示例,对解释器模式会掌握得比较扎实。建议每道题先自己写一遍,再对照参考答案和验证要点检查。

发表评论