Stroustrup C ++ Learning Exercise
I am learning C ++ from Stroustrup's book: Programming: Principles and Practice Using C ++. I'm in chapter 6 and we are writing a calculator and now:
I need to add a factorial clause that is stronger than
*
and/
. In the original program, we had 3 levels,primary
(floating point letters and parentheses),terms
(* and /) andexpressions
(+ or -). What itexpression
does is a challengeterm
to collect its left side and so on. To implement factorial, I addedfact()
betweenterm
andprimary()
to make the bet more rigid. Before adding thefact()
calculator worked fine. Then I addedfact
to take data fromprimary
andterm
now accepts data fromfact
.
Now for the questions:
Q1: As I said, fact
everything was fine before adding . Now the only correct equation that is derived is term
. When I try to do other arithmetic, it only prints the last number entered. Where I mixed up other operations.
Q2: (off-topic view) Why when I try to exit the program (that is, by typing 'q'
, I need to type three or four times 'q'
until it exits.
Here is the code:
#include "std_lib_facilities.h"
class Token {
public:
char kind; // what kind of token
double value; // for numbers: a value
Token(char ch) // make a Token from a char
:kind(ch), value(0) { }
Token(char ch, double val) // make a Token from a char and a double
:kind(ch), value(val) { }
};
//------------------------------------------------------------------------------
class Token_stream {
public:
Token_stream(); // make a Token_stream that reads from cin
Token get(); // get a Token
void putback(Token t); // put a Token back
private:
bool full; // is there a Token in the buffer
Token buffer; // here is where we keep a Token put back using putback();
};
// Constructor
Token_stream::Token_stream()
:full(false),buffer(0)
{
}
Token_stream ts;
void Token_stream::putback(Token t)
{
if (full) error("putback() into a full buffer"); // checks if we're using putback() twice
buffer = t; // copy t to buffer
full = true; // buffer is now full
}
Token Token_stream::get()
{
if (full) { // do we already have a Token ready?
// remove Token from buffer
full = false;
return buffer;
}
char ch;
cin >> ch; // note that >> skips whitespace
switch(ch) {
case ';': // for "print"
case 'q': // for "quit"
case '(': case ')': case '{': case '}': case '!': case'+': case '-': case'/': case '*':
return Token(ch); // let each character represent itself
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch); // put digit back into the input stream
double val;
cin >> val;
return Token('8',val); // let '8' represent a "number"
break;
}
default:
error("Bad token");
return 0;
}
}
Token get_token() // read a token from cin
{
char ch;
cin >> ch; // note that >> skips whitespace (space, newline, tab, etc.)
switch (ch) {
case 'q':
case ';':
case '(': case ')': case '{': case '}': case '!': case '+': case '-': case '*': case '/':
return Token(ch); // let each character represent itself
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch); // put digit back into the input stream
double val;
cin >> val; // read a floating-point number
return Token('8',val); // let '8' represent "a number"
}
default:
error("Bad token");
return 0;
}
}
//------------------------------------------------------------------------------
double expression(); // read and evaluate a Expression
//------------------------------------------------------------------------------
double term(); // read and evaluate a Term
//------------------------------------------------------------------------------
double primary()
{
Token t = ts.get();
switch (t.kind) {
case '(': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '{': // handle '(' expression ')'
{
double d = expression();
t = ts.get();
if (t.kind != '}') error("'}' expected");
return d;
}
case '8': // we use '8' to represent a number
return t.value; // return the number value
default:
return 0;
error("primary expected");
}
}
//------------------------------------------------------------------------------
int main()
try {
double val = 0;
while(cin)
{
Token t = ts.get();
if(t.kind == 'q') break; //'q' for quit
if(t.kind == ';')
cout << "=" << val << "\n";
else
ts.putback(t);
val = expression();
}
keep_window_open("q");
}
catch (exception& e) {
cerr << e.what() << endl;
keep_window_open ("q");
return 1;
}
catch (...) {
cerr << "exception \n";
keep_window_open ("q");
return 2;
}
//------------------------------------------------------------------------------
double expression()
{
double left = term(); // read and evaluate a Term
Token t = ts.get(); // get the next token
while(true) {
switch(t.kind) {
case '+':
left += term(); // evaluate Term and add
t = ts.get();
break;
case '-':
left -= term(); // evaluate Term and subtract
t = ts.get();
break;
default:
ts.putback(t);
return left; // finally: no more + or -: return the answer
}
}
}
//------------------------------------------------------------------------------
double factorial(double val)
{
double res=1;
for(int i=1; i<=val; i++)
res *= i;
return res;
}
double fact()
{
double left = primary();
Token t = ts.get();
switch(t.kind)
{
case '!':
{ double res = factorial(left);
return res;
}
default:
return left;
}
}
//------------------------------------------------------------------------------
double term()
{
double left = fact();
Token t = ts.get(); // get the next token
while(true) {
switch (t.kind) {
case '*':
left *= fact();
t = ts.get();
break;
case '/':
{
double d = fact();
if (d == 0) error("divide by zero");
left /= d;
t = ts.get();
break;
}
default:
ts.putback(t);
return left;
}
}
}
Sorry if I'm not specific enough. This is my first time posting such a stream.
source to share
Starting from the second question:
You need to push q
multiple times like you use it as an argument in:
keep_window_open("q");
so, every time you exit the loop while
, it waits for you to enter q
to close the console.
As for your factor function:
Please enter your calculator. Grammar: write the order of precedence of existing operations, i.e. the order in which the functions that contain them are called. This will make it easier for you to introduce new features.
It is a good idea to put factorial
as complementary case
in primary()
, since parentheses have a higher order of precedence (called earlier) than multiplication, division, etc. in term()
.
Here's one possible implementation:
double primary(){
Token t = ts.get();
switch (t.kind) {
case '{':{
double d = expression();
t = ts.get();
if (t.kind != '}') error("'}' expected");
return d;
}
case '(': // handles '(' expression ')'{
double d = expression();
t = ts.get();
if (t.kind != ')') error("')' expected");
return d;
}
case '8': case '!':{
// include a test whether the number is integer and > 0
if(is_factorial()){
double d = factorial(t.value);
t = ts.get();
return d;
}
else return t.value;
}
default:
error("primary expected");
}
}
Where:
/*
Non-member method: factorial.
Use: double fact = factorial(double);
This funnction provides factorial operator.
*/
double factorial(double num){
if(num <= 1) return 1;
return num*factorial(num-1);
}
and
/*
Non-member method: is_factorial.
Use: bool fact = is_factorial(void);
This funnction returns true if a number
is followed by factorial opertor.
Used as an indicator to call factorial function.
*/
bool is_factorial(){
Token t = ts.get();
if (t.kind == '!'){
ts.putback(t);
return true;
}
ts.putback(t);
return false;
}
source to share