/* Program: evalExpression-v2.c (Report comments/bugs to chikh@yuntech.edu.tw) Function: 整合書範例infixTopostfix.c程式與第三章末習題第四題的要求,使得程式可接受使用者 從鍵盤輸入中序表示式字串,隨即顯示對應的後序表示式,再依此進行運算並顯示結果值。 與evalExpression-v1.c功能相同,唯一差異在evaluate()函數的實作方式;此版以strtok() 作字串剖析,以空白作分隔字元,分離出所需的運算元與運算子。 Notes: 10+20*(50-30)/2^4-6*8 10+20*(-50+30)/-2^4-6*8 10.5+20.8*(50.1-30.6)/2.5^4-6.6*8.2 10.5+20.8*(-50.1+30.6)/2.5^-4-6.6*8.2 10-30*(+50-20)/-2^-4+7.5*-6 10+30*(+50.2+3.8)/+2^2.5+15*-6.5 */ #include #include /* for strtok() */ #include /* for pow() */ #include /* for isdigit() */ #define MAX 120 void infix_to_postfix(char [], int, char []); /* 由中序轉後序函數 */ int compare(char, char); /* 比較兩個運算子函數 */ float evaluate(char []); /* 新訂的函數,為後序式實作所需的數學運算並回傳結果值 */ /* 在中序表示法佇列及暫存堆疊中,運算子的優先順序表,其優先值為INDEX/2 */ char infix_priority[9] = {'#', ')', '+', '-', '*', '/', '^', '('}; char stack_priority[8] = {'#', '(', '+', '-', '*', '/', '^'}; int main() { int i, index = -1; char infix_q[MAX], postExpr[2*MAX] = {0}; /* 儲存使用者輸入中序式字元串,轉換為後序式字串存於postExpr */ printf("\n請輸入中序表示式(支援+-*/^()運算) => "); while (infix_q[index] != '\n') infix_q[++index] = getchar(); infix_q[index] = '#'; /* 原輸入的最後一個字元取代為'#',作為運算式字串結尾 */ printf("\n對應的後序表示式:"); infix_to_postfix(infix_q,index,postExpr); //printf("\n --> %s\n",postExpr); printf("\n\n運算結果=%g",evaluate(postExpr)); //printf("\t(正確值=%g)\n\n",10+20*(50-30)/pow(2,4)-6*8); //printf("\t(正確值=%g)\n\n",10+20*(-50+30)/pow(-2,4)-6*8); printf("\t(正確值=%g)\n\n",10.5+20.8*(50.1-30.6)/pow(2.5,4)-6.6*8.2); //printf("\t(正確值=%g)\n\n",10.5+20.8*(-50.1+30.6)/pow(2.5,-4)-6.6*8.2); //printf("\t(正確值=%g)\n\n",10-30*(+50-20)/pow(-2,-4)+7.5*-6); //printf("\t(正確值=%g)\n\n",10+30*(+50.2+3.8)/pow(2,2.5)+15*-6.5); return 0; } void infix_to_postfix(char infix_q[], int index, char postExpr[]) { int top = 0, ctr, tag = 1, i = 0; char c, stack_t[MAX]; /* 儲存還不必輸出的運算子 */ stack_t[top] = '#'; /* 於堆疊最底下加入#為結束符號 */ for (ctr = 0; ctr <= index; ctr++) { /* 運算式視為字元串,從第0字元開始,逐字掃描,直至最後一個字元為止 */ switch (infix_q[ctr]) { /* 檢視ctr索引值對應的字元內容,查看是否吻合底下任一情況 */ case ')': /* 輸入為')',將輸出堆疊內運算子,直到彈出'('為止 */ printf(" "); /* 螢幕顯示加入空白間隔 */ sprintf(postExpr,"%s ",postExpr); while (stack_t[top] != '(') { printf("%c ",c=stack_t[top--]); sprintf(postExpr,"%s%c ",postExpr,c); } top--; /* 把'('一併移除 */ break; case '#': /* 輸入為#,代表已掃描到最末字元,於是把堆疊內尚未輸出的運算子通通彈出 */ printf(" "); sprintf(postExpr,"%s ",postExpr); /* 導入空白於後序式字串 */ while (stack_t[top] != '#') { printf("%c ",c=stack_t[top--]); sprintf(postExpr,"%s%c ",postExpr,c); } break; case '(': case '^': case '*': case '/': printf(" "); sprintf(postExpr,"%s ",postExpr); while (compare(stack_t[top],infix_q[ctr])) { printf("%c ",c=stack_t[top--]); sprintf(postExpr,"%s%c ",postExpr,c); } stack_t[++top] = infix_q[ctr]; tag = 1; break; case '+': case '-': if (tag == 1) { /* 判斷 '(' '^' '*' '/' 後的 +- 號是否表示正負的一元運算子 */ stack_t[++top] = infix_q[ctr]; tag = 2; } else { printf(" "); sprintf(postExpr,"%s ",postExpr); while (compare(stack_t[top],infix_q[ctr])) { printf("%c ",c=stack_t[top--]); sprintf(postExpr,"%s%c ",postExpr,c); } stack_t[++top] = infix_q[ctr]; tag = 1; } break; case ' ': break; /* do nothing,直接略過 */ /* 輸入為運算元,則直接輸出 */ default: for (i = 0; isdigit(infix_q[ctr+i]) || infix_q[ctr+i]=='.'; i++) { printf("%c",c=infix_q[ctr+i]); sprintf(postExpr,"%s%c",postExpr,c); } ctr += (i-1); if (tag == 2) { /* 將存放在堆疊的負號輸出 */ printf(" %c ",c=stack_t[top--]); /* 視同彈出堆疊頂端的元素 */ sprintf(postExpr,"%s %c ",postExpr,c=='-'?'_':'@'); } tag = 0; break; } } } /* 比較兩運算子優先權,若輸入運算子小於等於堆疊中運算子,則傳回值為 1,否則為 0 */ int compare(char stack_o, char infix_o) { int index_s = 0, index_i = 0; while (stack_priority[index_s] != stack_o) index_s++; while (infix_priority[index_i] != infix_o) index_i++; return index_s/2 >= index_i/2 ? 1 : 0; } float evaluate(char expr[]) { char *token; float stack[MAX], a, b, c; int top = -1; token = strtok(expr," "); /* 取得第一個符記 */ while (token != NULL) { /* 若不為空,則嘗試繼續剖析字串 */ if (atof(token)!=0.0) stack[++top] = atof(token); else { b = stack[top--]; a = stack[top--]; switch(token[0]) { case '+': c = a+b; break; case '-': c = a-b; break; case '@': stack[++top] = a; /* 多取出的資料推回堆疊,因為一元運算 */ c = b; break; case '_': stack[++top] = a; /* 多取出的資料推回堆疊,因為一元運算 */ c = -b; break; case '*': c = a*b; break; case '/': c = (float)a/b; break; case '^': c = (float)pow(a,b); break; } stack[++top] = c; } token = strtok(NULL," "); /* 很特別的寫法,請同學多加留意 */ } return stack[top]; }