zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Git合并开发代码分支到测试代码分支
    用webdriver+phantomjs实现无浏览器的自动化过程
    软件测试工作中涉及的Linux命令整理
    Windows系统端口占用情况检查脚本
    PowerShell调用jira rest api实现对个人提交bug数的统计
    地下城堡游戏小脚本儿——自动炼金
    Java中通过JDBC远程连接Oracle数据库
    PowerShell调用jira rest api实现jira统计自动化
    【Spring】12、Spring Security 四种使用方式
    【hibernate】1、Hibernate的一个注解 @Transient
  • 原文地址:https://www.cnblogs.com/JustinRochester/p/12217654.html
Copyright © 2011-2022 走看看