📄 pat5c.htm
字号:
<A NAME="auto1089"></A>
<PRE>
BooleanExp ::= VariableExp | Constant | OrExp | AndExp | NotExp |
'(' BooleanExp ')'
AndExp ::= BooleanExp 'and' BooleanExp
OrExp ::= BooleanExp 'or' BooleanExp
NotExp ::= 'not' BooleanExp
Constant ::= 'true' | 'false'
VariableExp ::= 'A' | 'B' | ... | 'X' | 'Y' | 'Z'
</PRE>
<A NAME="auto1090"></A>
<P>We define two operations on Boolean expressions. The first,
<CODE>Evaluate</CODE>, evaluates a Boolean expression in a context
that assigns a true or false value to each variable. The second
operation, <CODE>Replace</CODE>, produces a new Boolean expression by
replacing a variable with an expression. <CODE>Replace</CODE> shows
how the Interpreter pattern can be used for more than just evaluating
expressions. In this case, it manipulates the expression itself.</P>
<A NAME="variableexp"></A>
<P>We give details of just the <CODE>BooleanExp</CODE>,
<CODE>VariableExp</CODE>, and <CODE>AndExp</CODE> classes here. Classes
<CODE>OrExp</CODE> and <CODE>NotExp</CODE> are similar to <CODE>AndExp</CODE>.
The <CODE>Constant</CODE> class represents the Boolean constants.</P>
<A NAME="auto1091"></A>
<P><CODE>BooleanExp</CODE> defines the interface for all classes that define
a Boolean expression:</P>
<A NAME="auto1092"></A>
<PRE>
class BooleanExp {
public:
BooleanExp();
virtual ~BooleanExp();
virtual bool Evaluate(Context&) = 0;
virtual BooleanExp* Replace(const char*, BooleanExp&) = 0;
virtual BooleanExp* Copy() const = 0;
};
</PRE>
<A NAME="auto1093"></A>
<P>The class <CODE>Context</CODE> defines a mapping from variables to
Boolean values, which we represent with the C++ constants
<CODE>true</CODE> and <CODE>false</CODE>. <CODE>Context</CODE> has the
following interface:</P>
<A NAME="auto1094"></A>
<PRE>
class Context {
public:
bool Lookup(const char*) const;
void Assign(VariableExp*, bool);
};
</PRE>
<A NAME="auto1095"></A>
<P>A <CODE>VariableExp</CODE> represents a named variable:</P>
<A NAME="auto1096"></A>
<PRE>
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;
};
</PRE>
<A NAME="auto1097"></A>
<P>The constructor takes the variable's name as an argument:</P>
<A NAME="auto1098"></A>
<PRE>
VariableExp::VariableExp (const char* name) {
_name = strdup(name);
}
</PRE>
<A NAME="auto1099"></A>
<P>Evaluating a variable returns its value in the current context.</P>
<A NAME="auto1100"></A>
<PRE>
bool VariableExp::Evaluate (Context& aContext) {
return aContext.Lookup(_name);
}
</PRE>
<A NAME="auto1101"></A>
<P>Copying a variable returns a new <CODE>VariableExp</CODE>:</P>
<A NAME="auto1102"></A>
<PRE>
BooleanExp* VariableExp::Copy () const {
return new VariableExp(_name);
}
</PRE>
<A NAME="auto1103"></A>
<P>To replace a variable with an expression, we check to see if the
variable has the same name as the one it is passed as an argument:</P>
<A NAME="auto1104"></A>
<PRE>
BooleanExp* VariableExp::Replace (
const char* name, BooleanExp& exp
) {
if (strcmp(name, _name) == 0) {
return exp.Copy();
} else {
return new VariableExp(_name);
}
}
</PRE>
<A NAME="andexp"></A>
<P>An <CODE>AndExp</CODE> represents an expression made by ANDing two
Boolean expressions together.</P>
<A NAME="auto1105"></A>
<PRE>
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;
}
</PRE>
<A NAME="auto1106"></A>
<P>Evaluating an <CODE>AndExp</CODE> evaluates its operands and returns
the logical "and" of the results.</P>
<A NAME="auto1107"></A>
<PRE>
bool AndExp::Evaluate (Context& aContext) {
return
_operand1->Evaluate(aContext) &&
_operand2->Evaluate(aContext);
}
</PRE>
<A NAME="auto1108"></A>
<P>An <CODE>AndExp</CODE> implements <CODE>Copy</CODE> and <CODE>Replace</CODE> by
making recursive calls on its operands:</P>
<A NAME="auto1109"></A>
<PRE>
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)
);
}
</PRE>
<A NAME="auto1110"></A>
<P>Now we can define the Boolean expression</P>
<A NAME="auto1111"></A>
<PRE>
(true and x) or (y and (not x))
</PRE>
<A NAME="auto1112"></A>
<P>and evaluate it for a given assignment of <CODE>true</CODE> or
<CODE>false</CODE> to the variables <CODE>x</CODE> and <CODE>y</CODE>:</P>
<A NAME="auto1113"></A>
<PRE>
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);
bool result = expression->Evaluate(context);
</PRE>
<A NAME="auto1114"></A>
<P>The expression evaluates to <CODE>true</CODE> for this assignment to
<CODE>x</CODE> and <CODE>y</CODE>. We can evaluate the expression with a
different assignment to the variables simply by changing the
context.</P>
<A NAME="auto1115"></A>
<P>Finally, we can replace the variable <CODE>y</CODE> with a new expression and
then reevaluate it:</P>
<A NAME="auto1116"></A>
<PRE>
VariableExp* z = new VariableExp("Z");
NotExp not_z(z);
BooleanExp* replacement = expression->Replace("Y", not_z);
context.Assign(z, true);
result = replacement->Evaluate(context);
</PRE>
<A NAME="auto1117"></A>
<P>This example illustrates an important point about the Interpreter
pattern: many kinds of operations can "interpret" a sentence. Of
the three operations defined for <CODE>BooleanExp</CODE>,
<CODE>Evaluate</CODE> fits our idea of what an interpreter should do most
closely—that is, it interprets a program or expression and returns a
simple result.</P>
<A NAME="variable-w-interp2"></A>
<P>However, <CODE>Replace</CODE> can be viewed as an interpreter as well.
It's an interpreter whose context is the name of the variable being
replaced along with the expression that replaces it, and whose result
is a new expression. Even <CODE>Copy</CODE> can be thought of as an
interpreter with an empty context. It may seem a little strange to
consider <CODE>Replace</CODE> and <CODE>Copy</CODE> to be interpreters, because
these are just basic operations on trees. The examples in
<A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor (331)</A> illustrate how all three operations can be
refactored into a separate "interpreter" visitor, thus showing that
the similarity is deep.</P>
<A NAME="auto1118"></A>
<P>The Interpreter pattern is more than just an operation distributed
over a class hierarchy that uses the <A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>
pattern. We consider <CODE>Evaluate</CODE> an interpreter because we
think of the <CODE>BooleanExp</CODE> class hierarchy as representing a
language. Given a similar class hierarchy for representing automotive
part assemblies, it's unlikely we'd consider operations like
<CODE>Weight</CODE> and <CODE>Copy</CODE> as interpreters even though they
are distributed over a class hierarchy that uses the Composite
pattern—we just don't think of automotive parts as a language. It's
a matter of perspective; if we started publishing grammars of
automotive parts, then we could consider operations on those parts to
be ways of interpreting the language.</P>
<A NAME="knownuses"><A>
<H2><A HREF="#relatedpatterns"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Known Uses</H2>
<A NAME="smalltalk-use-interp"></A>
<A NAME="spectalk-use-interp"></A>
<P>The Interpreter pattern is widely used in compilers implemented with
object-oriented languages, as the Smalltalk compilers are. SPECTalk
uses the pattern to interpret descriptions of input file
formats [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=szafron_tools92" TARGET="_mainDisplayFrame">Sza92</A>]. The QOCA constraint-solving toolkit
uses it to evaluate constraints [<A HREF="vfs.htm?doc=bib-0.htm&fid=bb&hid=qoca" TARGET="_mainDisplayFrame">HHMV92</A>].</P>
<A NAME="auto1119"></A>
<P>Considered in its most general form (i.e., an operation distributed
over a class hierarchy based on the Composite pattern), nearly every
use of the Composite pattern will also contain the Interpreter
pattern. But the Interpreter pattern should be reserved for those
cases in which you want to think of the class hierarchy as defining a
language.</P>
<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="gifsb/down3.gif" BORDER=0></A> Related Patterns</H2>
<A NAME="auto1120"></A>
<P><A HREF="pat4cfs.htm" TARGET="_mainDisplayFrame">Composite (163)</A>:
The abstract syntax tree is an instance of the Composite pattern.</P>
<A NAME="auto1121"></A>
<P><A HREF="pat4ffs.htm" TARGET="_mainDisplayFrame">Flyweight (195)</A>
shows how to share terminal symbols within the abstract syntax
tree.</P>
<A NAME="auto1122"></A>
<P><A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame">Iterator (257)</A>:
The interpreter can use an Iterator to traverse the structure.</P>
<A NAME="auto1123"></A>
<P><A HREF="pat5kfs.htm" TARGET="_mainDisplayFrame">Visitor (331)</A> can
be used to maintain the behavior in each node in the abstract syntax
tree in one class.</P>
<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="pat5dfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/rightar3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat5dfs.htm"
TARGET="_mainDisplayFrame">Iterator</A><BR>
<A HREF="pat5bfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="gifsb/leftarr3.gif"
ALIGN=TOP BORDER=0></A> <A HREF="pat5bfs.htm"
TARGET="_mainDisplayFrame">Command</A>
</P>
<HR>
<A NAME="footnote1"></A>
<P><SUP>1</SUP>For simplicity, we ignore operator precedence and
assume it's the responsibility of whichever object constructs the
syntax tree.
<A HREF="#fn1"><IMG SRC="gifsb/up3.gif" BORDER=0></A></P>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -