You are on page 1of 4

Data Structures and Analysis of Algorithms

EECE 330
Lab 3 – Fall 2020

In this lab assignment we will continue our C++ familiarity exercises. We will build a simple
expression calculator using two different methods to appreciate the contribution of each of the
methods.

Guidelines
ˆ This programming assignment consists of 3 problems.

ˆ It is due on Monday, October 5, 2020 11:55 pm.

ˆ Topics: Arrays, classes, inheritance, functions.

ˆ Readings: Stroustrup book chapters 9 and 14.3

? You are supposed to submit your own work. We have a zero tolerance policy for cheating.
Penalties may include one or more of the following: zero grades on programming assignments,
failing the course, disciplinary committee, Dean’s warning, and suspension .
? Lab attendance is mandatory. Violating this rule can lead to a failing grade.

Problem 1. Expression calculator using classes


Let V = {v1 , v2 , . . . , vn } be a set of variables that take values from Z. An expression is defined as
follows.
ˆ v is an expression where v ∈ V (e.g., if V = {x, y, z}, then x is an expression).

ˆ (◦e) is an expression where e is an expression, and ◦ ∈ {−, !} is a unary operation denoting


either negation or logical not (e.g., −y is an expression).
ˆ (e1 ◦ e2 ) is an expression where e1 and e2 are expressions and ◦ ∈ {+, −, ∗, /, %, <, ≤, >, ≥
, ==, and, or} is a binary operation with the typical semantics (e.g., x + y, y/z, (x > y),
!((x + y) > (y/z)) are examples of expressions).
Consequently, an expression has a name, a value, and a type (variable, unary-op, or binary-op).
Based on its type, an expression may have zero, one, or two operands who in turn are expressions.
For example, x is an expression, its type is “variable”, its name is “x”, and its value is the value of
x (in Z); (x + y) is an expression, its type is “binary operation”, its name is “ + ”, and its value is
the sum of the values of x and y.
In this problem, we will represent expressions, print them and evaluate them using an user-
defined class expression. (Then we will do the same using inheritance and overloading (polymorphism)
in the next problem.)
Optionally, one could implement them using five arrays as shown in Figure 1: an array of
expression names, an array of expression types, an array of expression values, and two arrays of
expression operands.

1
indices 0 1 2 3 4 5 6 7 8 9 10 11
types VAR VAR VAR UN BIN BIN BIN UN BIN BIN UN

names "x" "y" "z" "−" "*" "+" "/" "−" "/" ">" "!"

operand1 −1 −1 −1 0 1 0 5 6 1 7 9

operand2 −1 −1 −1 −1 3 2 4 −1 2 8 −1

values

Figure 1: Array representation of expressions. Entries at index 10 in the arrays specify expression
!(−((x + z)/(y ∗ (−x))) > (y/z)).

In the above example, the expression at index 10 is built is as follows: it is a unary operator
(as indicated by types[10]), its name is “!” (as indicated by names[10])(i.e., the logical not). It has
one operand (since it is unary), which is the expression at index 9 (as indicated by operand1[10]).
That is, Expression[10] = !(Expression [9]). In turn, Expression[9] has the binary operator >,
and two operands, Expression[7] and Expression[8]. Hence, Expression[10] = ! (Expression[7] >
Expression[8]). Expression[8] has the binary operator >, with operand1 being Expression[1] (which
is the variable y) and operand2 being Expression[2] (which is the variable z). Hence, Expression[10]
= !(Expression[7] > (y/z)), and so on.

Instead of having several arrays (as in the figure), we will define a user defined structure called
Expression and have only one array of expressions. Structure Expression has a name, a type, two
operands, and a value. The types VAR, UN, BIN will be defined using an enumerated user defined
type.
The class will also have methods print, setVarValue, and computeValue.
print takes two arguments, an array of expressions, and an integer indicating its size. In the
example code below (which is similar to the arrays in Figure 1), expressions[4].print(expressions,11)
prints (−x), expressions[8].print(expressions,11) prints (y/z), etc.
setVarValue takes an integer value val. First, it checks if the expression (that called setVarValue)
is indeed a variable. If so, it sets its value to val. Otherwise, it prints an error message and returns.
computeValue has two arguments, an array of expressions, and an integer indicating its size. If
the expression is a variable, the function returns its value. If the expression is of type unary, it will
call computeValue with the expression at index operand1 in the array of expressions. Similarly, if
the expression is of type binary. The function takes the return values and applies its own operation
on them, and returns the result.

Expression expressions[capacity] = {
{"x",VAR,-1,-1,0},
{"y",VAR,-1,-1,0},
{"z",VAR,-1,-1,0},
{"-",UN,0,-1,0},
{"*",BIN,1,3,0},
{"+",BIN,0,2,0},

2
{"/",BIN,5,4,0},
{"-",UN,6,-1,0},
{"/",BIN,1,2,0},
{">",BIN,7,8,0},
{"!",UN,9,-1,0}
};
expressions[10].print(expressions,11);
expressions[0].setVarValue(5);
expressions[1].setVarValue(5);
expressions[2].setVarValue(5);
int val=expressions[10].computeValue( expressions,11);

cout << "Value is: " << val << endl;


return 0;
}

Note that the initialization shown in the code above using {· · ·} is called brace-list initialization.
This works without explicitly defining a constructor for your class.

Problem 2. Expression class hierarchy


In this problem we won’t be needing arrays.
Instead, we will have a base class Expression. Class Variable has a name and a value and
inherits from Expression. Classes Unary and Binary inherit from Expression. Class Unary has
one operand that is a reference to an expression object. Class Binary has two operands that are
references to an expression objects. Classes UMinus and UNegation inherit from class Unary. Classes
Plus, Minus, Mult, Div, LThan, Equals, GThan, LThanEq, and GThanEq inherit from class Binary.
Class Expression has abstract (pure virtual) print, setVarValue and computeValue methods.
Classes Variable, UMinus, UNegation, Plus, Minus, ..., and GThanEq implement these methods
adequately. Note that each method in those classes only cares about its own logic.
Implement the same scenarios from Problem 2.
Instantiation and printing code should look as simple and readable as the following.

Variable x("x");
Variable y("y");
Variable z("z");
UMinus ux(x);
Mult m(y,ux);
Plus p(x,z);
Div d(p,m);
UMinus ud(d);
Div d2(y,z);
Bigger b(ud,d2);
UNegation ub(b);

ub.print();
x.setVarValue(5);

3
y.setVarValue(5);
z.setVarValue(5);

int val = ub.computeValue();

cout << "Value is: " << val << endl;


return 0;

Problem 3. Construct random expressions


Instead of taking values from the user as in Problem 1, use functions rand and srand to construct
random expressions with up to five variables and up to 32 expressions.
Evaluate your constructed expressions via setting the variables to random values as well.

You might also like