2020面向對象程序設計寒假作業1 題解


作業描述 詳情
這個作業屬於哪個課程 班級鏈接
這個作業要求在哪里 作業要求
這個作業的目標 問答題:
1-1. 如果你不了解C++請回答以下問題:你認為C語言有什么缺陷(你覺得哪里用的不順手)。
1-2. 如果你已經了解C++請回答以下問題:你覺得C++和C語言比有什么優點。
2. 查閱相關資料,簡述一下C語言/C++的編譯過程。

實踐題:
1. 查看自己的C++編譯器版本。
2. 使用命令行編譯一份C語言/C++代碼。

編程題:
編寫一個程序,輸入滿足以下語法要求的一段文字,輸出運行后的結果。
變量定義:          整數 錢包 等於 零
運算(加法):    錢包 增加 四
運算(減法):    錢包 減少 四
輸出:                看看 錢包
作業正文 2020面向對象程序設計寒假作業1 題解
其他參考文獻 C++ 編譯過程簡介

問答題

  1. 因為本人是轉專業進來的學生,以前奧賽也未學習C語言,所以很抱歉,無法完成比較。

  2. 查詢了相關資料后,發現C/C++的編譯過程分為以下步驟:(1)預處理 (2)編譯 (3)匯編 (4)鏈接

其中,預處理階段將先讀取原程序,再處理偽指令,接着刪除所有注釋,然后添加行號和文件名標識,最后保留所有的 #pragma 編譯器指令。

而編譯過程將預處理完的文件進行一系列詞法分析、語法分析、語義分析及優化后,產生相應的匯編代碼文件。

匯編過程將編譯完的匯編代碼文件翻譯成機器指令,並生成可重定位目標程序(*.o)文件,該文件為二進制文件,字節編碼是機器指令。

最后的鏈接過程通過鏈接器將一個個目標文件(*.obj)與庫文件(*.lib)鏈接在一起生成一個完整的可執行程序。

因為正常而言,一個比較完備、大型的可執行程序,其代碼量較多,很難將所有的代碼寫在一個 cpp 內,或者寫在一些頭文件內。一是在於這么寫很容易產生命名上的沖突以及宏定義上的沖突,而且加大了代碼的冗余量;二則在於如此大量的代碼,編譯速度極慢,當程序出現錯誤,需要 debug 時,將會耗費大量的時間在編譯上,降低了 debug 的效率;三則這樣的寫法不利於模塊化處理程序,當程序需求需要修改時,需要耗費極大量的時間在修改代碼上。

實踐題

1.查看自己的C++編譯器版本

Windows+R 打開運行窗口

輸入 cmd 進入命令行

輸入 g++ -v 查看版本

2.使用命令行編譯一份 C++ 代碼

Windows+R 打開運行窗口

輸入 cmd 進入命令行

使用 cd 指令進入代碼所在的文件夾

使用指令 g++ {file}.cpp 編譯文件,生成 a.exe 文件

編程題

前言:又遇到了最討厭的模擬題(碼力低是真的傷不起啊),幸好最后被我調出來了......

預處理

首先,作為設計人員,很重要的是要清楚用戶可能輸入什么。而對於非法的輸入一定要學會處理,而不是任由程序崩潰

所以,我先想到的就是設計錯誤拋出:

inline void error_output(){//錯誤拋出
    puts("輸入格式錯誤");
}

其次,題目保證了輸入的代表數字的漢字一定為零-十,關鍵字保證只有以下五類:
整數:定義新變量
等於:對變量賦值

整數...等於...:定義新變量並賦初值

增加:實現加法
減少:實現減法
看看:輸出變量值

於是,我們先把他們放到map中,初始化,方便后面處理:

vector<int> var_ar;
map<string,int> read_gbk,comd,var_id;
map<int,string> output_gbk;
/*
var_ar變量值
var_id從變量名到變量地址的映射
read_gbk將漢字轉為數字
output_gbk將數字轉成漢字
*/

inline void pre(){//初始化設置
    read_gbk["零"]=0;
    read_gbk["一"]=1;
    read_gbk["二"]=2;
    read_gbk["三"]=3;
    read_gbk["四"]=4;
    read_gbk["五"]=5;
    read_gbk["六"]=6;
    read_gbk["七"]=7;
    read_gbk["八"]=8;
    read_gbk["九"]=9;
    read_gbk["十"]=10;

    output_gbk[0]="零";
    output_gbk[1]="一";
    output_gbk[2]="二";
    output_gbk[3]="三";
    output_gbk[4]="四";
    output_gbk[5]="五";
    output_gbk[6]="六";
    output_gbk[7]="七";
    output_gbk[8]="八";
    output_gbk[9]="九";
    output_gbk[10]="十";

    comd["增加"]=1;
    comd["減少"]=-1;
    comd["等於"]=2;
    comd["看看"]=3;
    comd["整數"]=4;
}

