順序棧的應(yīng)用五:表達(dá)式求值

表達(dá)式求值

這里介紹一種簡(jiǎn)單直觀的“算符優(yōu)先法”。

要對(duì)一個(gè)表達(dá)式求值葫掉,首先要能夠正確解釋表達(dá)式稍刀。
例如表悬,要求對(duì)下面的算術(shù)表達(dá)式求值:
4+2*3-10/5

首先要了解算術(shù)四則運(yùn)算的規(guī)則。即:
(1)先乘除沼侣,后加減
(2)從左算到右
(3)先括號(hào)內(nèi)祖能,后括號(hào)外。

由此蛾洛,這個(gè)算術(shù)表達(dá)式的計(jì)算順序應(yīng)為:
4 + 2 * 3 - 10 / 5 = 4 + 6 - 10 / 5 = 10 - 10 / 5 = 10 - 2 =8

算符優(yōu)先法就是根據(jù)這個(gè)運(yùn)算關(guān)系的規(guī)定來(lái)實(shí)現(xiàn)對(duì)表達(dá)式的編譯或解釋執(zhí)行的养铸。
任何一個(gè)表達(dá)式都是由操作數(shù)(operand)、運(yùn)算符(operator)和界限符(delimiter)組成的轧膘。
一般的钞螟,操作數(shù)既可以是常數(shù)也可以是被說(shuō)明為變量或常量的標(biāo)識(shí)符;
運(yùn)算符可以分為算術(shù)運(yùn)算符谎碍、關(guān)系運(yùn)算符和邏輯運(yùn)算符3類(lèi)鳞滨;
基本界限符有左右括號(hào)和表達(dá)式結(jié)束符等。

我們把運(yùn)算符和界限符統(tǒng)稱(chēng)為算符蟆淀,他們構(gòu)成的集合命名為OP拯啦。根據(jù)上述3條運(yùn)算規(guī)則,在運(yùn)算的每一步中熔任,任意兩個(gè)相繼出現(xiàn)的算符a和b之間的優(yōu)先關(guān)系至多是下面三種關(guān)系之一:
a<b:a的優(yōu)先級(jí)低于b
a=b:a的優(yōu)先級(jí)等于b
a>b:a的優(yōu)先級(jí)大于b

下表給出了部分a褒链、b運(yùn)算符之間的優(yōu)先級(jí)關(guān)系(列為a運(yùn)算符,行為b運(yùn)算符):

+ - * / % ( )
+ > > < < < < >
- > > < < < < >
* > > > > > < >
/ > > > > > < >
% > > > > > < >
( < < < < < < =
) > > > > > N N

表達(dá)式中的‘=’表示當(dāng)左右括號(hào)相遇時(shí)笋敞,括號(hào)內(nèi)的運(yùn)算完成碱蒙,此時(shí)要把左右括號(hào)從表達(dá)式中及時(shí)脫離。
表達(dá)式中的'N'表示這種狀態(tài)如果出現(xiàn)了夯巷,則表達(dá)式格式有誤赛惩,一定是左右括號(hào)不匹配。

在代碼中趁餐,我給出了上述7種運(yùn)算符

char opetr_char[7]={'+','-','*','/','%','(',')'};

首先是要判斷一個(gè)字符屬不屬于運(yùn)算符喷兼,

int isOpetrChar(char ch){
    int i;
    for(i=0;i<7;i++){
        if(ch ==  opetr_char[i]) return i;
    }
    return -1;
}

上表的優(yōu)先級(jí)用一個(gè)char型的二維數(shù)組直觀表示:

char opetr_priority[7][7]={
    {'>','>','<','<','<','<','>'},
    {'>','>','<','<','<','<','>'},
    {'>','>','>','>','>','<','>'},
    {'>','>','>','>','>','<','>'},
    {'>','>','>','>','>','<','>'},
    {'<','<','<','<','<','<','='},
    {'>','>','>','>','>','N','N'}
};

那么,比較兩個(gè)運(yùn)算符的優(yōu)先級(jí)可以簡(jiǎn)單得出:

char getOpetrPrior(char a,char b){
    int i = isOpetrChar(a);
    int j = isOpetrChar(b);
    return opetr_priority[i][j];
}

實(shí)現(xiàn)算符優(yōu)先算法后雷,可以使用兩個(gè)棧季惯,一個(gè)optr_stack吠各,用于存放運(yùn)算符,一個(gè)opnd_stack勉抓,用于存放操作數(shù)贾漏。
算法的核心思想是,依次讀入表達(dá)式中每個(gè)字符藕筋,若是操作數(shù)則進(jìn)opnd_stack纵散,若是運(yùn)算符則與optr_stack的棧頂操作符比較優(yōu)先權(quán)后作相應(yīng)操作,直至整個(gè)表達(dá)式求值完畢隐圾。

