198 lines
8.7 KiB
C
198 lines
8.7 KiB
C
/*
|
||
Program: evalExpression-v1.c (Report comments/bugs to chikh@yuntech.edu.tw)
|
||
Function: 整合書範例infixTopostfix.c程式與第三章末習題第四題的要求,使得程式可接受使用者
|
||
從鍵盤輸入中序表示式字串,隨即顯示對應的後序表示式,再進行運算並顯示結果值。
|
||
為利實作求值程序,運算子與運算元之間均加入空格隔開,且後序式有螢幕顯示與字串內部
|
||
表示二個版本,二者幾乎相同,僅+-符號當作一元運算子用途時,字串內部將分別改以@_表
|
||
之,如此設計將有助於分辨應從堆疊彈出一個運算元或彈出二個運算元進行運算
|
||
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
|
||
1+(3*(50.2-3.8)/2^2.5)/12+15*6.5
|
||
1+(3*(-50.2+3.8)*2^-2.5)/12+15*-6.5
|
||
*/
|
||
|
||
#include <stdio.h> /* for scanf(), printf(), sscanf(), sprintf() */
|
||
#include <math.h> /* for pow() */
|
||
#include <ctype.h> /* 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); /* 中序式轉後序式函數,轉換結果存於postExpr */
|
||
printf("\n內部所用的後序式:%s",postExpr); /* 把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);
|
||
//printf("\t(正確值=%g)\n\n",1+(3*(50.2-3.8)/pow(2,2.5))/12+15*6.5);
|
||
//printf("\t(正確值=%g)\n\n",1+(3*(-50.2+3.8)*pow(2,-2.5))/12+15*-6.5);
|
||
|
||
return 0;
|
||
}
|
||
|
||
void infix_to_postfix(char infix_q[], int index, char postExpr[]) /* 中序式轉後序式函數,轉換結果存於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); /* 現有postExpr尾端加一空白 */
|
||
while (stack_t[top] != '(') {
|
||
printf("%c ",c=stack_t[top--]); /* 彈出堆疊頂端的運算子,存入c並顯示於螢幕 */
|
||
sprintf(postExpr,"%s%c ",postExpr,c); /* 把c加入postExpr尾並加一空白 */
|
||
}
|
||
top--; /* 把'('一併移除 */
|
||
break;
|
||
|
||
case '#': /* 輸入為#,代表已掃描到最末字元,於是把堆疊內尚未輸出的運算子通通彈出 */
|
||
printf(" "); /* 螢幕顯示加入空白間隔 */
|
||
sprintf(postExpr,"%s ",postExpr); /* postExpr後序式字串尾再加一空白 */
|
||
while (stack_t[top] != '#') {
|
||
printf("%c ",c=stack_t[top--]); /* 迴圈內這二行指令的邏輯與上面第73、74行相同 */
|
||
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--]); /* 迴圈內這二行指令的邏輯與上面第73、74行相同 */
|
||
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; /* tag改設為2,標示為特殊狀況,將使得緊接著的運算元輸出之後,隨即把+-號彈出 */
|
||
}
|
||
else { /* 只要tag不為1,則進行一般的處理方式。因+-運算的優先權較低,故把堆疊內優先權較高的運算子彈出 */
|
||
printf(" ");
|
||
sprintf(postExpr,"%s ",postExpr);
|
||
while (compare(stack_t[top],infix_q[ctr])) {
|
||
printf("%c ",c=stack_t[top--]); /* 迴圈內這二行指令的邏輯與上面第73、74行相同 */
|
||
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]); /* 以ctr為開始位置,+i決定下一個該讀取的字元位置,把該字元讀出存入c並顯示於螢幕 */
|
||
sprintf(postExpr,"%s%c",postExpr,c);/* 把c加入postExpr尾,注意不加空白間隔 */
|
||
}
|
||
ctr += (i-1); /* 運算元已處理妥,調整ctr至運算元之後的下一個字元位置 */
|
||
if (tag == 2) { /* tag設為2,意味著+-運算子為一元用途,於是把存放在堆疊的+-符號輸出 */
|
||
printf(" %c ",c=stack_t[top--]); /* 彈出堆疊頂端的元素,存為c並顯示於螢幕 */
|
||
sprintf(postExpr,"%s %c ",postExpr,c=='-'?'_':'@'); /* 這裡耍把戲;檢視c是否為-號,若是,則改設'_'於postExpr的尾端,否則把'@'設入後序式 */
|
||
}
|
||
tag = 0; /* 一旦輸出過運算元,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 *str) /* 依輸入的後序式字串,進行運算求值 */
|
||
{
|
||
char token[16];
|
||
float stack[MAX], operand, a, b, t; /* stack[]為另一個堆疊,求值運算過程中暫存運算元之用 */
|
||
int top = -1;
|
||
|
||
for (; *str; str++) { /* 以迴圈逐一檢視str內所有字元,只要目前檢視的字元不為'\0',則執行底下動作 */
|
||
if (*str == ' ') /* 若為空白字元,則略過 */
|
||
continue;
|
||
else if (isdigit(*str)) { /* 若當下檢視的字元為數字,意味緊接連續幾個字元對應一個運算元 */
|
||
sscanf(str,"%f%1000[^\n]",&operand,str); /* 為了讀取完整的運算元,以符點數的格式讀入operand,剩餘的字串再設為str */
|
||
stack[++top] = operand; /* 運算元operand推入堆疊 */
|
||
}
|
||
else { /* 當下檢視的字元為運算子 */
|
||
b = stack[top--]; /* stack彈出元素,為最新推入的運算元 */
|
||
a = stack[top--]; /* stack彈出元素,為次新運算元 */
|
||
switch (*str) { /* 檢視當下字元代表哪一種運算 */
|
||
case '+':
|
||
t = a+b; /* 加法運算結果設為t */
|
||
break;
|
||
case '-':
|
||
t = a-b; /* 減法運算結果設為t */
|
||
break;
|
||
case '@':
|
||
stack[++top] = a; /* 多取出的資料推回堆疊,因為一元運算,注意應把a(而非b)推回堆疊 */
|
||
t = b;
|
||
break;
|
||
case '_':
|
||
stack[++top] = a; /* 多取出的資料推回堆疊,因為一元運算 */
|
||
t = -b; /* negate b */
|
||
break;
|
||
case '*':
|
||
t = a*b; /* 乘法運算結果設為t */
|
||
break;
|
||
case '/':
|
||
t = (float)a/b; /* 除法運算結果設為t */
|
||
break;
|
||
case '^':
|
||
t = (float)pow(a,b); /* 指數運算結果設為t */
|
||
break;
|
||
}
|
||
stack[++top] = t; /* 新得到的t值推入堆疊 */
|
||
}
|
||
}
|
||
|
||
return stack[top]; /* 整個程序完畢,堆疊頂端保有運算最終結果值 */
|
||
}
|