行为型模式--解释器

1、意图

  给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

2、结构

 

 

 3、参与者

  AbstractExpression:声明一个抽象的解释操作,这个接口为抽象语法树中所有节点所共享。

  TerminalExpression:实现与文法中的终结符相关联的解释操作;一个句子中的每个终结符需要该类的一个实例。

  NonterminalExpression:对文法中的每一条规则R ::= R1R2...Rn都需要一个 NonterminalExpression类;为从R1到Rn的每个符合都维护一个AbstractExpression类型的实例变量;为文法中的非终结符实现解释(Interpret)操作。解释一般要递归地调用表示R1到Rn的那些对象的解释操作。

  Context:包含解释器之外的一些全局信息。

  Client:构建(或被给定)表示该文法定义的语言中的一个特定的句子的抽象语法树,该抽象语法树由NonterminalExpression和TerminalExpression的实例装配而成;调用解释操作。

4、适用性

  当有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时,可使用解释器模式。而当存在以下情况时该模式效果最好:

  该文法简单。对于复杂的文法,文法的类层次变得庞大而无法管理,不适合用解释器模式,此时语法分析程序生成器这样的工具是更好的选择。它们无需构建抽象语法树即可解释表达式,这样可以节省空间还可能节省时间;

  不要求效率。最高效的解释器通常不是通过直接解释语法分析树实现的,而是首先将它们转换成另一种形式。例如,正则表达式通常被转换成状态机。但即使在这种情况下,转换器仍可用解释器模式实现,该模式仍是有用的。

5、代码示例

  场景:对布尔表达式进行操作和求值。在这个语言中终结符是布尔变量,即常量true和false。非终结符表示包含运算符and,or和not的布尔表达式。文法定义如下:

AndExp ::= BooleanExp 'and' BooleanExp 
OrExp ::= BooleanExp 'or' BooleanExp 
NotExp ::= 'not' BooleanExp 
Constant ::= 'true' | 'false'
VariableExp := 'A' | 'B' | ... | 'X' | 'Y' | 'Z'

  代码如下:

// BooleanExp为所有定义一个布尔表达式的类定义了一个接口
class BooleanExp 
{
public:
    BooleanExp(); 
    virtual ~BooleanExp(); 
    virtual bool Evaluate(Context&)= 0; 
    virtual BooleanExp* Replace(const char*,BooleanExp&)=0; 
    virtual BooleanExp* Copy()const =0;
};

// 类Context定义从变量到布尔值的一个映射,这些布尔值我们可用C++中的常量true和false来表示。Context有以下接口
class Context
{
public:
    bool Lookup(const char*) const; 
    void Assign(VariableExp*, bool);
};
// 一个VariableExp表示一个有名变量
class VariableExp : public BooleanExp
{
public:
    VariableExp(const char*);
    virtual ~VariableExp(); 
    virtual bool Evaluate(Context&); 
    virtual BooleanExp* Replace(const char*,BooleanExp&); 
    virtual BooleanExp* Copy()const; 
private:
    char* _name;
};

// 构造器将变量的名字作为参数
VariableExp::VariableExp (const char* name)
{
    _name = strdup(name);
}
// 求一个变量的值,返回它在当前上下文中的值。
bool VariableExp::Evaluate (Context& aContext)
{
    return aContext.Lookup(_name);
}

// 拷贝一个变量返回一个新的VariableExp
BooleanExp* VariableExp::Copy ()const 
{
    return new VariableExp(_name);
}
// 在用一个表达式替换一个变量时,我们检查该待替换变量是否就是本对象代表的变量
BooleanExp* VariableExp::Replace( const char* name, BooleanExp& exp)
{
    if(strcmp(name, _name) == 0)
    {
        return exp.Copy(); 
    }
    else
    {    
        return new VariableExp(_name);
    }
}
// AndExp表示由两个布尔表达式与操作得到的表达式
class AndExp : public BooleanExp
{
public:
    AndExp(BooleanExp*, BooleanExp*); 
    virtual ~AndExp(); 
    virtual bool Evaluate(Context&);
    virtual BooleanExp* Replace(const char*, BooleanExp&);
    virtual BooleanExp* Copy() const; 
private:
    BooleanExp* _operand1; 
    BooleanExp* _operand2;
}:
AndExp::AndExp (BooleanExp* op1, BooleanExp*op2)
{
    _operand1 op1;
    _operand2 op2;
}

// 一个AndExp的值求是它的操作数的值的逻辑“与”。
bool AndExp::Evaluate (Context& acontext)
{
    return _operandl->Evaluate(acontext)&&
        _operand2->Evaluate(aContext); 
}
// AndExp的Copy和Replace操作将递归调用它的操作数的Copy和Replace操作
BooleanExp* AndExp::Copy () const
{
    return new AndExp(_operand1->Copy(), _operand2->Copy ());
}

BooleanExp* AndExp::Replace (const char* name, BooleanExp& exp)
{
    return new AndExp(_operand1->Replace(name, exp), _operand2->Replace(name, exp));
}
// 现在我们可以定义布尔表达式
//(true and x) or (y and (not x))
// 并对给定的以true或false赋值的x和y求这个表达式值:
BooleanExp* expression;
Context context;
VariableExp* x = new VariableExp("X"); 
VariableExp* y = new VariableExp("Y"); 
expression = new OrExp(
    new AndExp(new Constant(true), x), 
    new AndExp(y, new NotExp(x))
);
context.Assign(x, false); 
context.Assign(y, true); 
// 对x和y的这一赋值,求得该表达式值为true。
bool result = expression->Evaluate(context);
// 要对其它赋值情况求该表达式的值仅需改变上下文对象即可。
// 最后,我们可用一个新的表达式替换变量y,并重新求值:
VariableExp* z = new VariableExp(Z"); 
NotExp not_z(z); 

BooleanExp* replacement = expression->Replace("Y", not_z); 

context.Assign(z, true); 
result = replacement->Evaluate(context);

6、总结

  解释器模式主要用在文法分析场景,且针对的是简单的文法规则,对于复杂的文法则难以维护。在解释器中,每个语法规则至少要定义一个类,如果规则太多,类的个数也会急剧增加,导致系统难以管理维护,此时可以用语法分析程序代替解释器模式;

  解释器模式执行效率低,通常在解释器中需要使用大量的循环和递归调用来解析复杂的句子。

posted @ 2022-05-03 23:41  流翎  阅读(29)  评论(0编辑  收藏  举报