EvaluateExpression.c利用了前面的C封裝的順序棧對(duì)象 用線性表表示的順序棧

實(shí)現(xiàn)了輸入表達(dá)式伍掀,顯示每一步的計(jì)算過(guò)程,并輸出最終結(jié)果的功能暇藏。
且支持表達(dá)式格式檢測(cè)蜜笤,支持多重括號(hào)結(jié)構(gòu)、負(fù)數(shù)運(yùn)算(‘-’號(hào)既可能為減號(hào)盐碱,也可能為負(fù)號(hào))把兔。

github源碼

EvaluateExpression.c文件

#include <stdio.h>
#include <malloc.h>
#include "LinearListStack.h"

char opetr_char[7]={'+','-','*','/','%','(',')'};

//operational character priority (a,b),operational character b after a.
//   b: +  -  *  /  %  (  )
// a:
// +    >  >  <  <  <  <  >
// -    >  >  <  <  <  <  >
// *    >  >  >  >  >  <  >
// /    >  >  >  >  >  <  >
// %    >  >  >  >  >  <  >
// (    <  <  <  <  <  <  =
// )    >  >  >  >  >  N  N

char opetr_priority[7][7]={
    {'>','>','<','<','<','<','>'},
    {'>','>','<','<','<','<','>'},
    {'>','>','>','>','>','<','>'},
    {'>','>','>','>','>','<','>'},
    {'>','>','>','>','>','<','>'},
    {'<','<','<','<','<','<','='},
    {'>','>','>','>','>','N','N'}
};

int isOpetrChar(char ch){
    int i;
    for(i=0;i<7;i++){
        if(ch ==  opetr_char[i]) return i;
    }
    return -1;
}

char getOpetrPrior(char a,char b){
    int i = isOpetrChar(a);
    int j = isOpetrChar(b);
    return opetr_priority[i][j];
}

double my_pow(double a,int b){
    int s = 0,i;
    double r = 1;
    if(b == 0) return 1;
    if(b<0){
        b*=-1;
        s = 1;
    }
    for(i = 0; i < b; i++){
        r *= a;
    }
    if(s) r = 1/r;
    return r;
}

int getOpndNumFromStack(LinearListStack *opnd_stack){
    int num = 0,i = 0;
    char elem;
    if(opnd_stack->length(opnd_stack)){
        opnd_stack->pop(opnd_stack,&elem);
        if(elem == ' '){
            while(elem == ' '){
                if(opnd_stack->length(opnd_stack) == 0) break;//確保不會(huì)pop空棧
                opnd_stack->pop(opnd_stack,&elem);
            }
        }
        if(elem != ' '){ //兩個(gè)判斷必須分開(kāi)來(lái)寫(xiě)
            while(elem != ' ' && elem != '-'){
                num += my_pow(10,i)*(elem - 0x30);
                if(opnd_stack->length(opnd_stack) == 0) break; //確保不會(huì)pop空棧
                opnd_stack->pop(opnd_stack,&elem);
                i++;
            }
            if(elem == '-'){ //如果是負(fù)號(hào)
                num = -num;
            }else if(elem == ' '){  //將移出的空格間隔符再補(bǔ)進(jìn)去
                opnd_stack->push(opnd_stack,&elem);
            }
        }
    }
    return num;
}

int operate(int number_a,char opetr_char,int number_b){
    int result = 0;
    switch(opetr_char){
        case '+':
            result = number_a + number_b;
            break;
        case '-':
            result = number_a - number_b;
            break;
        case '*':
            result = number_a * number_b;
            break;
        case '/':
            result = number_a / number_b;
            break;
        case '%':
            result = number_a % number_b;
            break;
        default:
            result = number_b;
            break;
    }
    return result;
}

void pushResultToStack(LinearListStack *opnd_stack, int result){
    char elem[10],n_elem;
    int i = 0,index = 0;
    if(result < 0){
        result = - result;
        n_elem = '-';
        opnd_stack->push(opnd_stack,&n_elem);
    }else if(result == 0){
        n_elem = '0';
        opnd_stack->push(opnd_stack,&n_elem);
    }
    while(result > 0){
        elem[index] = (result % 10) + 0x30;
        result /= 10;
        index++;
    }
    for(i=index;i>0;i--){
        opnd_stack->push(opnd_stack,&elem[i-1]);
    }
}