由於變量個數未知,我使用了vector來儲存變量的值,開一個map作為變量名到它在vector中地址的映射

主體

好的,預處理部分結束,我們來講主體部分

主體功能較簡單,無非以下工作:
輸入語句、分析與執行語句、輸出

由於語句條數未知,本人使用 while 循環來執行:

    while( getline(cin,s) ) carry(s);//讀入、分析並執行指令

其中 carry(string) 函數為分析與執行語句的主體

carry(string) 函數中包括了對輸入的語句進行分析

首先,合法的語句一定是含有空格的,我們把語句按空格分成前半部分和后半部分;否則記得錯誤的拋出

前半部分如果不是指令,則是已經申請的變量,否則也是需要拋出的錯誤

而這樣一來,后面跟着的一定是 加、減、賦值的語句+空格+值 的形式,而值可能是已申請的變量或者數字對應的漢字

一樣先分析是否含有空格,否則拋出;然后讀取前半部分是否是加、減、賦值的語句,否則拋出;接着判斷是否最后那個是值,否則拋出;最后進行相應的運算

當然,判斷前半部分是否是那三種指令之前,更要先判斷是否是合法指令。如果連合法指令都不算,就更不要說那三種指令了。

實現起來代碼如下:

if( !iscomd(tmp) ){//不是指令作為開頭
        if( var_id.find(tmp)==var_id.end() ){//也不是變量(例如數字),或變量還未申請,語句非法
            error_output();
            return ;
        }

        id=var_id[tmp];//變量的地址
        if( s.find(" ")==string::npos ){//不含空格,語句非法
            error_output();
            return ;
        }

        tmp=s.substr(0, s.find(" ") );
        s=s.substr( s.find(" ")+1 );

        if( !iscomd(tmp) ){//不是指令,語句非法
            error_output();
            return ;
        }
        
        int command=comd[tmp];
        if(command!=-1&&command!=1&&command!=2){//不是加減或者賦值,語句非法
            error_output();
            return ;
        }

        int num=number(s);
        if(num<0){//語句非法
            error_output();
            return ;
        }
        if(command==-1) var_ar[id]-=num;
        else if(command==1) var_ar[id]+=num;
        else if(command==2) var_ar[id]=num;
    }

而判斷是否是值,我用一個函數 number 進行了模塊化處理:有限判定是否是數字對應的漢字;如果不是,再判定是否是已申請的變量;最后返回的如果是非負數,則代表值

inline int number(string s){//讀取用於運算的數字或變量
    int num=gbk_read(s);
    if(num>=0) return num;//先判定是否是數字
    if( var_id.find(s)!=var_id.end() ) return var_ar[ var_id[s] ];//再判定是否為變量
    return -1;//否則非法
}

而將數字對應的漢字轉化為數字,我將它們最后全部整合成了兩個字的形式,再處理。整合分為以下幾種情況:

  1. 零-九:前補零
  2. 十:前補一
  3. 十一-十九:不變
  4. 十的整數倍:不變
  5. 其它:去掉中間的十

當然,不保證用戶的輸入一定合法,所以考慮了以下幾種情況:

  1. 三個漢字但中間不為十,或首尾為十:非法
  2. 含前導零非一個漢字的數字:去掉所有前導零后,按1處理
  3. 一十一-一十九:同5
  4. 一一-九九:不變
  5. 一零、二零、三零...九零:不變

整理以下上述情況,就是:先去除非一個漢字的前導零,若是一個漢字,則前補零;三個漢字則依次執行情況6與5;剩下兩個漢字的情況分為以下幾類:

1.僅有十開頭的形式
2.僅有十結尾的形式
3.十開頭結尾的形式
4.其它形式

對於3需要錯誤拋出,4可以直接處理

為了高效地處理1與2,本人在計算第一位后,先判定是否為十,如果不為十,在乘十實現十進制下的左移;而最后一位加上對應數字對10取模即可解決2

實現代碼如下:

inline int gbk_read(string s){
    // return -1 means the input string is illegal
    int num=-1;
    string tmp;
    while( s.substr(0,2)=="零"&&s.size()>2 ) s=s.substr(2);
    if( s.size()==2 ){
        if( !islegal(s) ) return -1;
        if(read_gbk[s]<10) s="零"+s;
        else s="一"+s;
    }//一位數和十,均湊成兩位數
    if( s.size()==6 ){
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if( read_gbk[tmp]!=10 ) return -1;//查詢第二個字是否為十,否則非法
        if( read_gbk[s.substr(0,2)]==10||read_gbk[s.substr(4,2)]==10 ) return -1;//查詢一三是否為十,是則非法
        s=s.substr(0,2)+s.substr(4,2);//去掉中間,拼成兩位數
    }
    if( s.size()==4 ){
        tmp=s.substr(0,2);
        if( !islegal(tmp) ) return -1;
        num=read_gbk[tmp];
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if(num<10) num*=10;//十* 的形式
        else if(tmp=="十") return -1;
        num+=read_gbk[tmp]%10;//*十 的形式
    }
    return num;
}

