Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding the ! operator and sqrt(), pow(), etc. to a calculator example application

Tags:

c++

I'm doing the exercises in Stroustrup's new book "Programming Principles and Practice Using C++" and was wondering if anyone on Stack Overflow has done them and is willing to share the knowledge?

Specifically about the calculator that's developed in Chap 6 and 7. For example, the questions about adding the ! operator and sqrt(), pow(), etc. I have done these, but I don't know if the solution I have is the "good" way of doing things, and there are no published solutions on Bjarne's website. I'd like to know if I am going down the right track. Maybe we can make a wiki for the exercises?

Basically I have a token parser. It reads a char at a time from cin. It's meant to tokenise expressions like 5*3+1 and it works great for that. One of the exercises is to add a sqrt() function. So I modified the tokenising code to detect "sqrt(" and then return a Token object representing sqrt. In this case I use the char 's'. Is this how others would do it? What if I need to implement sin()? The case statement would get messy.

char ch;
cin >> ch;    // Note that >> skips whitespace (space, newline, tab, etc.)

switch (ch) {
    case ';':    // For "print"
    case 'q':    // For "quit"
    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"
        }
    case 's':
        {
            char q, r, t, br;
            cin >> q >> r >> t >> br;
            if (q == 'q' && r == 'r' && t == 't' && br == '(') {
                cin.putback('(');    // Put back the bracket
                return Token('s');   // Let 's' represent sqrt
            }
        }

    default:
        error("Bad token");
}
like image 509
PowerApp101 Avatar asked May 16 '09 00:05

PowerApp101


3 Answers

  1. There are a few solutions posted on Stroustrup - Programming and more will be coming over time.

  2. Try solving exercises only with the language features and the library facilities presented so far in the book -- real novice users can't do anything else. Then return later to see how a solution can be improved.

like image 68
Bjarne Stroustrup Avatar answered Nov 09 '22 22:11

Bjarne Stroustrup


I thought a map of strings to function pointers might be a concise way to represent things like sqrt, sin, cos, etc. that take a single double and return a double:

map<std::string, double (*)(double)> funcs;
funcs["sqrt"] = &sqrt;
funcs["sin"] = &sin;
funcs["cos"] = &cos;

Then when the parser detects a correct string (str) it can call the function with an argument (arg) like so:

double result = funcs[str](arg);

With this method, a single call can handle all cases of functions (of that type).

Actually I'm not sure if that's the correct syntax, can anyone confirm?

Does this seem like a usable method?

like image 10
PowerApp101 Avatar answered Nov 09 '22 23:11

PowerApp101


It is easier to work with derived classes and virtual functions: each specialized class reading its own input...

class base {
    public:
        virtual double calc() = 0;
};

class get_sqrt : public base {
    int useless;

    public:
        virtual double calc() {
            cin >> number;
            return sqrt(number);
        }
}

get_sqrt;

Now we organize these in a map, and we will only use their pointers:

map<string,base*> func;
func["sqrt"] = &get_sqrt;

There is also a specialized method which only looks at the next character: peek();

char c = cin.peek();

You can get rid of the switch by using 1 if putting !, +, -, etc. in func; (they should operate on left_param for simplicity):

 if (c>='0' && c<='9')
     cin >> right_param; // Get a number, you don't have to put the
                         // character back as it hasn't been removed
 else {
     string s;
     cin >> s;
     right_param = func[s]->calc();
 }

So basically some kind of function pointers, but without the messy syntax and in which you could store data between calculations.

I thought about the whitespace problem; it can be added before it starts to compute, I also think there could be a way to set different separators, like numbers, but I don't know how.

like image 4
csiz Avatar answered Nov 10 '22 00:11

csiz