LinearListStack *evaluateExpression(char *str){
    char elem,opetr_prior,opetr_char;
    int cal_result = 0,number_a,number_b;
    int num_before_flag = 0;
    LinearListStack *optr_stack = InitLinearListStack(); //運(yùn)算符棧
    LinearListStack *opnd_stack = InitLinearListStack(); //操作數(shù)棧
    while(*str != '\0'){
        if(isOpetrChar(*str) != -1){
            if(optr_stack->length(optr_stack)){
                optr_stack->getTop(optr_stack,&elem);
                opetr_prior = getOpetrPrior(elem,*str);
                switch(opetr_prior){
                    case '<': //棧頂運(yùn)算符優(yōu)先級(jí)低
                        if(num_before_flag == 0){ //前一個(gè)入棧的是符號(hào)
                            if(*str == '-'){ //此時(shí)'-'號(hào)表示為負(fù)號(hào)
                                opnd_stack->push(opnd_stack,str);//'-'號(hào)加入操作數(shù)棧
                                num_before_flag = 1; //加入的是數(shù)字
                            }else if(elem == '(' || *str == '('){ //多個(gè)括號(hào)與操作符相鄰的情況
                                optr_stack->push(optr_stack,str);  
                                elem = ' '; //數(shù)字字符加入空格間隔符
                                opnd_stack->push(opnd_stack,&elem);
                                num_before_flag = 0;//加入運(yùn)算符
                            }else{
                                printf("Expression format error!");
                                break;
                            }
                        }else{ //前面有數(shù)字入棧
                            optr_stack->push(optr_stack,str);  
                            elem = ' '; //數(shù)字字符加入空格間隔符
                            opnd_stack->push(opnd_stack,&elem);
                            num_before_flag = 0;//加入運(yùn)算符
                        }
                        break;
                    case '=':
                        optr_stack->pop(optr_stack,&elem);//脫括號(hào)
                        num_before_flag = 1; //脫括號(hào)等效為加入的是數(shù)字
                        break;
                    case '>': //棧頂運(yùn)算符優(yōu)先級(jí)高
                        if(num_before_flag == 0){ //前一個(gè)入棧的是符號(hào)
                            if(*str == '-'){ //此時(shí)'-'號(hào)表示為負(fù)號(hào)
                                opnd_stack->push(opnd_stack,str);//'-'號(hào)加入操作數(shù)棧
                                num_before_flag = 1; //加入的是數(shù)字
                            }else{
                                printf("Expression format error!");
                                break;
                            }
                        }else{ //前一個(gè)入棧的是數(shù)字
                            optr_stack->pop(optr_stack,&opetr_char);//取運(yùn)算符
                            number_b = getOpndNumFromStack(opnd_stack);
                            number_a = getOpndNumFromStack(opnd_stack);
                            cal_result = operate(number_a,opetr_char,number_b);
                            printf("%d",number_a);
                            printf(" %c ",opetr_char);
                            printf("%d = ",number_b);
                            printf("%d\n",cal_result);
                            pushResultToStack(opnd_stack, cal_result);
                            num_before_flag = 1; //加入的是數(shù)字
                            str--;
                        }
                        break;
                }
            }else{
                //第一個(gè)運(yùn)算符,此時(shí)運(yùn)算符棧是空的
                if(num_before_flag == 0){ //前面沒(méi)有數(shù)字入棧
                    if(*str == '-'){ //此時(shí)'-'號(hào)表示為負(fù)號(hào)
                        opnd_stack->push(opnd_stack,str);//'-'號(hào)加入操作數(shù)棧
                        num_before_flag = 1; //加入的是數(shù)字
                    }else if(*str == '('){
                        optr_stack->push(optr_stack,str);  
                        elem = ' '; //數(shù)字字符加入空格間隔符
                        opnd_stack->push(opnd_stack,&elem);
                        num_before_flag = 0;//加入運(yùn)算符
                    }else{
                        printf("Expression format error!");
                        break;
                    }
                }else{ //前面有數(shù)字入棧
                    optr_stack->push(optr_stack,str);  
                    elem = ' '; //數(shù)字字符加入空格間隔符
                    opnd_stack->push(opnd_stack,&elem);
                    num_before_flag = 0;//加入運(yùn)算符
                }
            }
        }else{
            if(*str >= 0x30 && *str <= 0x39){
                opnd_stack->push(opnd_stack,str);
                num_before_flag = 1; //加入的是數(shù)字
            }
        }
        str++;
    }
    if(*str == '\0'){ //輸入完成
        while(optr_stack->length(optr_stack)){
            optr_stack->pop(optr_stack,&opetr_char);//取運(yùn)算符
            if(isOpetrChar(opetr_char)<5){
                number_b = getOpndNumFromStack(opnd_stack);
                number_a = getOpndNumFromStack(opnd_stack);
                cal_result = operate(number_a,opetr_char,number_b);
                printf("%d",number_a);
                printf(" %c ",opetr_char);
                printf("%d = ",number_b);
                printf("%d\n",cal_result);
                pushResultToStack(opnd_stack, cal_result);
            }
        }
    }
    DestroyLinearListStack(optr_stack);
    return opnd_stack;
}

