研读 Bjarne Stroustrup 的 <>,才看到大一时徐老让我们做的一个计算器的升级版本。从前用的是什么胸牙利表达式还是波兰表达式,总之不是正常人类喜欢的描述。
这回的代码是在sourceforge找到的,读懂了以后我加了些中文注释,有可能你看起来嫌我罗嗦了。但如我一般愚笨的人解读这段程序一般都需要全神投入的花一个小时以上,所以趁着刚懂,写下来。
我认为从中(当然还有读了前9章B.S的书)学到了这些:
1.名字空间的使用
在学校上课的时候从来没有注意过。
2.使用enum
再也不要用什么宏定义,我们需要将一类具有特征的玩意儿组织起来。
3.使用标准库里的map
学C++课的时候谁也没有告诉过我有这种好东西。好像我们上完C++课程只知道了类是什么,还知道了一个比printf更简单的cout,就再也没有多明白些东西。
4.引用,引用
引用(&)其实是很有用的,就拿这个程序里对map索引时来说。
5.error的管理
完全用error来输出错误信息是一个很好的方法。看上去更清晰了。
当然书中介绍了还可以把这些代码组织成好几个namespace,过几天有精力再试一下吧。还有,谁能告诉我怎么用键盘输入一个字符串结束符?
代注释的源码:
/* Reference Chapter 6: "The C++ Programming Language", Special Edition. Bjarne Stroustrup,Addison-Wesley Pub Co; 3 edition (February 15, 2000) ISBN: 0201700735
Also reference:
*/
#include <iostream> #include <sstream> #include <string> #include <map> #include <cctype> using namespace std;
enum Token_value { NAME, NUMBER, END, INC, DEC, PLUS='+', MINUS='-', MUL='*', DIV='/', PRINT=';', ASSIGN='=', LP='(', RP=')' };
Token_value curr_tok = PRINT; // 全局变量curr_tok用来表示当前读到的内容的类型
double number_value; // 全局变量number_value用来记录当前读到的变量值
string string_value; // 全局变量string_value用来记录当前读到的变量名
double expr(bool ); // expr(bool)返回一个带'+'或'-'表达式的值,
double term(bool ); // term(bool)返回一个将要读到的带'*'或'/'的表达式的值
double prim(bool ); // prim(bool)返回一个将要读到的优先级高于乘除法的表达式的值
// 以上三个函数用true调用表示不需要往前读,
// 用false调用表示要在cin里读一个“匹配式”
double error(const string&);
Token_value get_token(); // 函数get_token()用来读一个完整的“匹配式”,
// 然后向curr_tok赋值(如果是数值或变量名的话
// 还有number_value和string_value)
map<string, double> table;
double expr(bool get) { // 第一次调用用false,以后都用true
double left = term(get);
for(;;) { switch(curr_tok) { case PLUS: left += term(true); break; case MINUS: left -= term(true); break; default: return left; } } }
double term(bool get) { double left = prim(get);
for(;;) { switch(curr_tok) { case MUL: left *= prim(true); break; case DIV: if (double d = prim(true)) { left /=d; break; } return error("divide by 0"); default: return left; } } }
double prim(bool get) { if(get) get_token(); //如果用false调用,则不用再读字符
switch(curr_tok) { case NUMBER: //若读到数字v,则再往前读,并且返回v
{ double v = number_value; get_token(); return v; } case NAME: //若读到变量名
{ double& v = table[string_value]; get_token(); //往前读
if (curr_tok == ASSIGN) //若遇到等号
v = expr(true); //则给v赋值(调用expr(true)得到等号后面的表达式的值)
if (curr_tok == INC) //r++;
v = v+1; if (curr_tok == DEC) //r++;
v = v-1; return v; } case INC: { return prim(true)+1; } case DEC: { return prim(true)-1; } case MINUS: { return -prim(true); } case LP: { double e=expr(true); if(curr_tok != RP) return error(" ')' expected"); get_token(); return e; } default: return error("primary expected"); } }
Token_value get_token() { char ch; // 不断的读新的字符直到结束符或回车
do { if(!cin.get(ch)) { return curr_tok = END; } } while(ch!='\n' && isspace(ch));
switch(ch) { case ';': case '\n': { return curr_tok=PRINT; } case 0: { return curr_tok=END; } case '+': // added for ++name and name++ //检查是否自增或自减的情况
{ char c; if ( cin.get(c) && c == '+' ) { return curr_tok=INC; }
cin.putback(c); { return curr_tok=Token_value(ch); } } case '-': { char c; if ( cin.get(c) && c == '-' ) { return curr_tok=DEC; }
cin.putback(c); { return curr_tok=Token_value(ch); } } case '*': case '/': case '(': case ')': case '=': { return curr_tok=Token_value(ch); } //若是加、减、乘、除或括号,就告诉curr_tok这是一个"操作符"
//(转换成Token_value的枚举类型)
case '0':case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': cin.putback(ch); cin >> number_value; return curr_tok=NUMBER; //若是数字,则告诉curr_tok这是一个NUMBER,
//再将数字的值读入number_value
default: if (isalpha(ch)) { string_value=ch; while(cin.get(ch) && isalnum(ch)) string_value.push_back(ch); cin.putback(ch); return curr_tok=NAME; } //若长得像一个变量,则告诉curr_tok这是一个NAME
//再将变量名读入string_value
error("bad token"); return curr_tok=PRINT; //否则就是一个错误的语法
} }
int no_of_errors;
double error(const string& s) { no_of_errors++; cerr << "error: " << s << '\n'; return 1; }
istream* input;
int main(int argc, char* argv[]) { switch(argc) { case 1: input = &cin; break; case 2: input = new istringstream(argv[1]); break; default: error("too man arguments"); return 1; }
table["pi"] = 3.1415926535897932385; table["e"] = 2.718281818284590452354;
while(cin) { get_token(); if( curr_tok == END) break; if( curr_tok == PRINT) continue; cout << expr(false) << '\n'; }
if(input != &cin) delete input; return no_of_errors; }
|
阅读(1494) | 评论(1) | 转发(0) |