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 /) and expressions

(+ or -). What it expression

does is a challenge term

to collect its left side and so on. To implement factorial, I added fact()

between term

and primary()

to make the bet more rigid. Before adding the fact()

calculator worked fine. Then I added fact

to take data from primary

and term

now accepts data from fact

.

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.

+3


source to share


1 answer


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;
}

      

+2


source







All Articles