int main(void)
{
    int i;
    char string[1000];
    LinearListStack *optr_result = NULL;
    printf("please enter a expression:");
    gets(string);
    optr_result = evaluateExpression(string);
    printf("%s = ",string);
    optr_result->risePrint(optr_result);
    DestroyLinearListStack(optr_result);
    return 0;
}

編譯:

gcc LinearListStack.c LinearListStack.h EvaluateExpression.c -o EvaluateExpression

運(yùn)行EvaluateExpression:

please enter a expression:3+3+(4*5)
3 + 3 = 6
4 * 5 = 20
6 + 20 = 26
3+3+(4*5) = 26

please enter a expression:-2+3+4*5
-2 + 3 = 1
4 * 5 = 20
1 + 20 = 21
-2+3+4*5 = 21

please enter a expression:-2+(3+4)*-5
3 + 4 = 7
7 * -5 = -35
-2 + -35 = -37
-2+(3+4)*-5 = -37

please enter a expression:3+3+((4*5))
3 + 3 = 6
4 * 5 = 20
6 + 20 = 26
3+3+((4*5)) = 26

please enter a expression:-2+(3/4)*-5
3 / 4 = 0
0 * -5 = 0
-2 + 0 = -2
-2+(3/4)*-5 = -2

please enter a expression:-2+(3%4)*-5
3 % 4 = 3
3 * -5 = -15
-2 + -15 = -17
-2+(3%4)*-5 = -17
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末甸各,一起剝皮案震驚了整個(gè)濱河市垛贤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趣倾,老刑警劉巖聘惦,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異儒恋,居然都是意外死亡善绎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)诫尽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)禀酱,“玉大人,你說(shuō)我怎么就攤上這事牧嫉〖粮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵酣藻,是天一觀的道長(zhǎng)曹洽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)辽剧,這世上最難降的妖魔是什么送淆? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮怕轿,結(jié)果婚禮上偷崩,老公的妹妹穿的比我還像新娘辟拷。我一直安慰自己,他們只是感情好阐斜,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布衫冻。 她就那樣靜靜地躺著,像睡著了一般谒出。 火紅的嫁衣襯著肌膚如雪羽杰。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天到推,我揣著相機(jī)與錄音,去河邊找鬼惕澎。 笑死莉测,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的唧喉。 我是一名探鬼主播捣卤,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼八孝!你這毒婦竟也來(lái)了董朝?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤干跛,失蹤者是張志新(化名)和其女友劉穎子姜,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體楼入,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哥捕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了嘉熊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遥赚。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖阐肤,靈堂內(nèi)的尸體忽然破棺而出凫佛,到底是詐尸還是另有隱情,我是刑警寧澤孕惜,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布愧薛,位于F島的核電站,受9級(jí)特大地震影響诊赊,放射性物質(zhì)發(fā)生泄漏厚满。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一碧磅、第九天 我趴在偏房一處隱蔽的房頂上張望碘箍。 院中可真熱鬧遵馆,春花似錦、人聲如沸丰榴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)四濒。三九已至换况,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盗蟆,已是汗流浹背戈二。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喳资,地道東北人觉吭。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仆邓,于是被迫代替她去往敵國(guó)和親鲜滩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line)节值,也就是一...
    悟名先生閱讀 4,118評(píng)論 0 13
  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,367評(píng)論 0 5
  • 運(yùn)算符是處理數(shù)據(jù)的基本方法徙硅,用來(lái)從現(xiàn)有的值得到新的值。JavaScript 提供了多種運(yùn)算符搞疗,本章逐一介紹這些運(yùn)算...
    許先生__閱讀 596評(píng)論 0 3
  • 運(yùn)算符是處理數(shù)據(jù)的基本方法嗓蘑,用來(lái)從現(xiàn)有的值得到新的值。JavaScript 提供了多種運(yùn)算符贴汪,本章逐一介紹這些運(yùn)算...
    徵羽kid閱讀 659評(píng)論 0 0
  • ?1 C語(yǔ)言程序的結(jié)構(gòu)認(rèn)識(shí) 用一個(gè)簡(jiǎn)單的c程序例子脐往,介紹c語(yǔ)言的基本構(gòu)成、格式扳埂、以及良好的書(shū)寫(xiě)風(fēng)格业簿,使讀者對(duì)c語(yǔ)...
    CONLYOUC閱讀 8,696評(píng)論 9 66