至此非指令開頭情況已解釋完畢。下文解釋指令開頭的情況:

指令作為開頭則一定為定義變量(並申請初值)或輸出的形式,否則都是需要拋出的

輸出則一定是輸出值,否則需要拋出。如果是的話,用一個函數專門負責將數字轉化成漢字。轉化分為以下情況:

  1. 0-9
  2. 10
  3. 11-19
  4. 10的其它整數倍
  5. 其它數字

總結起來,針對兩位數,當十位大於1時需要輸出十位數,再輸出十,若個位不為零則還要輸出各位

針對一位數,直接用map輸出即可

本人用 gbk_output(int) 封裝了以下,實現過程如下:

inline string gbk_output(int num){//數字轉漢字(只輸出0-99)
    string tmp="";
    if(num/10){
        if( (num/10)>1 ) tmp+=output_gbk[num/10];//不輸出 一十* 的形式
        tmp+=output_gbk[10];
        if(num%10) tmp+=output_gbk[num%10];//*十的形式,不輸出為 *十零 的形式
    }
    else tmp+=output_gbk[num%10];
    return tmp;
}

而如果是定義變量,則后面的語句先判定是否含有空格,若含有,則分為前半段與后半段;不含有,則干脆全部作為前半段,后半段放空

考慮前半段雖然保證不是關鍵字,但仍可能申請到沖突的變量名:例如數字或者已申請的變量

但考慮到上文所提,若是數字或已申請的變量,可以用 number(string) 函數取出值,所以依此判斷即可

定義語句可能后跟賦值語句,思維跟上述相同,只不過地址要直接在上述步驟中保存好即可

實現過程如下:

else{//指令開頭
        int command=comd[tmp];
        if(command==-1||command==1||command==2){//加減或賦值開頭,語句非法
            error_output();
            return ;
        }
        if(command==3){
            int num=number(s);
            if(num<0){//輸出未申請的變量,語句非法
                error_output();
                return ;
            }
            cout<<gbk_output(num)<<endl;
        }
        else{
            if( s.find(" ")==string::npos ){
                tmp=s;
                s="";
            }
            else{
                tmp=s.substr(0, s.find(" ") );
                s=s.substr( s.find(" ")+1  );
            }

            if( number(tmp)>=0 ){//變量已申請或者是數字對應的漢字,語句非法
                error_output();
                return ;
            }
            int id=var_id[tmp]=var_ar.size();
            var_ar.push_back(0);

            if(s=="") return ;//不賦初值,直接跳出

            if( s.find(" ")==string::npos ){//語句非法
                error_output();
                return ;
            }
            tmp=s.substr(0, s.find(" ") );
            s=s.substr( s.find(" ")+1 );

            if( iscomd(tmp)&&comd[tmp]==2 ){//賦初值
                int num=number(s);
                if(num>=0) var_ar[id]=num;
                else error_output();
            }
            else error_output();//語句非法
        }
    }

完整代碼

#include<cstdio>
#include<string>
#include<iostream>
#include<map>
#include<vector>
using namespace std;

vector<int> var_ar;
map<string,int> read_gbk,comd,var_id;
map<int,string> output_gbk;
/*
var_ar變量值
var_id從變量名到變量地址的映射
read_gbk將漢字轉為數字
output_gbk將數字轉成漢字
*/

inline void pre(){//初始化設置
    read_gbk["零"]=0;
    read_gbk["一"]=1;
    read_gbk["二"]=2;
    read_gbk["三"]=3;
    read_gbk["四"]=4;
    read_gbk["五"]=5;
    read_gbk["六"]=6;
    read_gbk["七"]=7;
    read_gbk["八"]=8;
    read_gbk["九"]=9;
    read_gbk["十"]=10;

    output_gbk[0]="零";
    output_gbk[1]="一";
    output_gbk[2]="二";
    output_gbk[3]="三";
    output_gbk[4]="四";
    output_gbk[5]="五";
    output_gbk[6]="六";
    output_gbk[7]="七";
    output_gbk[8]="八";
    output_gbk[9]="九";
    output_gbk[10]="十";

    comd["增加"]=1;
    comd["減少"]=-1;
    comd["等於"]=2;
    comd["看看"]=3;
    comd["整數"]=4;
}

