Here are two example expression trees:
+ *
/ \ / \
Y * + 5
/ \ / \
+ 5 Y +
/ \ / \
X Z X Z
Leaves of an expression tree refer to operands (variables or numbers)
The other nodes describe operations, where the children are operands.
The structure of the tree very much affects the order of operations
(e.g. the order of the addition and multiplication operations above)
Compilers can use expression trees to improve usage of the finite storage with the Central Processing Unit (registers)
e.g. the tree on the left could have these instruction sequences:
Load Y into a register Load X into a register
Load X into a register Load Z into a register
Load Z into a register Add the values of X and Z
Add the values of X and Z Multiply the sum by 5
Multiply the sum by 5 Load Y into a register
Add Y to the product Add Y to the product
Requires 3 machine registers Requires 2 machine registers
The most common purpose for a complete traversal is to display the tree contents.
For Binary Search Trees, the most likely traversals are inorder traversals,
in which a node value is visited between its children.
There can also pre-order traversals (visit before)
and post-order traversals (visit after).
Traversal is much more interesting for expression trees:
+
/ \ pre-order + Y * + X Z 5
Y *
/ \ post-order Y X Z + 5 * +
+ 5
/ \ in-order Y + X + Z * 5 (imperfect)
X Z
An implementation for an in-order traversal must insert parentheses:
( Y + ( ( X + Z ) * 5 )Once the expression tree is constructed, it can be used to display expressions in any format we desired, as well as giving information on how to evaluate the expression.
The expression tree can also be built by any parsing routine that can understand any of the three expressions.
A couple simple observations regarding expression evaluation:
--- a node that performs addition does not care what its operands are
--- not all leaf nodes are treated in the same way
These two observations should strongly suggest a good use for derived classes.
Consider an abstract class representing an expression tree node,
and derived classes for each different kind of subexpression:
constant value, variable, arithmetic computation, assignment
And allowing derived classes means we are not limited to a binary tree.
X = ( Y > 0 ? Y : -Y ) - has one child; ?: has 3 children
class ExprNode
{
public:
virtual string toString() const = 0; // facilitates << operator
virtual int evaluate( VarTree &v ) const = 0; // evaluate this node
};
class Value: public ExprNode
{
private:
int value;
public:
string toString() const; // facilitates << operator
int evaluate( VarTree &v ) const;
Value(int v)
{
value = v;
}
};
string Value::toString() const
{
ostringstream convert; // output string stream
convert << value; // 'output' into the stream
return convert.str(); // and extract its string equivalent
}
int Value::evaluate( VarTree &v ) const
{
return value;
}
class Variable: public ExprNode
{
private:
string name;
public:
string toString() const ; // facilitates << operator
int evaluate( VarTree &v ) const;
Variable(string var)
{
name = var;
}
};
string Variable::toString() const
{
return name;
}
int Variable::evaluate( VarTree &v ) const
{
return v.lookup( name );
}