PLACE |
spec |
solutions |
C++ |
As C++ is an extension to C, when we consider an implementation in this language, it is natural to be interested in how we can do better than in plain C. As it turns out, the main advantage of C++ over C in solving our problem is reading and tokenizing the input. C++'s standard library is particularly convenient in this respect.
In the main
function, the outer loop calls getline
on cin
to read lines into the variable s
. The return value of getline
is used to determine when the loop and the program are to be terminated due to exhausting the input. cin
is a library-provided stream object associated with the standard input, and s
is of class string
. In contrast to the C implementation, lines of arbitrary length are read by a single function call, and the buffer needed to accomodate the data is automatically and transparently allocated.
For tokenizing the input, we create a ‘string stream’ sstr
on s
, so as, in the inner loop, individual tokens are extracted using the standard input operator >>
. All tokens go into the stack tks
, from where, unless the input line contained no tokens at all, they are passed to evalrpn
, which evaluates the RPN expression by recursively working downwards on tks
.
Within evalrpn
, a token tk
is popped off tks
and checked for being an operator or a number. If an operator, we are also interested in whether tks
contains the respective operands, pull them off the stack, and perform the corresponding arithmetical operation. If tk
is not an operator, stod
is used to read it as a number. In any case, the result goes into x
, and is eventually returned as the value of evalrpn
.
Syntax errors are signalled by throw
ing exceptions. For convenience, the test for an erroneous situation and the throwing itself are packed in a macro definition. As we do not want to differentiate between the various reasons that may cause a syntax error, what exactly value is being thrown is irrelevant. The call of evalrpn
in main
is nested in a try
block. A throw
occurs either directly in that block or within evalrpn
, so all throws are handled by the single catch
statement following the try
block. An implicit throw takes place within stod
whenever it fails to read tk
as a number. The ellipsis in the handler stands for a ‘don't care’ parameter, meaning that it can accept any possible value thrown but is not actually using it.
For comparison, the C implementation handles errors by the setjmp
/longjmp
mechanism, which is less convenient to write or read, but also significantly less time-consuming than the throw
/catch
exception handling mechanism of C++ (although the latter does not matter here).
#include <iostream> #include <sstream> #include <string> #include <stack> using namespace std; #define ASSERT(b) if (!(b)) throw "" double evalrpn(stack<string> & tks) { ASSERT(!tks.empty()); double x,y; auto tk = tks.top(); tks.pop(); auto n = tk.size(); if (n==1 && string("+-*/").find(tk)!=string::npos) { y = evalrpn(tks); x = evalrpn(tks); if (tk[0]=='+') x += y; else if (tk[0]=='-') x -= y; else if (tk[0]=='*') x *= y; else x /= y; } else { unsigned i; x = stod(tk,&i); ASSERT(i==n); } return x; } int main() { string s; while (getline(cin,s)) { stack<string> tks; istringstream sstr(s); string tk; while (sstr >> tk) tks.push(tk); if (!tks.empty()) try { auto z = evalrpn(tks); ASSERT(tks.empty()); cout << z << endl; } catch (...) {cout << "error\n";} } return 0; }
boykobbatgmaildotcom