inline void error_output(){//錯誤拋出
    puts("輸入格式錯誤");
}
inline string gbk_output(int num){//數字轉漢字(只輸出0-99)
    string tmp="";
    if(num/10){
        if( (num/10)>1 ) tmp+=output_gbk[num/10];//不輸出 一十* 的形式
        tmp+=output_gbk[10];
        if(num%10) tmp+=output_gbk[num%10];//*十的形式,不輸出為 *十零 的形式
    }
    else tmp+=output_gbk[num%10];
    return tmp;
}

inline bool islegal(string s){ return read_gbk.find(s)!=read_gbk.end(); }//查詢讀入是否為數字對應的漢字
inline int gbk_read(string s){
    // return -1 means the input string is illegal
    int num=-1;
    string tmp;
    if( s.size()==2 ){
        if( !islegal(s) ) return -1;
        if(read_gbk[s]<10) s="零"+s;
        else s="一"+s;
    }//一位數和十,均湊成兩位數
    if( s.size()==6 ){
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if( read_gbk[tmp]!=10 ) return -1;//查詢第二個字是否為十,否則非法
        if( read_gbk[s.substr(0,2)]==10||read_gbk[s.substr(4,2)]==10 ) return -1;//查詢一三是否為十,是則非法
        s=s.substr(0,2)+s.substr(4,2);//去掉中間,拼成兩位數
    }
    if( s.size()==4 ){
        tmp=s.substr(0,2);
        if( !islegal(tmp) ) return -1;
        num=read_gbk[tmp];
        tmp=s.substr(2,2);
        if( !islegal(tmp) ) return -1;
        if(num<10) num*=10;//十* 的形式
        else if(tmp=="十") return -1;
        num+=read_gbk[tmp]%10;//*十 的形式
    }
    return num;
}

inline int number(string s){//讀取用於運算的數字或變量
    int num=gbk_read(s);
    if(num>=0) return num;//先判定是否是數字
    if( var_id.find(s)!=var_id.end() ) return var_ar[ var_id[s] ];//再判定是否為變量
    return -1;//否則非法
}

inline bool iscomd(string s) { return comd.find(s)!=comd.end(); }//判定是否為合法指令
inline void carry(string s){
    string tmp="";
    int id;
    if( s.find(" ")==string::npos ){//不含空格,語句非法
        error_output();
        return ;
    }

    tmp=s.substr(0, s.find(" ") );
    s=s.substr( s.find(" ")+1 );

    if( !iscomd(tmp) ){//不是指令作為開頭
        if( var_id.find(tmp)==var_id.end() ){//也不是變量(例如數字),或變量還未申請,語句非法
            error_output();
            return ;
        }

        id=var_id[tmp];//變量的地址
        if( s.find(" ")==string::npos ){//不含空格,語句非法
            error_output();
            return ;
        }

        tmp=s.substr(0, s.find(" ") );
        s=s.substr( s.find(" ")+1 );

        if( !iscomd(tmp) ){//不是指令,語句非法
            error_output();
            return ;
        }
        
        int command=comd[tmp];
        if(command!=-1&&command!=1&&command!=2){//不是加減或者賦值,語句非法
            error_output();
            return ;
        }

        int num=number(s);
        if(num<0){//語句非法
            error_output();
            return ;
        }
        if(command==-1) var_ar[id]-=num;
        else if(command==1) var_ar[id]+=num;
        else if(command==2) var_ar[id]=num;
    }
    else{//指令開頭
        int command=comd[tmp];
        if(command==-1||command==1||command==2){//加減或賦值開頭,語句非法
            error_output();
            return ;
        }
        if(command==3){
            int num=number(s);
            if(num<0){//輸出未申請的變量,語句非法
                error_output();
                return ;
            }
            cout<<gbk_output(num)<<endl;
        }
        else{
            if( s.find(" ")==string::npos ){
                tmp=s;
                s="";
            }
            else{
                tmp=s.substr(0, s.find(" ") );
                s=s.substr( s.find(" ")+1  );
            }

            if( number(tmp)>=0 ){//變量已申請或者是數字對應的漢字,語句非法
                error_output();
                return ;
            }
            int id=var_id[tmp]=var_ar.size();
            var_ar.push_back(0);

            if(s=="") return ;//不賦初值,直接跳出

            if( s.find(" ")==string::npos ){//語句非法
                error_output();
                return ;
            }
            tmp=s.substr(0, s.find(" ") );
            s=s.substr( s.find(" ")+1 );

            if( iscomd(tmp)&&comd[tmp]==2 ){//賦初值
                int num=number(s);
                if(num>=0) var_ar[id]=num;
                else error_output();
            }
            else error_output();//語句非法
        }
    }
}

int main(){
    pre();
    string s;
    while( getline(cin,s) ) carry(s);//讀入、分析並執行指令
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM