Polish notation interpreter/calculator
Just for the heck of it, I wrote a simple little interpreter that takes in inputs in the form of Polish Notation.
The user will input something like
+ 5 6
And it will then output
11
Here's another sample input:
+ 5 * 2 3
And the output:
11
Users can even define their own variables, like this:
def myVar 10
and like this:
def anotherVar + 5 6
And then of course reuse those variables. So let's say you type
myVar
it will ouput
10
or
anotherVar
which outputs
11
Furthermore, you type in
+ myVar anotherVar
you would then get
21
I'm a total newbie to well-formatted and readable code. What would be a good way to refactor this?
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
class Variable
{
public:
Variable(const string& name, double val)
{
this->name = name;
this->val = val;
}
inline const string& get_name() const { return name; }
inline double get_val() const { return val; }
private:
string name;
double val;
};
//----------------------------------
double operate(const string& op, istringstream& iss, vector<Variable>& v);
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
void define_new_var(vector<Variable>& v, istringstream& iss);
bool is_number(const string& op)
{
int char_to_int = op[0];
if (char_to_int >= 48 && char_to_int <= 57)
return true;
return false;
}
void print_var_list(vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
cout << v[i].get_name() << ", " << v[i].get_val() << endl;
}
cout << endl;
}
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
print_var_list(v);
else
cout << endl << operate(op, iss, v) << endl;
}
}
double perform_addition(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) + operate(right, iss, v);
}
double perform_subtraction(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) - operate(right, iss, v);
}
double perform_division(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) / operate(right, iss, v);
}
double perform_multiplication(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) * operate(right, iss, v);
}
double get_variable(const string& op, vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].get_name())
return v[i].get_val();
}
}
double operate(const string& op, istringstream& iss, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_addition(iss, v);
else if (op == "-")
value = perform_subtraction(iss, v);
else if (op == "/")
value = perform_division(iss, v);
else if(op == "*")
value = perform_multiplication(iss, v);
else if (is_number(op))
value = atof(op.c_str());
else
value = get_variable(op, v);
return value;
}
void define_new_var(vector<Variable>& v, istringstream& iss)
{
string name;
iss >> name;
string temp;
iss >> temp;
double value = operate(temp, iss, v);
v.push_back(Variable(name, value));
}
c++ calculator math-expression-eval
add a comment |
Just for the heck of it, I wrote a simple little interpreter that takes in inputs in the form of Polish Notation.
The user will input something like
+ 5 6
And it will then output
11
Here's another sample input:
+ 5 * 2 3
And the output:
11
Users can even define their own variables, like this:
def myVar 10
and like this:
def anotherVar + 5 6
And then of course reuse those variables. So let's say you type
myVar
it will ouput
10
or
anotherVar
which outputs
11
Furthermore, you type in
+ myVar anotherVar
you would then get
21
I'm a total newbie to well-formatted and readable code. What would be a good way to refactor this?
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
class Variable
{
public:
Variable(const string& name, double val)
{
this->name = name;
this->val = val;
}
inline const string& get_name() const { return name; }
inline double get_val() const { return val; }
private:
string name;
double val;
};
//----------------------------------
double operate(const string& op, istringstream& iss, vector<Variable>& v);
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
void define_new_var(vector<Variable>& v, istringstream& iss);
bool is_number(const string& op)
{
int char_to_int = op[0];
if (char_to_int >= 48 && char_to_int <= 57)
return true;
return false;
}
void print_var_list(vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
cout << v[i].get_name() << ", " << v[i].get_val() << endl;
}
cout << endl;
}
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
print_var_list(v);
else
cout << endl << operate(op, iss, v) << endl;
}
}
double perform_addition(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) + operate(right, iss, v);
}
double perform_subtraction(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) - operate(right, iss, v);
}
double perform_division(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) / operate(right, iss, v);
}
double perform_multiplication(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) * operate(right, iss, v);
}
double get_variable(const string& op, vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].get_name())
return v[i].get_val();
}
}
double operate(const string& op, istringstream& iss, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_addition(iss, v);
else if (op == "-")
value = perform_subtraction(iss, v);
else if (op == "/")
value = perform_division(iss, v);
else if(op == "*")
value = perform_multiplication(iss, v);
else if (is_number(op))
value = atof(op.c_str());
else
value = get_variable(op, v);
return value;
}
void define_new_var(vector<Variable>& v, istringstream& iss)
{
string name;
iss >> name;
string temp;
iss >> temp;
double value = operate(temp, iss, v);
v.push_back(Variable(name, value));
}
c++ calculator math-expression-eval
You've described Polish notation, not Reverse Polish notation.
– 200_success
Jan 26 '14 at 20:50
add a comment |
Just for the heck of it, I wrote a simple little interpreter that takes in inputs in the form of Polish Notation.
The user will input something like
+ 5 6
And it will then output
11
Here's another sample input:
+ 5 * 2 3
And the output:
11
Users can even define their own variables, like this:
def myVar 10
and like this:
def anotherVar + 5 6
And then of course reuse those variables. So let's say you type
myVar
it will ouput
10
or
anotherVar
which outputs
11
Furthermore, you type in
+ myVar anotherVar
you would then get
21
I'm a total newbie to well-formatted and readable code. What would be a good way to refactor this?
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
class Variable
{
public:
Variable(const string& name, double val)
{
this->name = name;
this->val = val;
}
inline const string& get_name() const { return name; }
inline double get_val() const { return val; }
private:
string name;
double val;
};
//----------------------------------
double operate(const string& op, istringstream& iss, vector<Variable>& v);
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
void define_new_var(vector<Variable>& v, istringstream& iss);
bool is_number(const string& op)
{
int char_to_int = op[0];
if (char_to_int >= 48 && char_to_int <= 57)
return true;
return false;
}
void print_var_list(vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
cout << v[i].get_name() << ", " << v[i].get_val() << endl;
}
cout << endl;
}
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
print_var_list(v);
else
cout << endl << operate(op, iss, v) << endl;
}
}
double perform_addition(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) + operate(right, iss, v);
}
double perform_subtraction(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) - operate(right, iss, v);
}
double perform_division(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) / operate(right, iss, v);
}
double perform_multiplication(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) * operate(right, iss, v);
}
double get_variable(const string& op, vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].get_name())
return v[i].get_val();
}
}
double operate(const string& op, istringstream& iss, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_addition(iss, v);
else if (op == "-")
value = perform_subtraction(iss, v);
else if (op == "/")
value = perform_division(iss, v);
else if(op == "*")
value = perform_multiplication(iss, v);
else if (is_number(op))
value = atof(op.c_str());
else
value = get_variable(op, v);
return value;
}
void define_new_var(vector<Variable>& v, istringstream& iss)
{
string name;
iss >> name;
string temp;
iss >> temp;
double value = operate(temp, iss, v);
v.push_back(Variable(name, value));
}
c++ calculator math-expression-eval
Just for the heck of it, I wrote a simple little interpreter that takes in inputs in the form of Polish Notation.
The user will input something like
+ 5 6
And it will then output
11
Here's another sample input:
+ 5 * 2 3
And the output:
11
Users can even define their own variables, like this:
def myVar 10
and like this:
def anotherVar + 5 6
And then of course reuse those variables. So let's say you type
myVar
it will ouput
10
or
anotherVar
which outputs
11
Furthermore, you type in
+ myVar anotherVar
you would then get
21
I'm a total newbie to well-formatted and readable code. What would be a good way to refactor this?
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
using namespace std;
class Variable
{
public:
Variable(const string& name, double val)
{
this->name = name;
this->val = val;
}
inline const string& get_name() const { return name; }
inline double get_val() const { return val; }
private:
string name;
double val;
};
//----------------------------------
double operate(const string& op, istringstream& iss, vector<Variable>& v);
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
void define_new_var(vector<Variable>& v, istringstream& iss);
bool is_number(const string& op)
{
int char_to_int = op[0];
if (char_to_int >= 48 && char_to_int <= 57)
return true;
return false;
}
void print_var_list(vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
cout << v[i].get_name() << ", " << v[i].get_val() << endl;
}
cout << endl;
}
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
print_var_list(v);
else
cout << endl << operate(op, iss, v) << endl;
}
}
double perform_addition(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) + operate(right, iss, v);
}
double perform_subtraction(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) - operate(right, iss, v);
}
double perform_division(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) / operate(right, iss, v);
}
double perform_multiplication(istringstream& iss, vector<Variable>& v)
{
string left;
iss >> left;
string right;
iss >> right;
return operate(left, iss, v) * operate(right, iss, v);
}
double get_variable(const string& op, vector<Variable>& v)
{
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].get_name())
return v[i].get_val();
}
}
double operate(const string& op, istringstream& iss, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_addition(iss, v);
else if (op == "-")
value = perform_subtraction(iss, v);
else if (op == "/")
value = perform_division(iss, v);
else if(op == "*")
value = perform_multiplication(iss, v);
else if (is_number(op))
value = atof(op.c_str());
else
value = get_variable(op, v);
return value;
}
void define_new_var(vector<Variable>& v, istringstream& iss)
{
string name;
iss >> name;
string temp;
iss >> temp;
double value = operate(temp, iss, v);
v.push_back(Variable(name, value));
}
c++ calculator math-expression-eval
c++ calculator math-expression-eval
edited Aug 6 '15 at 0:08
Ethan Bierlein
12.8k242136
12.8k242136
asked May 3 '11 at 17:30
skizeey
339129
339129
You've described Polish notation, not Reverse Polish notation.
– 200_success
Jan 26 '14 at 20:50
add a comment |
You've described Polish notation, not Reverse Polish notation.
– 200_success
Jan 26 '14 at 20:50
You've described Polish notation, not Reverse Polish notation.
– 200_success
Jan 26 '14 at 20:50
You've described Polish notation, not Reverse Polish notation.
– 200_success
Jan 26 '14 at 20:50
add a comment |
5 Answers
5
active
oldest
votes
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib> // mmocny: I needed to add this to use atof
#include <functional>
using namespace std;
//----------------------------------
class Variable
{
public:
Variable(const string& name, double val)
: name_(name), val_(val) // mmocny: Use initializer lists
{
}
// mmocny: get_* syntax is less common in C++ than in java etc.
const string& name() const { return name_; } // mmocny: Don't mark as inline (they already are, and its premature optimization)
double val() const { return val_; } // mmocny: Again, don't mark as inline
private:
string name_; // mmocny: suggest renaming name_ or _name: Easier to spot member variables in method code, and no naming conflicts with methods
double val_;
};
//----------------------------------
// mmocny: Replace print_* methods with operator<< so that other (non cout) streams can be used.
// mmocny: Alternatively, define to_string()/str() methods which can also be piped out to different streams
std::ostream & operator<<(std::ostream & out, Variable const & v)
{
return out << v.name() << ", " << v.val() << endl;
}
std::ostream & operator<<(std::ostream & out, vector<Variable> const & v)
{
for (vector<Variable>::const_iterator it = v.begin(), end = v.end(); it != end; ++it ) // mmocny: Use iterators rather than index access
{
out << *it << endl;
}
return out;
}
//----------------------------------
double get_variable(const string& op, vector<Variable>& v)
{
// mmocny: instead of using a vector<Variable> you should be using a map/unordered_map<string,double> and doing a key lookup here
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].name())
return v[i].val();
}
// mmocny: what do you do if you don't find the variable?
throw std::exception(); // mmocny: You should do something better than throw a generic exception()
}
//----------------------------------
bool is_number(const string& op)
{
// mmocny: someone else already mentioned: what if op is empty?
int char_to_int = op[0];
// mmocny: couple notes here:
// 1) chars are actually numbers you can reference directly, and not need "magic" constants
// 2) functions in the form "if (...) return true; else return false;" should just be reduced to "return (...);"
// 3) is_number functionality already exists in libc as isdigit()
// 4) long term, you are probably going to want to improve this function.. what about negative numbers? numbers in the form .02? etc..
//return (char_to_int >= '0' && char_to_int <= '9');
return isdigit(char_to_int);
}
//----------------------------------
// mmocny: replace istringstream with istream
// mmocny: you only need to predeclare this one function
double operate(const string& op, istream& in, vector<Variable>& v);
//----------------------------------
/*
* mmocny: All of your perform_* functions have identical code other than the operator being used.
* You can turn all of these functions into a single function template where you pass the operator to be used.
* Luckily, <functional> already has plus minus multiplies divides function objects (functors)
*/
template< class Operator >
double perform_action(istream& in, vector<Variable>& v, Operator op)
{
string left;
in >> left;
double result = operate(left, in, v); // mmocny: This is a big one: for correctness, you must calculate result of left BEFORE you read right
string right;
in >> right;
return op(result, operate(right, in, v));
}
//----------------------------------
double operate(const string& op, istream& in, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_action(in, v, plus<double>());
else if (op == "-")
value = perform_action(in, v, minus<double>());
else if(op == "*")
value = perform_action(in, v, multiplies<double>());
else if (op == "/")
value = perform_action(in, v, divides<double>());
else if (is_number(op))
value = atof(op.c_str()); // mmocny: consider using boost::lexical_cast<>, or strtod (maybe)
else
value = get_variable(op, v);
return value;
}
//----------------------------------
void define_new_var(vector<Variable>& v, istream& in)
{
string name;
in >> name;
string temp;
in >> temp;
double value = operate(temp, in, v);
v.push_back(Variable(name, value));
}
//----------------------------------
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
if (temp.empty()) // mmocny: This also handles the case of CTRL+D
continue;
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
std::cout << v << std::endl;
else
cout << endl << operate(op, iss, v) << endl;
}
}
Those are my changes, with comments inline. I would make more changes, but thats enough for now :)
Notice one BIG change: you have a serious correctness bug in your perform_* functions. Not that I've tested my modified code above for all edge cases, but the original code was flat out always wrong for any nested calculations.
add a comment |
As a minor aside, I've renamed it to PN
instead of RPN
. The form you've give (with operators preceding the operands) is Polish Notation as Jan Łukasiewicz invented it. RPN is when you reverse that, and have the operands first, followed by the applicable operator.
As to why they decided to call that RPN: because English speakers had a hard enough time figuring out that his last name was pronounced roughly like "Wookashayvitch", not to mention trying to figure out how to say that backwards.
In any case, I think I'd write the code more like this:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>
using namespace std; // really would *not* normally do this, but...
void define_var(map<string, int> &v, istringstream& iss) {
std::string name;
int value;
iss >> name >> value;
v[name] = value;
}
int do_op(char op, int val1, int val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
string error("Unknown operator: ");
error += op;
throw runtime_error(error);
}
}
bool isoperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
char getop(istream &is) {
char ch;
while (isspace(ch = is.peek()))
is.get(ch);
ch = is.peek();
return ch;
}
int eval(istream &is, map<string, int> const &v) {
// evaluate an expression. It consists of:
// an operator followed by operands, or
// a number, or
// a variable.
//
char ch = getop(is);
if (isoperator(ch)) {
is.get(ch);
int val1 = eval(is, v);
int val2 = eval(is, v);
return do_op(ch, val1, val2);
}
if (isdigit(ch)) {
int val;
is >> val;
return val;
}
string var_name;
is >> var_name;
map<string, int>::const_iterator p = v.find(var_name);
if (p==v.end()) {
string problem("Unknown variable: ");
problem +=var_name;
throw runtime_error(problem.c_str());
}
return p->second;
}
// used only for dumping out the variables.
namespace std {
ostream &operator<<(ostream &os, pair<string, int> const &v) {
return os << v.first << ": " << v.second;
}
}
int main() {
cout << endl << "LePN Programming Language" << endl;
map<string, int> v;
string temp;
cout << endl << "> ";
while (getline(cin, temp)) {
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_var(v, iss);
else if (op == "show_vars")
std::copy(v.begin(), v.end(), ostream_iterator<pair<string, int> >(cout, "n"));
else {
// Technically, this isn't right -- it only ungets one
// character, not the whole string.
// For example, this would interpret "this+ 2 3" as "+ 2 3"
// and give 5 instead of an error message. Shouldn't affect
// correct input though.
//
iss.unget();
cout << endl << eval(iss, v) << endl;
}
cout << endl << "> ";
}
}
1
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and thech=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.
– Jerry Coffin
May 4 '11 at 2:45
1
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
add a comment |
double get_variable(const string& op, vector<Variable>& v)
-
must return default value (0.0), for example ifop
is empty. Alternatively, you can show an error message.
double operate(const string& op, istringstream& iss, vector<Variable>& v)
-
Always initialize variables, like this:
double value(0.0); OR
double value = 0.0;
and you must check this
op
in vector, if it doesn't exist - show error.
void define_new_var(vector<Variable>& v, istringstream& iss)
-
if variable already exists, set new value or show error.
bool is_number(const string& op)
int char_to_int = op[0];
if
op
is empty.
For this functions:
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
Define common function who get left and right.
I think you can define some
enum
s and functions, like this:
double perform_operator(istringstream& iss, vector<Variable>& v, OperatorType type)
{
std::string lhs, rhs;
GetValues(iss, lhs, rhs); // fill them
switch(type)
{
case Minus
{
return operate(left, iss, v) - operate(right, iss, v);
}
break;
}
}
add a comment |
If you look at the similarity of your various perform_xxx functions, it suggests there is duplication that could be removed. One way to do this would be to extract Operator classes, one for each operation, where the common behavior of extracting left and right values from the stream was expressed in the superclass and the individual operations of multiplication, addition, etc. was left to the subclasses.
add a comment |
I don't write the expressions as you did, I use Reverse Polish Notation :
Exp : + 5 * 2 3 = 11 .... (5 + 2 * 3)
I would write :
Exp : 5 2 3 * + = 11 .... (5 + 2 * 3)
Another example of my writing :
10 2 * 15 3 * 5 - + .... (10 * 2 + (15 * 3 - 5)) = 20 + (45 - 5) = 20 + 40 = 60
It is better to write the same way you enter in the RPN calculator , such as HP 12C
New contributor
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f2211%2fpolish-notation-interpreter-calculator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib> // mmocny: I needed to add this to use atof
#include <functional>
using namespace std;
//----------------------------------
class Variable
{
public:
Variable(const string& name, double val)
: name_(name), val_(val) // mmocny: Use initializer lists
{
}
// mmocny: get_* syntax is less common in C++ than in java etc.
const string& name() const { return name_; } // mmocny: Don't mark as inline (they already are, and its premature optimization)
double val() const { return val_; } // mmocny: Again, don't mark as inline
private:
string name_; // mmocny: suggest renaming name_ or _name: Easier to spot member variables in method code, and no naming conflicts with methods
double val_;
};
//----------------------------------
// mmocny: Replace print_* methods with operator<< so that other (non cout) streams can be used.
// mmocny: Alternatively, define to_string()/str() methods which can also be piped out to different streams
std::ostream & operator<<(std::ostream & out, Variable const & v)
{
return out << v.name() << ", " << v.val() << endl;
}
std::ostream & operator<<(std::ostream & out, vector<Variable> const & v)
{
for (vector<Variable>::const_iterator it = v.begin(), end = v.end(); it != end; ++it ) // mmocny: Use iterators rather than index access
{
out << *it << endl;
}
return out;
}
//----------------------------------
double get_variable(const string& op, vector<Variable>& v)
{
// mmocny: instead of using a vector<Variable> you should be using a map/unordered_map<string,double> and doing a key lookup here
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].name())
return v[i].val();
}
// mmocny: what do you do if you don't find the variable?
throw std::exception(); // mmocny: You should do something better than throw a generic exception()
}
//----------------------------------
bool is_number(const string& op)
{
// mmocny: someone else already mentioned: what if op is empty?
int char_to_int = op[0];
// mmocny: couple notes here:
// 1) chars are actually numbers you can reference directly, and not need "magic" constants
// 2) functions in the form "if (...) return true; else return false;" should just be reduced to "return (...);"
// 3) is_number functionality already exists in libc as isdigit()
// 4) long term, you are probably going to want to improve this function.. what about negative numbers? numbers in the form .02? etc..
//return (char_to_int >= '0' && char_to_int <= '9');
return isdigit(char_to_int);
}
//----------------------------------
// mmocny: replace istringstream with istream
// mmocny: you only need to predeclare this one function
double operate(const string& op, istream& in, vector<Variable>& v);
//----------------------------------
/*
* mmocny: All of your perform_* functions have identical code other than the operator being used.
* You can turn all of these functions into a single function template where you pass the operator to be used.
* Luckily, <functional> already has plus minus multiplies divides function objects (functors)
*/
template< class Operator >
double perform_action(istream& in, vector<Variable>& v, Operator op)
{
string left;
in >> left;
double result = operate(left, in, v); // mmocny: This is a big one: for correctness, you must calculate result of left BEFORE you read right
string right;
in >> right;
return op(result, operate(right, in, v));
}
//----------------------------------
double operate(const string& op, istream& in, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_action(in, v, plus<double>());
else if (op == "-")
value = perform_action(in, v, minus<double>());
else if(op == "*")
value = perform_action(in, v, multiplies<double>());
else if (op == "/")
value = perform_action(in, v, divides<double>());
else if (is_number(op))
value = atof(op.c_str()); // mmocny: consider using boost::lexical_cast<>, or strtod (maybe)
else
value = get_variable(op, v);
return value;
}
//----------------------------------
void define_new_var(vector<Variable>& v, istream& in)
{
string name;
in >> name;
string temp;
in >> temp;
double value = operate(temp, in, v);
v.push_back(Variable(name, value));
}
//----------------------------------
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
if (temp.empty()) // mmocny: This also handles the case of CTRL+D
continue;
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
std::cout << v << std::endl;
else
cout << endl << operate(op, iss, v) << endl;
}
}
Those are my changes, with comments inline. I would make more changes, but thats enough for now :)
Notice one BIG change: you have a serious correctness bug in your perform_* functions. Not that I've tested my modified code above for all edge cases, but the original code was flat out always wrong for any nested calculations.
add a comment |
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib> // mmocny: I needed to add this to use atof
#include <functional>
using namespace std;
//----------------------------------
class Variable
{
public:
Variable(const string& name, double val)
: name_(name), val_(val) // mmocny: Use initializer lists
{
}
// mmocny: get_* syntax is less common in C++ than in java etc.
const string& name() const { return name_; } // mmocny: Don't mark as inline (they already are, and its premature optimization)
double val() const { return val_; } // mmocny: Again, don't mark as inline
private:
string name_; // mmocny: suggest renaming name_ or _name: Easier to spot member variables in method code, and no naming conflicts with methods
double val_;
};
//----------------------------------
// mmocny: Replace print_* methods with operator<< so that other (non cout) streams can be used.
// mmocny: Alternatively, define to_string()/str() methods which can also be piped out to different streams
std::ostream & operator<<(std::ostream & out, Variable const & v)
{
return out << v.name() << ", " << v.val() << endl;
}
std::ostream & operator<<(std::ostream & out, vector<Variable> const & v)
{
for (vector<Variable>::const_iterator it = v.begin(), end = v.end(); it != end; ++it ) // mmocny: Use iterators rather than index access
{
out << *it << endl;
}
return out;
}
//----------------------------------
double get_variable(const string& op, vector<Variable>& v)
{
// mmocny: instead of using a vector<Variable> you should be using a map/unordered_map<string,double> and doing a key lookup here
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].name())
return v[i].val();
}
// mmocny: what do you do if you don't find the variable?
throw std::exception(); // mmocny: You should do something better than throw a generic exception()
}
//----------------------------------
bool is_number(const string& op)
{
// mmocny: someone else already mentioned: what if op is empty?
int char_to_int = op[0];
// mmocny: couple notes here:
// 1) chars are actually numbers you can reference directly, and not need "magic" constants
// 2) functions in the form "if (...) return true; else return false;" should just be reduced to "return (...);"
// 3) is_number functionality already exists in libc as isdigit()
// 4) long term, you are probably going to want to improve this function.. what about negative numbers? numbers in the form .02? etc..
//return (char_to_int >= '0' && char_to_int <= '9');
return isdigit(char_to_int);
}
//----------------------------------
// mmocny: replace istringstream with istream
// mmocny: you only need to predeclare this one function
double operate(const string& op, istream& in, vector<Variable>& v);
//----------------------------------
/*
* mmocny: All of your perform_* functions have identical code other than the operator being used.
* You can turn all of these functions into a single function template where you pass the operator to be used.
* Luckily, <functional> already has plus minus multiplies divides function objects (functors)
*/
template< class Operator >
double perform_action(istream& in, vector<Variable>& v, Operator op)
{
string left;
in >> left;
double result = operate(left, in, v); // mmocny: This is a big one: for correctness, you must calculate result of left BEFORE you read right
string right;
in >> right;
return op(result, operate(right, in, v));
}
//----------------------------------
double operate(const string& op, istream& in, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_action(in, v, plus<double>());
else if (op == "-")
value = perform_action(in, v, minus<double>());
else if(op == "*")
value = perform_action(in, v, multiplies<double>());
else if (op == "/")
value = perform_action(in, v, divides<double>());
else if (is_number(op))
value = atof(op.c_str()); // mmocny: consider using boost::lexical_cast<>, or strtod (maybe)
else
value = get_variable(op, v);
return value;
}
//----------------------------------
void define_new_var(vector<Variable>& v, istream& in)
{
string name;
in >> name;
string temp;
in >> temp;
double value = operate(temp, in, v);
v.push_back(Variable(name, value));
}
//----------------------------------
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
if (temp.empty()) // mmocny: This also handles the case of CTRL+D
continue;
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
std::cout << v << std::endl;
else
cout << endl << operate(op, iss, v) << endl;
}
}
Those are my changes, with comments inline. I would make more changes, but thats enough for now :)
Notice one BIG change: you have a serious correctness bug in your perform_* functions. Not that I've tested my modified code above for all edge cases, but the original code was flat out always wrong for any nested calculations.
add a comment |
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib> // mmocny: I needed to add this to use atof
#include <functional>
using namespace std;
//----------------------------------
class Variable
{
public:
Variable(const string& name, double val)
: name_(name), val_(val) // mmocny: Use initializer lists
{
}
// mmocny: get_* syntax is less common in C++ than in java etc.
const string& name() const { return name_; } // mmocny: Don't mark as inline (they already are, and its premature optimization)
double val() const { return val_; } // mmocny: Again, don't mark as inline
private:
string name_; // mmocny: suggest renaming name_ or _name: Easier to spot member variables in method code, and no naming conflicts with methods
double val_;
};
//----------------------------------
// mmocny: Replace print_* methods with operator<< so that other (non cout) streams can be used.
// mmocny: Alternatively, define to_string()/str() methods which can also be piped out to different streams
std::ostream & operator<<(std::ostream & out, Variable const & v)
{
return out << v.name() << ", " << v.val() << endl;
}
std::ostream & operator<<(std::ostream & out, vector<Variable> const & v)
{
for (vector<Variable>::const_iterator it = v.begin(), end = v.end(); it != end; ++it ) // mmocny: Use iterators rather than index access
{
out << *it << endl;
}
return out;
}
//----------------------------------
double get_variable(const string& op, vector<Variable>& v)
{
// mmocny: instead of using a vector<Variable> you should be using a map/unordered_map<string,double> and doing a key lookup here
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].name())
return v[i].val();
}
// mmocny: what do you do if you don't find the variable?
throw std::exception(); // mmocny: You should do something better than throw a generic exception()
}
//----------------------------------
bool is_number(const string& op)
{
// mmocny: someone else already mentioned: what if op is empty?
int char_to_int = op[0];
// mmocny: couple notes here:
// 1) chars are actually numbers you can reference directly, and not need "magic" constants
// 2) functions in the form "if (...) return true; else return false;" should just be reduced to "return (...);"
// 3) is_number functionality already exists in libc as isdigit()
// 4) long term, you are probably going to want to improve this function.. what about negative numbers? numbers in the form .02? etc..
//return (char_to_int >= '0' && char_to_int <= '9');
return isdigit(char_to_int);
}
//----------------------------------
// mmocny: replace istringstream with istream
// mmocny: you only need to predeclare this one function
double operate(const string& op, istream& in, vector<Variable>& v);
//----------------------------------
/*
* mmocny: All of your perform_* functions have identical code other than the operator being used.
* You can turn all of these functions into a single function template where you pass the operator to be used.
* Luckily, <functional> already has plus minus multiplies divides function objects (functors)
*/
template< class Operator >
double perform_action(istream& in, vector<Variable>& v, Operator op)
{
string left;
in >> left;
double result = operate(left, in, v); // mmocny: This is a big one: for correctness, you must calculate result of left BEFORE you read right
string right;
in >> right;
return op(result, operate(right, in, v));
}
//----------------------------------
double operate(const string& op, istream& in, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_action(in, v, plus<double>());
else if (op == "-")
value = perform_action(in, v, minus<double>());
else if(op == "*")
value = perform_action(in, v, multiplies<double>());
else if (op == "/")
value = perform_action(in, v, divides<double>());
else if (is_number(op))
value = atof(op.c_str()); // mmocny: consider using boost::lexical_cast<>, or strtod (maybe)
else
value = get_variable(op, v);
return value;
}
//----------------------------------
void define_new_var(vector<Variable>& v, istream& in)
{
string name;
in >> name;
string temp;
in >> temp;
double value = operate(temp, in, v);
v.push_back(Variable(name, value));
}
//----------------------------------
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
if (temp.empty()) // mmocny: This also handles the case of CTRL+D
continue;
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
std::cout << v << std::endl;
else
cout << endl << operate(op, iss, v) << endl;
}
}
Those are my changes, with comments inline. I would make more changes, but thats enough for now :)
Notice one BIG change: you have a serious correctness bug in your perform_* functions. Not that I've tested my modified code above for all edge cases, but the original code was flat out always wrong for any nested calculations.
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdlib> // mmocny: I needed to add this to use atof
#include <functional>
using namespace std;
//----------------------------------
class Variable
{
public:
Variable(const string& name, double val)
: name_(name), val_(val) // mmocny: Use initializer lists
{
}
// mmocny: get_* syntax is less common in C++ than in java etc.
const string& name() const { return name_; } // mmocny: Don't mark as inline (they already are, and its premature optimization)
double val() const { return val_; } // mmocny: Again, don't mark as inline
private:
string name_; // mmocny: suggest renaming name_ or _name: Easier to spot member variables in method code, and no naming conflicts with methods
double val_;
};
//----------------------------------
// mmocny: Replace print_* methods with operator<< so that other (non cout) streams can be used.
// mmocny: Alternatively, define to_string()/str() methods which can also be piped out to different streams
std::ostream & operator<<(std::ostream & out, Variable const & v)
{
return out << v.name() << ", " << v.val() << endl;
}
std::ostream & operator<<(std::ostream & out, vector<Variable> const & v)
{
for (vector<Variable>::const_iterator it = v.begin(), end = v.end(); it != end; ++it ) // mmocny: Use iterators rather than index access
{
out << *it << endl;
}
return out;
}
//----------------------------------
double get_variable(const string& op, vector<Variable>& v)
{
// mmocny: instead of using a vector<Variable> you should be using a map/unordered_map<string,double> and doing a key lookup here
int size = v.size();
for (int i = 0; i < size; i++)
{
if (op == v[i].name())
return v[i].val();
}
// mmocny: what do you do if you don't find the variable?
throw std::exception(); // mmocny: You should do something better than throw a generic exception()
}
//----------------------------------
bool is_number(const string& op)
{
// mmocny: someone else already mentioned: what if op is empty?
int char_to_int = op[0];
// mmocny: couple notes here:
// 1) chars are actually numbers you can reference directly, and not need "magic" constants
// 2) functions in the form "if (...) return true; else return false;" should just be reduced to "return (...);"
// 3) is_number functionality already exists in libc as isdigit()
// 4) long term, you are probably going to want to improve this function.. what about negative numbers? numbers in the form .02? etc..
//return (char_to_int >= '0' && char_to_int <= '9');
return isdigit(char_to_int);
}
//----------------------------------
// mmocny: replace istringstream with istream
// mmocny: you only need to predeclare this one function
double operate(const string& op, istream& in, vector<Variable>& v);
//----------------------------------
/*
* mmocny: All of your perform_* functions have identical code other than the operator being used.
* You can turn all of these functions into a single function template where you pass the operator to be used.
* Luckily, <functional> already has plus minus multiplies divides function objects (functors)
*/
template< class Operator >
double perform_action(istream& in, vector<Variable>& v, Operator op)
{
string left;
in >> left;
double result = operate(left, in, v); // mmocny: This is a big one: for correctness, you must calculate result of left BEFORE you read right
string right;
in >> right;
return op(result, operate(right, in, v));
}
//----------------------------------
double operate(const string& op, istream& in, vector<Variable>& v)
{
double value;
if (op == "+")
value = perform_action(in, v, plus<double>());
else if (op == "-")
value = perform_action(in, v, minus<double>());
else if(op == "*")
value = perform_action(in, v, multiplies<double>());
else if (op == "/")
value = perform_action(in, v, divides<double>());
else if (is_number(op))
value = atof(op.c_str()); // mmocny: consider using boost::lexical_cast<>, or strtod (maybe)
else
value = get_variable(op, v);
return value;
}
//----------------------------------
void define_new_var(vector<Variable>& v, istream& in)
{
string name;
in >> name;
string temp;
in >> temp;
double value = operate(temp, in, v);
v.push_back(Variable(name, value));
}
//----------------------------------
int main()
{
cout << endl << "LeRPN Programming Language" << endl;
vector<Variable> v;
string temp;
while (cin)
{
cout << endl << "> ";
getline(cin, temp);
if (temp.empty()) // mmocny: This also handles the case of CTRL+D
continue;
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_new_var(v, iss);
else if (op == "show_vars")
std::cout << v << std::endl;
else
cout << endl << operate(op, iss, v) << endl;
}
}
Those are my changes, with comments inline. I would make more changes, but thats enough for now :)
Notice one BIG change: you have a serious correctness bug in your perform_* functions. Not that I've tested my modified code above for all edge cases, but the original code was flat out always wrong for any nested calculations.
edited May 3 '11 at 19:46
answered May 3 '11 at 19:39
mmocny
1827
1827
add a comment |
add a comment |
As a minor aside, I've renamed it to PN
instead of RPN
. The form you've give (with operators preceding the operands) is Polish Notation as Jan Łukasiewicz invented it. RPN is when you reverse that, and have the operands first, followed by the applicable operator.
As to why they decided to call that RPN: because English speakers had a hard enough time figuring out that his last name was pronounced roughly like "Wookashayvitch", not to mention trying to figure out how to say that backwards.
In any case, I think I'd write the code more like this:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>
using namespace std; // really would *not* normally do this, but...
void define_var(map<string, int> &v, istringstream& iss) {
std::string name;
int value;
iss >> name >> value;
v[name] = value;
}
int do_op(char op, int val1, int val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
string error("Unknown operator: ");
error += op;
throw runtime_error(error);
}
}
bool isoperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
char getop(istream &is) {
char ch;
while (isspace(ch = is.peek()))
is.get(ch);
ch = is.peek();
return ch;
}
int eval(istream &is, map<string, int> const &v) {
// evaluate an expression. It consists of:
// an operator followed by operands, or
// a number, or
// a variable.
//
char ch = getop(is);
if (isoperator(ch)) {
is.get(ch);
int val1 = eval(is, v);
int val2 = eval(is, v);
return do_op(ch, val1, val2);
}
if (isdigit(ch)) {
int val;
is >> val;
return val;
}
string var_name;
is >> var_name;
map<string, int>::const_iterator p = v.find(var_name);
if (p==v.end()) {
string problem("Unknown variable: ");
problem +=var_name;
throw runtime_error(problem.c_str());
}
return p->second;
}
// used only for dumping out the variables.
namespace std {
ostream &operator<<(ostream &os, pair<string, int> const &v) {
return os << v.first << ": " << v.second;
}
}
int main() {
cout << endl << "LePN Programming Language" << endl;
map<string, int> v;
string temp;
cout << endl << "> ";
while (getline(cin, temp)) {
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_var(v, iss);
else if (op == "show_vars")
std::copy(v.begin(), v.end(), ostream_iterator<pair<string, int> >(cout, "n"));
else {
// Technically, this isn't right -- it only ungets one
// character, not the whole string.
// For example, this would interpret "this+ 2 3" as "+ 2 3"
// and give 5 instead of an error message. Shouldn't affect
// correct input though.
//
iss.unget();
cout << endl << eval(iss, v) << endl;
}
cout << endl << "> ";
}
}
1
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and thech=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.
– Jerry Coffin
May 4 '11 at 2:45
1
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
add a comment |
As a minor aside, I've renamed it to PN
instead of RPN
. The form you've give (with operators preceding the operands) is Polish Notation as Jan Łukasiewicz invented it. RPN is when you reverse that, and have the operands first, followed by the applicable operator.
As to why they decided to call that RPN: because English speakers had a hard enough time figuring out that his last name was pronounced roughly like "Wookashayvitch", not to mention trying to figure out how to say that backwards.
In any case, I think I'd write the code more like this:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>
using namespace std; // really would *not* normally do this, but...
void define_var(map<string, int> &v, istringstream& iss) {
std::string name;
int value;
iss >> name >> value;
v[name] = value;
}
int do_op(char op, int val1, int val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
string error("Unknown operator: ");
error += op;
throw runtime_error(error);
}
}
bool isoperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
char getop(istream &is) {
char ch;
while (isspace(ch = is.peek()))
is.get(ch);
ch = is.peek();
return ch;
}
int eval(istream &is, map<string, int> const &v) {
// evaluate an expression. It consists of:
// an operator followed by operands, or
// a number, or
// a variable.
//
char ch = getop(is);
if (isoperator(ch)) {
is.get(ch);
int val1 = eval(is, v);
int val2 = eval(is, v);
return do_op(ch, val1, val2);
}
if (isdigit(ch)) {
int val;
is >> val;
return val;
}
string var_name;
is >> var_name;
map<string, int>::const_iterator p = v.find(var_name);
if (p==v.end()) {
string problem("Unknown variable: ");
problem +=var_name;
throw runtime_error(problem.c_str());
}
return p->second;
}
// used only for dumping out the variables.
namespace std {
ostream &operator<<(ostream &os, pair<string, int> const &v) {
return os << v.first << ": " << v.second;
}
}
int main() {
cout << endl << "LePN Programming Language" << endl;
map<string, int> v;
string temp;
cout << endl << "> ";
while (getline(cin, temp)) {
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_var(v, iss);
else if (op == "show_vars")
std::copy(v.begin(), v.end(), ostream_iterator<pair<string, int> >(cout, "n"));
else {
// Technically, this isn't right -- it only ungets one
// character, not the whole string.
// For example, this would interpret "this+ 2 3" as "+ 2 3"
// and give 5 instead of an error message. Shouldn't affect
// correct input though.
//
iss.unget();
cout << endl << eval(iss, v) << endl;
}
cout << endl << "> ";
}
}
1
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and thech=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.
– Jerry Coffin
May 4 '11 at 2:45
1
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
add a comment |
As a minor aside, I've renamed it to PN
instead of RPN
. The form you've give (with operators preceding the operands) is Polish Notation as Jan Łukasiewicz invented it. RPN is when you reverse that, and have the operands first, followed by the applicable operator.
As to why they decided to call that RPN: because English speakers had a hard enough time figuring out that his last name was pronounced roughly like "Wookashayvitch", not to mention trying to figure out how to say that backwards.
In any case, I think I'd write the code more like this:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>
using namespace std; // really would *not* normally do this, but...
void define_var(map<string, int> &v, istringstream& iss) {
std::string name;
int value;
iss >> name >> value;
v[name] = value;
}
int do_op(char op, int val1, int val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
string error("Unknown operator: ");
error += op;
throw runtime_error(error);
}
}
bool isoperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
char getop(istream &is) {
char ch;
while (isspace(ch = is.peek()))
is.get(ch);
ch = is.peek();
return ch;
}
int eval(istream &is, map<string, int> const &v) {
// evaluate an expression. It consists of:
// an operator followed by operands, or
// a number, or
// a variable.
//
char ch = getop(is);
if (isoperator(ch)) {
is.get(ch);
int val1 = eval(is, v);
int val2 = eval(is, v);
return do_op(ch, val1, val2);
}
if (isdigit(ch)) {
int val;
is >> val;
return val;
}
string var_name;
is >> var_name;
map<string, int>::const_iterator p = v.find(var_name);
if (p==v.end()) {
string problem("Unknown variable: ");
problem +=var_name;
throw runtime_error(problem.c_str());
}
return p->second;
}
// used only for dumping out the variables.
namespace std {
ostream &operator<<(ostream &os, pair<string, int> const &v) {
return os << v.first << ": " << v.second;
}
}
int main() {
cout << endl << "LePN Programming Language" << endl;
map<string, int> v;
string temp;
cout << endl << "> ";
while (getline(cin, temp)) {
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_var(v, iss);
else if (op == "show_vars")
std::copy(v.begin(), v.end(), ostream_iterator<pair<string, int> >(cout, "n"));
else {
// Technically, this isn't right -- it only ungets one
// character, not the whole string.
// For example, this would interpret "this+ 2 3" as "+ 2 3"
// and give 5 instead of an error message. Shouldn't affect
// correct input though.
//
iss.unget();
cout << endl << eval(iss, v) << endl;
}
cout << endl << "> ";
}
}
As a minor aside, I've renamed it to PN
instead of RPN
. The form you've give (with operators preceding the operands) is Polish Notation as Jan Łukasiewicz invented it. RPN is when you reverse that, and have the operands first, followed by the applicable operator.
As to why they decided to call that RPN: because English speakers had a hard enough time figuring out that his last name was pronounced roughly like "Wookashayvitch", not to mention trying to figure out how to say that backwards.
In any case, I think I'd write the code more like this:
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include <iterator>
using namespace std; // really would *not* normally do this, but...
void define_var(map<string, int> &v, istringstream& iss) {
std::string name;
int value;
iss >> name >> value;
v[name] = value;
}
int do_op(char op, int val1, int val2) {
switch (op) {
case '+': return val1 + val2;
case '-': return val1 - val2;
case '*': return val1 * val2;
case '/': return val1 / val2;
default:
string error("Unknown operator: ");
error += op;
throw runtime_error(error);
}
}
bool isoperator(char ch) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/';
}
char getop(istream &is) {
char ch;
while (isspace(ch = is.peek()))
is.get(ch);
ch = is.peek();
return ch;
}
int eval(istream &is, map<string, int> const &v) {
// evaluate an expression. It consists of:
// an operator followed by operands, or
// a number, or
// a variable.
//
char ch = getop(is);
if (isoperator(ch)) {
is.get(ch);
int val1 = eval(is, v);
int val2 = eval(is, v);
return do_op(ch, val1, val2);
}
if (isdigit(ch)) {
int val;
is >> val;
return val;
}
string var_name;
is >> var_name;
map<string, int>::const_iterator p = v.find(var_name);
if (p==v.end()) {
string problem("Unknown variable: ");
problem +=var_name;
throw runtime_error(problem.c_str());
}
return p->second;
}
// used only for dumping out the variables.
namespace std {
ostream &operator<<(ostream &os, pair<string, int> const &v) {
return os << v.first << ": " << v.second;
}
}
int main() {
cout << endl << "LePN Programming Language" << endl;
map<string, int> v;
string temp;
cout << endl << "> ";
while (getline(cin, temp)) {
istringstream iss(temp);
string op;
iss >> op;
if (op == "quit")
break;
else if (op == "def")
define_var(v, iss);
else if (op == "show_vars")
std::copy(v.begin(), v.end(), ostream_iterator<pair<string, int> >(cout, "n"));
else {
// Technically, this isn't right -- it only ungets one
// character, not the whole string.
// For example, this would interpret "this+ 2 3" as "+ 2 3"
// and give 5 instead of an error message. Shouldn't affect
// correct input though.
//
iss.unget();
cout << endl << eval(iss, v) << endl;
}
cout << endl << "> ";
}
}
edited May 4 '11 at 2:43
answered May 3 '11 at 23:49
Jerry Coffin
27.8k460125
27.8k460125
1
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and thech=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.
– Jerry Coffin
May 4 '11 at 2:45
1
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
add a comment |
1
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and thech=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.
– Jerry Coffin
May 4 '11 at 2:45
1
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
1
1
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
You throw a string in do_op instead of std::runtime_error(), though i imagine that was just overlooked as you do the right thing later... I believe that the ch = is.peek() is not needed in getop() after the loop... i like that you wrote a getop() function that doesn't require op to precede whitespace...
– mmocny
May 4 '11 at 2:38
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and the
ch=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.– Jerry Coffin
May 4 '11 at 2:45
@mmocny: Oops, thanks for pointing out the silly mistake in do_op. Perhaps a first: a time that an exception specification might have actually been useful! I think you're right and the
ch=is.peek()
is technically unnecessary, but at least to me it seemed clearer this way.– Jerry Coffin
May 4 '11 at 2:45
1
1
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Totally unrelated: depends on your notion of "reverse": a phonemic reversal such as Cziveisakuł would be [tʂiveisakuw], but a phonetic reversal would be [ʂtivejɕakuw], roughly Sztiveiśakuł.
– Jon Purdy
May 4 '11 at 23:17
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
Adding an overload to namespace std? :'( Edit: woah.. this is old.. didn't see that
– dyp
Jun 15 '14 at 23:27
add a comment |
double get_variable(const string& op, vector<Variable>& v)
-
must return default value (0.0), for example ifop
is empty. Alternatively, you can show an error message.
double operate(const string& op, istringstream& iss, vector<Variable>& v)
-
Always initialize variables, like this:
double value(0.0); OR
double value = 0.0;
and you must check this
op
in vector, if it doesn't exist - show error.
void define_new_var(vector<Variable>& v, istringstream& iss)
-
if variable already exists, set new value or show error.
bool is_number(const string& op)
int char_to_int = op[0];
if
op
is empty.
For this functions:
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
Define common function who get left and right.
I think you can define some
enum
s and functions, like this:
double perform_operator(istringstream& iss, vector<Variable>& v, OperatorType type)
{
std::string lhs, rhs;
GetValues(iss, lhs, rhs); // fill them
switch(type)
{
case Minus
{
return operate(left, iss, v) - operate(right, iss, v);
}
break;
}
}
add a comment |
double get_variable(const string& op, vector<Variable>& v)
-
must return default value (0.0), for example ifop
is empty. Alternatively, you can show an error message.
double operate(const string& op, istringstream& iss, vector<Variable>& v)
-
Always initialize variables, like this:
double value(0.0); OR
double value = 0.0;
and you must check this
op
in vector, if it doesn't exist - show error.
void define_new_var(vector<Variable>& v, istringstream& iss)
-
if variable already exists, set new value or show error.
bool is_number(const string& op)
int char_to_int = op[0];
if
op
is empty.
For this functions:
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
Define common function who get left and right.
I think you can define some
enum
s and functions, like this:
double perform_operator(istringstream& iss, vector<Variable>& v, OperatorType type)
{
std::string lhs, rhs;
GetValues(iss, lhs, rhs); // fill them
switch(type)
{
case Minus
{
return operate(left, iss, v) - operate(right, iss, v);
}
break;
}
}
add a comment |
double get_variable(const string& op, vector<Variable>& v)
-
must return default value (0.0), for example ifop
is empty. Alternatively, you can show an error message.
double operate(const string& op, istringstream& iss, vector<Variable>& v)
-
Always initialize variables, like this:
double value(0.0); OR
double value = 0.0;
and you must check this
op
in vector, if it doesn't exist - show error.
void define_new_var(vector<Variable>& v, istringstream& iss)
-
if variable already exists, set new value or show error.
bool is_number(const string& op)
int char_to_int = op[0];
if
op
is empty.
For this functions:
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
Define common function who get left and right.
I think you can define some
enum
s and functions, like this:
double perform_operator(istringstream& iss, vector<Variable>& v, OperatorType type)
{
std::string lhs, rhs;
GetValues(iss, lhs, rhs); // fill them
switch(type)
{
case Minus
{
return operate(left, iss, v) - operate(right, iss, v);
}
break;
}
}
double get_variable(const string& op, vector<Variable>& v)
-
must return default value (0.0), for example ifop
is empty. Alternatively, you can show an error message.
double operate(const string& op, istringstream& iss, vector<Variable>& v)
-
Always initialize variables, like this:
double value(0.0); OR
double value = 0.0;
and you must check this
op
in vector, if it doesn't exist - show error.
void define_new_var(vector<Variable>& v, istringstream& iss)
-
if variable already exists, set new value or show error.
bool is_number(const string& op)
int char_to_int = op[0];
if
op
is empty.
For this functions:
double perform_addition(istringstream& iss, vector<Variable>& v);
double perform_subtraction(istringstream& iss, vector<Variable>& v);
double perform_division(istringstream& iss, vector<Variable>& v);
double perform_multiplication(istringstream& iss, vector<Variable>& v);
Define common function who get left and right.
I think you can define some
enum
s and functions, like this:
double perform_operator(istringstream& iss, vector<Variable>& v, OperatorType type)
{
std::string lhs, rhs;
GetValues(iss, lhs, rhs); // fill them
switch(type)
{
case Minus
{
return operate(left, iss, v) - operate(right, iss, v);
}
break;
}
}
edited Jun 15 '14 at 6:07
Jamal♦
30.2k11116226
30.2k11116226
answered May 3 '11 at 19:00
siquell
23214
23214
add a comment |
add a comment |
If you look at the similarity of your various perform_xxx functions, it suggests there is duplication that could be removed. One way to do this would be to extract Operator classes, one for each operation, where the common behavior of extracting left and right values from the stream was expressed in the superclass and the individual operations of multiplication, addition, etc. was left to the subclasses.
add a comment |
If you look at the similarity of your various perform_xxx functions, it suggests there is duplication that could be removed. One way to do this would be to extract Operator classes, one for each operation, where the common behavior of extracting left and right values from the stream was expressed in the superclass and the individual operations of multiplication, addition, etc. was left to the subclasses.
add a comment |
If you look at the similarity of your various perform_xxx functions, it suggests there is duplication that could be removed. One way to do this would be to extract Operator classes, one for each operation, where the common behavior of extracting left and right values from the stream was expressed in the superclass and the individual operations of multiplication, addition, etc. was left to the subclasses.
If you look at the similarity of your various perform_xxx functions, it suggests there is duplication that could be removed. One way to do this would be to extract Operator classes, one for each operation, where the common behavior of extracting left and right values from the stream was expressed in the superclass and the individual operations of multiplication, addition, etc. was left to the subclasses.
answered May 3 '11 at 18:43
Carl Manaster
1,239812
1,239812
add a comment |
add a comment |
I don't write the expressions as you did, I use Reverse Polish Notation :
Exp : + 5 * 2 3 = 11 .... (5 + 2 * 3)
I would write :
Exp : 5 2 3 * + = 11 .... (5 + 2 * 3)
Another example of my writing :
10 2 * 15 3 * 5 - + .... (10 * 2 + (15 * 3 - 5)) = 20 + (45 - 5) = 20 + 40 = 60
It is better to write the same way you enter in the RPN calculator , such as HP 12C
New contributor
add a comment |
I don't write the expressions as you did, I use Reverse Polish Notation :
Exp : + 5 * 2 3 = 11 .... (5 + 2 * 3)
I would write :
Exp : 5 2 3 * + = 11 .... (5 + 2 * 3)
Another example of my writing :
10 2 * 15 3 * 5 - + .... (10 * 2 + (15 * 3 - 5)) = 20 + (45 - 5) = 20 + 40 = 60
It is better to write the same way you enter in the RPN calculator , such as HP 12C
New contributor
add a comment |
I don't write the expressions as you did, I use Reverse Polish Notation :
Exp : + 5 * 2 3 = 11 .... (5 + 2 * 3)
I would write :
Exp : 5 2 3 * + = 11 .... (5 + 2 * 3)
Another example of my writing :
10 2 * 15 3 * 5 - + .... (10 * 2 + (15 * 3 - 5)) = 20 + (45 - 5) = 20 + 40 = 60
It is better to write the same way you enter in the RPN calculator , such as HP 12C
New contributor
I don't write the expressions as you did, I use Reverse Polish Notation :
Exp : + 5 * 2 3 = 11 .... (5 + 2 * 3)
I would write :
Exp : 5 2 3 * + = 11 .... (5 + 2 * 3)
Another example of my writing :
10 2 * 15 3 * 5 - + .... (10 * 2 + (15 * 3 - 5)) = 20 + (45 - 5) = 20 + 40 = 60
It is better to write the same way you enter in the RPN calculator , such as HP 12C
New contributor
edited 4 mins ago
New contributor
answered 16 mins ago
Marcio Vale
11
11
New contributor
New contributor
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f2211%2fpolish-notation-interpreter-calculator%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
You've described Polish notation, not Reverse Polish notation.
– 200_success
Jan 26 '14 at 20:50