2020面向对象程序设计寒假作业3 题解

作业描述 详情
这个作业属于哪个课程 班级链接
这个作业要求在哪里 作业要求
这个作业的目标 编程题:
继续完成作业二的编程题。
1. 优化架构,思考代码的拓展性,比如我需要增加其他功能,如选择,循环语句怎么办。
2. 思考:可以参考现有的编程语言,把这些语言的内容加入。如选择、循环语句、函数、或者扩大数字范围,支持负数等。
作业正文 2020面向对象程序设计寒假作业3 题解
其他参考文献 C++之vector类型的使用和迭代器使用

参考 本人第二次作业 后,重新规划了架构


题目要求

  1. 继续完成作业二的编程题。
  2. 优化架构,思考代码的拓展性,比如我需要增加其他功能,如选择,循环语句怎么办。
  3. 思考:可以参考现有的编程语言,把这些语言的内容加入。如选择、循环语句、函数、或者扩大数字范围,支持负数等。

优化目标

  1. 在一定程度上减少码量
  2. 增加一定的封装,使得代码可读性提高
  3. 增加对负数的处理
  4. 尽可能使用引用来实现传值,方便后续的拓展
  5. 增加对其它运算的支持

思考过程

  1. 在实现作业二的过程中,发现 World 类中,出现了较多的错误抛出方法,因此构建一个新的类 ErrorRepository 来专门实现错误抛出
  2. 在实现作业二的过程中,发现赋值语句的右侧,可以是变量或者数字。最后如果是返回数值,则一定是这两者的值之一。对于在代码中多次出现:试探是否是数值、再试探是否是变量,这一重复操作。故此,将两个类用一个新的类 ValueRepository 进行封装。在该类中实现方法,进行查询
  3. 通过查询 相关资料 ,学习到可以使用迭代器实现变量的操作
  4. 拓展数域至负数,实际上就是考虑“负”以及对负号的处理
  5. 在实现作业二的过程中发现,大部分代码量花费在对语句的划分上,直接构造一个新的方法来实现对语句的划分

因此开始构建:


一、变量库(VariableRepository)

将方法 VariableFind 改为 bool 类型返回值,返回值表示是否查得到(即查得到为 true ,查不到为 false)。而本身返回变量在 vector 中索引的操作,改为用引用储存变量的迭代器

        bool VariableFind(vector<int>::iterator &v,string name){//v 为 vector<int> 型的迭代器的一个引用
            if(variableMap.find(name)==variableMap.end()) return false;//找不到
            v=variableValue.begin()+variableMap[name];
            return true;
        }

而因此,增删改查操作都可以更加简便的实现,下面以增加为例:

        bool VariableAdd(string name,int value){
            vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v+=value;
            return true;
        }

返回值代表是否修改成功,而是否成功的决定因素即为变量是否存在

而还可进而实现变量的删除操作:

        bool VariableDelete(string name){
            vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;//变量不存在,下同 
            variableValue.erase(v);
            variableMap.erase( variableMap.find(name) );
            return true;
        }

且在作业二的实现过程中,发现语句:整数 <变量名> 与语句 整数 <变量名> 等于 <数值> 具有一定的区别,因此,为避免在 World 中占据方法,因此封装回 VariableRepository 类中:

        bool VariableApplyAssign(string name,int value){
            if( !VariableApply(name) ) return false;
            VariableAssign(name,value);
            return true;
        }

而考虑到一般的 C++ 变量都需要支持的五则运算包括:增加、减少、乘以、除以和取模,而对于 C++ 的取模与取余运算有有一定的区别。故增添取余运算,区别于取模

对于位运算,暂且先不进行考虑,因为涉及到对数位的处理;以及目前数字的输入输出环境仅支持 -99~99 ,加入位运算有一些大材小用的感觉

增加操作在上文已经可见,减少、乘以、除以、取模操作相差不大,以乘以为例,直接给出代码:

        bool VariableMultiply(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v*=value;
            return true;
        }

取余运算对于取模运算最大的区别,在于取余后为正数,也可通过取模等操作实现:

        bool VariableRemainder(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v=(*v%value+value)%value;
            return true;
		}

下面给出完整代码:

VariableRepository.h

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

#ifndef VARIABLEREPOSITORY
#define VARIABLEREPOSITORY
class VariableRepository{
    private:
        vector<int> variableValue;
        map<string,int> variableMap;
        bool VariableFind(vector<int>::iterator &v,string name){//v 为 vector<int> 型的迭代器的一个引用 
            if(variableMap.find(name)==variableMap.end()) return false;//找不到
            v=variableValue.begin()+variableMap[name];
            return true;
        }

    public:
        VariableRepository() {}
        ~VariableRepository() {}
        bool VariableApply(string name){
        	vector<int>::iterator v;
            if( VariableFind(v,name) ) return false;//变量存在
            variableMap[name]=variableValue.size();//存地址 
            variableValue.push_back(0);//赋初始值为 0
            return true;
        }
        bool VariableDelete(string name){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;//变量不存在,下同 
            variableValue.erase(v);
            variableMap.erase( variableMap.find(name) );
            return true;
        }
        bool VariableAssign(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v=value;
            return true;
        }
        bool VariableApplyAssign(string name,int value){
            if( !VariableApply(name) ) return false;
            VariableAssign(name,value);
            return true;
        }
        bool VariableAdd(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v+=value;
            return true;
        }
        bool VariableSubtract(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v-=value;
            return true;
        }
        bool VariableMultiply(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v*=value;
            return true;
        }
        bool VariableDivide(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v/=value;
            return true;
        }
        bool VariableModule(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v%=value;
            return true;
        }
        bool VariableRemainder(string name,int value){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            *v=(*v%value+value)%value;
            return true;
		}
        bool VariableShow(int &value,string name){
        	vector<int>::iterator v;
            if( !VariableFind(v,name) ) return false;
            value=*v;
            return true;
        }
};
#endif

二、数字库(NumberRepository)

同变量库(VariableRepository)一样,将原本方法的返回值全部设置为 bool 型,而原本的返回值用引用实现

同时,加入了负数。对于负数,使用特判“负”或负号的方法实现:

        bool ToChar(string &cn,int value){
            if(value<-99||value>99) return false;
            cn="";
            if(value<0) cn="负",value=-value;//先判定是否是负数 
            if(value<=10) cn+=numberChar[value];
            else if(value<20) cn+=numberChar[10]+numberChar[value-10];//十几的形式
            else if(value%10==0) cn+=numberChar[value/10]+numberChar[10];//几十的形式
            else cn+=numberChar[value/10]+numberChar[10]+numberChar[value%10];
            return true;
        }

        bool ToNumber(int &value,string name){
            bool negative=0;
            if(name.substr(0,2)=="负") negative=1,name=name.substr(2);//先判定是否是负数 
            if( !FormatChar(name) ) return false;//失败
            int ten,base;
            FindChar( ten,name.substr(0,2) );
			FindChar( base,name.substr(2,2) );
			value=ten*10+base;
			if(negative) value=-value;
            return true;
        }

其次,本人在此基础上,优化了输出的范围:为所有 int 范围内

根据本人翻阅小学数学书,大数的读法与写法,其中介绍了大数的读法:

  1. 根据中国的读法,四个数位为一级
  2. 从小到大,数级为:个、万、亿等(其余不在 int 范围内)
  3. 读数时从大的数级往小读
  4. 若存在更大数级的情况下,本数级若不满四位,且前一数级不全为 0 的话,应加前缀零。如:20147,读作:两万零一百四十七
  5. 最高位数级若介于 11 至 19 ,应读作“十*”的形式
  6. 凡是非最高位数级, 11 至 19 均应读作“一十*”的形式
  7. 数级内,若后缀全为零的不再补充后缀。如:2100300,读作:二百一十万零三百
  8. 数级内,两非零数位间存在零,读且仅读一个零。如:1003,读作:一千零三

依次,构造出新的 ToChar 方法

首先,对于输入的数特判是否会超出 int 范围(虽然现在的代码中,变量使用 int 储存值,但之后会改成 longlong 甚至高精)

对于在 int 范围内的数,分别提取亿级、万级、个级,依次用 ToCharInThousand 方法处理数级内读法

最后根据前缀情况(前缀是否存在、前缀是否含零),判定是否要前补零

给出这两个方法的代码:

        void ToCharInThousand(string &cn,int value){
        	if(value<20){
        		if(value<=10) cn+=numberChar[value];
        		else cn+=numberChar[10]+numberChar[value-10];
        		return ;
    		}
		bool prefix=0;
		if(value>=1000) cn+=numberChar[value/1000]+numberChar[1000],prefix=1;
		else if(prefix&&value%1000!=0) cn+=numberChar[0],prefix=0;
		value%=1000;
		if(value>=100) cn+=numberChar[value/100]+numberChar[100],prefix=1;
		else if(prefix&&value%100!=0) cn+=numberChar[0],prefix=0;
		value%=100;
		if(value>=10) cn+=numberChar[value/10]+numberChar[10];
		else if(prefix&&value%10!=0) cn+=numberChar[0];
		value%=10;
		if(value>=1) cn+=numberChar[value];
	}

以及将三个数级整合起来的代码

这里为了方便实现,将 numberChar 改为了 map 容器储存,并且补充了 charNumber 来储存反向的信息

        bool ToChar(string &cn,long long int value){
        	if(value<0) cn="负",value=-value;
        	if(value>>31) return false;
        	else cn="";
        	int Ge=value%10000,Wan=value/10000%10000,Yi=value/10000/10000;
        	bool prefix=0;
        	if(Yi) ToCharInThousand(cn,Yi),cn+=numberChar[100000000],prefix=1;
        	if(Wan){
        		if(Wan<1000&&prefix) cn+=numberChar[0];
        		if(Wan>=10&&Wan<20&&prefix) cn+=numberChar[1];
        		ToCharInThousand(cn,Wan),cn+=numberChar[10000];
        		prefix=1;
			}
			else if(prefix) cn+=numberChar[0],prefix=0;
			if(Ge){
				if(Ge<1000&&prefix) cn+=numberChar[0];
				if(Ge>=10&&Ge<20&&prefix) cn+=numberChar[1];
				ToCharInThousand(cn,Ge);
			}
			return true;
        }

最后放出总代码:

NumberRepository.h

#include<string>
#include<map>
using namespace std;

#ifndef NUMBERREPOSITORY
#define NUMBERREPOSITORY
class NumberRepository{
    private:
        map<int,string> numberChar;
        map<string,int> charNumber;
        void NumberCharPrepare(){
        	numberChar[0]="零";
        	numberChar[1]="一";
        	numberChar[2]="二";
        	numberChar[3]="三";
        	numberChar[4]="四";
        	numberChar[5]="五";
        	numberChar[6]="六";
        	numberChar[7]="七";
        	numberChar[8]="八";
        	numberChar[9]="九";
        	numberChar[10]="十";
        	numberChar[100]="百";
        	numberChar[1000]="千";
        	numberChar[10000]="万";
        	numberChar[100000000]="亿";
		}
		void CharNumberPrepare(){
			charNumber["零"]=0;
			charNumber["一"]=1;
			charNumber["二"]=2;
			charNumber["三"]=3;
			charNumber["四"]=4;
			charNumber["五"]=5;
			charNumber["六"]=6;
			charNumber["七"]=7;
			charNumber["八"]=8;
			charNumber["九"]=9;
			charNumber["十"]=10;
			charNumber["百"]=100;
			charNumber["千"]=1000;
			charNumber["万"]=10000;
			charNumber["亿"]=100000000;
		}
        bool FindChar(int &number,string num){
        	if( charNumber.find(num)==charNumber.end() ) return 0;//找不到 
        	number=charNumber[num];
        	return true;
        }
        bool FormatChar(string &num){
            int temporary[3];
            for(int i=0;i<=num.size()-2;i+=2)
				if( !FindChar(temporary[i>>1],num.substr(i,2)) ) return false;//字符合法性检验
            if( num.size()==2 ) num=numberChar[0]+num;//一个字,前补零,十因为特殊性也可以这样处理
            else if( num.size()==4 ){
                if(temporary[0]==10&&temporary[1]==10) return false;//两个数不同时为十
                if(temporary[0]==10) num=numberChar[1]+num.substr(2,2);//十几的形式
                else if(temporary[1]==10) num=num.substr(0,2)+numberChar[0];//几十的形式
            }
            else if( num.size()==6 ){
                if( temporary[0]==10 || temporary[1]!=10 || temporary[2]==10 ) return false;//首尾不能为十,中间一定要为十
                num=num.substr(0,2)+num.substr(4,2);//去掉中间
            }
            return true;
        }
        void ToCharInThousand(string &cn,int value){
        	if(value<20){
        		if(value<=10) cn+=numberChar[value];
        		else cn+=numberChar[10]+numberChar[value-10];
        		return ;
			}
			bool prefix=0;
			if(value>=1000) cn+=numberChar[value/1000]+numberChar[1000],prefix=1;
			else if(prefix&&value%1000!=0) cn+=numberChar[0],prefix=0;
			value%=1000;
			if(value>=100) cn+=numberChar[value/100]+numberChar[100],prefix=1;
			else if(prefix&&value%100!=0) cn+=numberChar[0],prefix=0;
			value%=100;
			if(value>=10) cn+=numberChar[value/10]+numberChar[10];
			else if(prefix&&value%10!=0) cn+=numberChar[0];
			value%=10;
			if(value>=1) cn+=numberChar[value];
		}

    public:
        NumberRepository(){//初始化赋值
        	NumberCharPrepare();
        	CharNumberPrepare();
        }
        ~NumberRepository() {}

        bool ToChar(string &cn,long long int value){
        	if(value<0) cn="负",value=-value;
        	if(value>>31) return false;
        	else cn="";
        	int Ge=value%10000,Wan=value/10000%10000,Yi=value/10000/10000;
        	bool prefix=0;
        	if(Yi) ToCharInThousand(cn,Yi),cn+=numberChar[100000000],prefix=1;
        	if(Wan){
        		if(Wan<1000&&prefix) cn+=numberChar[0];
        		if(Wan>=10&&Wan<20&&prefix) cn+=numberChar[1];
        		ToCharInThousand(cn,Wan),cn+=numberChar[10000];
        		prefix=1;
			}
			else if(prefix) cn+=numberChar[0],prefix=0;
			if(Ge){
				if(Ge<1000&&prefix) cn+=numberChar[0];
				if(Ge>=10&&Ge<20&&prefix) cn+=numberChar[1];
				ToCharInThousand(cn,Ge);
			}
			return true;
        }

        bool ToNumber(int &value,string name){
            bool negative=0;
            if(name.substr(0,2)=="负") negative=1,name=name.substr(2);//先判定是否是负数 
            if( !FormatChar(name) ) return false;//失败
            int ten,base;
            FindChar( ten,name.substr(0,2) );
			FindChar( base,name.substr(2,2) );
			value=ten*10+base;
			if(negative) value=-value;
            return true;
        }
};
#endif

三、值库(ValueRepository)

内部实例化了变量库与数字库,大部分方法都与两者相同,只需要使用到两者的方法即可

而唯一增加的即使对右值的查询

        bool ValueFind(int &value,string name){
            if( numberRepository.ToNumber(value,name) ) return true;
            return variableRepository.VariableShow(value,name);
        }

一样为先判定是否是数字,再判定是否是变量。通过引用返回具体数值

直接给出代码:

ValueRepository.h

#include "VariableRepository.h"
#include "NumberRepository.h"

#ifndef VALUEREPOSITORY
#define VALUEREPOSITORY
class ValueRepository{
    private:
        VariableRepository variableRepository;
        NumberRepository numberRepository;
    
    public:
        ValueRepository() {}
        ~ValueRepository() {}
        bool ToChar(string &cn,int value){
			return numberRepository.ToChar(cn,value);
		}
        bool ToNumber(int &value,string name){
			return numberRepository.ToNumber(value,name);
		}
        bool VariableApply(string name){
			return variableRepository.VariableApply(name);
		}
        bool VariableDelete(string name){
			return variableRepository.VariableDelete(name);
		}
        bool VariableAssign(string name,int value){
			return variableRepository.VariableAssign(name,value);
		}
        bool VariableApplyAssign(string name,int value){
			return variableRepository.VariableApplyAssign(name,value);
		}
        bool VariableAdd(string name,int value){
			return variableRepository.VariableAdd(name,value);
		}
        bool VariableSubtract(string name,int value){
			return variableRepository.VariableSubtract(name,value);
		}
		bool VariableMultiply(string name,int value){
			return variableRepository.VariableMultiply(name,value);
		}
		bool VariableDevide(string name,int value){
			return variableRepository.VariableDivide(name,value);
		}
		bool VariableModule(string name,int value){
			return variableRepository.VariableModule(name,value);
		}
		bool VariableRemainder(string name,int value){
			return variableRepository.VariableRemainder(name,value);
		}
        bool VariableShow(int &value,string name){
			return variableRepository.VariableShow(value,name);
		}
        bool ValueFind(int &value,string name){
            if( numberRepository.ToNumber(value,name) ) return true;
            return variableRepository.VariableShow(value,name);
        }
};
#endif

四、错误处理库(ErrorRepository.h)

为使得 World 作为唯一与用户交互的类,错误处理库使用传入 string 类引用的方法

然后,可以根据错误类型(errorType)为整数的特性,直接用 vector 储存需要返回的 string

而同时,将关键字(Keyword)直接存到 ErrorRepository 中,可以避免 World 冗长,也方便查找

因此 ErrorRepository 在处理错误抛出的同时,还应开一个 vector 储存所有非数字关键字,到时候直接查询这个库来避免冲突关键字

比较简单,直接给出代码:

ErrorRepository.h

#include<vector>
#include<string>
using namespace std;

#ifndef ERRORREPOSITORY
#define ERRORREPOSITORY
class ErrorRepository{
    private:
        vector<string> keywordRepository,errorWordRepository;
        void KeywordRepositoryPrepare(){
            keywordRepository.push_back("增加");
            keywordRepository.push_back("减少");
            keywordRepository.push_back("乘以");
            keywordRepository.push_back("除以");
            keywordRepository.push_back("取模");
            keywordRepository.push_back("取余");
            keywordRepository.push_back("看看");
            keywordRepository.push_back("等于");
            keywordRepository.push_back("整数");
            keywordRepository.push_back("删除");
		}
		void ErrorWordRepositoryPrepare(){
			errorWordRepository.push_back("");
			errorWordRepository.push_back("变量不存在");
			errorWordRepository.push_back("变量已申请");
			errorWordRepository.push_back("数字错误");
			errorWordRepository.push_back("语句无法识别");
			errorWordRepository.push_back("与关键字冲突");
			errorWordRepository.push_back("数字超限");
			errorWordRepository.push_back("零不得为除数");
		}
    
    public:
        ErrorRepository(){
        	KeywordRepositoryPrepare();
        	ErrorWordRepositoryPrepare();
        }
        ~ErrorRepository() {}
        void ErrorOutput(string &output,int errorType){
        	if(errorType>=0&&errorType<errorWordRepository.size())
        		output=errorWordRepository[errorType];
        	else output="未知错误";
        }
        bool IsKeyword(string name){
            for(int i=0;i<keywordRepository.size();i++)
                if(keywordRepository[i]==name) return 1;
            return 0;
        }
};
#endif

五、世界(World)

为避免大部分码量又耗费在划分关键字上,直接构造方法 OrderBreakDown 将指令分解,并存在指令的暂存器 orderRegister 内

        void OrderBreakDown(string order){//将指令按空格分解
            orderRegister.clear();//先清空暂存器 
            while(order.find(" ")!=string::npos){
                orderRegister.push_back( order.substr(0,order.find(" ")) );
                order=order.substr( order.find(" ")+1 );
            }
            if(order.size()!=0) orderRegister.push_back(order);
        }

而如此一来,之前用以分解指令的 Update_string,Apply_string,Show_string 方法都没有存在的必要了

修改 World 的结构,使之直接在 Understand 方法中进行分装:

        void Understand(int &errorType,string &answer,string sentence){
            errorType=0;//初始化无错误 
            answer="";//初始化输出为空 
            OrderBreakDown(sentence);//分解指令 
            
            if(orderRegister.size()<=1){//除空格外,只含不超过一个内容,指令非法
                errorType=4;
                return ;
            }
            if(orderRegister.size()==4&&orderRegister[0]=="整数"){
                if(orderRegister[2]!="等于") errorType=4;
				//分解为四节的,一定需为“整数 <变量> 等于 <值>”的形式
                int value;
                if( !valueRepository.ValueFind(value,orderRegister[3]) ) errorType=3;
                else ApplyAssign(errorType,orderRegister[1],value);
            }
            else if(orderRegister.size()==2){
                if(orderRegister[0]=="看看") Print(errorType,answer,orderRegister[1]);
                else if(orderRegister[0]=="整数") Apply(errorType,orderRegister[1]);
                else if(orderRegister[0]=="删除") Delete(errorType,orderRegister[1]);
            }
            else if(orderRegister.size()==3){
                int value;
                if( !valueRepository.ValueFind(value,orderRegister[2]) ) errorType=3;
                else Update(errorType,orderRegister[0],value);
            }
            else errorType=4;
        }

这里的 Update 是将所有对变量值操作的函数,加入到一个新的方法中进行分发

        void Update(int &errorType,string name,int value){//变量值发生变化的操作 
        	if(orderRegister[1]=="增加") Add(errorType,name,value);
        	if(orderRegister[1]=="减少") Subtract(errorType,name,value);
        	if(orderRegister[1]=="乘以") Multiply(errorType,name,value);
        	if(orderRegister[1]=="除以") Devide(errorType,name,value);
        	if(orderRegister[1]=="取模") Module(errorType,name,value);
        	if(orderRegister[1]=="取余") Remainder(errorType,name,value);
        }

考虑到了 OrderBreakDown 方法的引入,对变量的值修改的方法书写起来就会方便许多,以增加操作为例:

        void Add(int &errorType,string name,int value){
            if( !valueRepository.VariableAdd(name,value) ) errorType=1;
        }

而较为特殊的就是除以、取模、取余操作,其均需要先进行特判:除数不为0,以除以操作为例:

        void Devide(int &errorType,string name,int value){
        	if(value==0) errorType=7;//0 不能作为除数,下同 
            else if( !valueRepository.VariableDevide(name,value) ) errorType=1;
        }

由对于变量库的修改,对应的 World 类中也需要增加 ApplyAssign 方法

        void ApplyAssign(int &errorType,string name,int value){
            errorType=0;
            if( IsConflict(name) ) errorType=5;//与关键字冲突
            else if( !valueRepository.VariableApplyAssign(name,value) ) errorType=2;//申请失败,返回2(变量已申请)
        }

其余的方法变化不大,这里直接给出总代码:
World.h

#include "ValueRepository.h"
#include "ErrorRepository.h"
#include<vector>
#include<string>
#include<iostream>
using namespace std;

#ifndef WORLD
#define WORLD
class World{
    private:
        ValueRepository valueRepository;
        ErrorRepository errorRepository;
        vector<string> orderRegister;
        bool IsConflict(string name){
            int value;
            if( valueRepository.ToNumber(value,name) ) return 1;
            return errorRepository.IsKeyword(name);
        }
        bool Input(string &sentence){
            return getline(cin,sentence);
        }
        void OrderBreakDown(string order){//将指令按空格分解
            orderRegister.clear();//先清空暂存器 
            while(order.find(" ")!=string::npos){
                orderRegister.push_back( order.substr(0,order.find(" ")) );
                order=order.substr( order.find(" ")+1 );
            }
            if(order.size()!=0) orderRegister.push_back(order);
        }
        void Add(int &errorType,string name,int value){
            if( !valueRepository.VariableAdd(name,value) ) errorType=1;
        }
        void Subtract(int &errorType,string name,int value){
            if( !valueRepository.VariableSubtract(name,value) ) errorType=1;
        }
        void Multiply(int &errorType,string name,int value){
            if( !valueRepository.VariableMultiply(name,value) ) errorType=1;
        }
        void Devide(int &errorType,string name,int value){
        	if(value==0) errorType=7;//0 不能作为除数,下同 
            else if( !valueRepository.VariableDevide(name,value) ) errorType=1;
        }
        void Module(int &errorType,string name,int value){
        	if(value==0) errorType=7;
            else if( !valueRepository.VariableModule(name,value) ) errorType=1;
        }
        void Remainder(int &errorType,string name,int value){
        	if(value==0) errorType=7;
            else if( !valueRepository.VariableRemainder(name,value) ) errorType=1;
        }
        void Assign(int &errorType,string name,int value){
            if( !valueRepository.VariableAssign(name,value) ) errorType=1;
        }
        void Apply(int &errorType,string name){
            if( IsConflict(name) ) errorType=5;//与关键字冲突
            else if( !valueRepository.VariableApply(name) ) errorType=2;//申请失败,返回2(变量已申请)
		}
        void ApplyAssign(int &errorType,string name,int value){
            errorType=0;
            if( IsConflict(name) ) errorType=5;//与关键字冲突
            else if( !valueRepository.VariableApplyAssign(name,value) ) errorType=2;//申请失败,返回2(变量已申请)
        }
        void Print(int &errorType,string &value,string name){
            int data;
            if( !valueRepository.VariableShow(data,name) ) errorType=1;
            else if( !valueRepository.ToChar(value,data) ) errorType=6;//数字超限
        }
        void Delete(int &errorType,string name){
            if( !valueRepository.VariableDelete(name) ) errorType=1;
        }
        void Update(int &errorType,string name,int value){//变量值发生变化的操作 
        	if(orderRegister[1]=="增加") Add(errorType,name,value);
        	if(orderRegister[1]=="减少") Subtract(errorType,name,value);
        	if(orderRegister[1]=="乘以") Multiply(errorType,name,value);
        	if(orderRegister[1]=="除以") Devide(errorType,name,value);
        	if(orderRegister[1]=="取模") Module(errorType,name,value);
        	if(orderRegister[1]=="取余") Remainder(errorType,name,value);
		}
        void Understand(int &errorType,string &answer,string sentence){
            errorType=0;//初始化无错误 
            answer="";//初始化输出为空 
            OrderBreakDown(sentence);//分解指令 
            
            if(orderRegister.size()<=1){//除空格外,只含不超过一个内容,指令非法
                errorType=4;
                return ;
            }
            if(orderRegister.size()==4&&orderRegister[0]=="整数"){
                if(orderRegister[2]!="等于") errorType=4;
				//分解为四节的,一定需为“整数 <变量> 等于 <值>”的形式
                int value;
                if( !valueRepository.ValueFind(value,orderRegister[3]) ) errorType=3;
                else ApplyAssign(errorType,orderRegister[1],value);
            }
            else if(orderRegister.size()==2){
                if(orderRegister[0]=="看看") Print(errorType,answer,orderRegister[1]);
                if(orderRegister[0]=="整数") Apply(errorType,orderRegister[1]);
                if(orderRegister[0]=="删除") Delete(errorType,orderRegister[1]);
            }
            else if(orderRegister.size()==3){
                int value;
                if( !valueRepository.ValueFind(value,orderRegister[2]) ) errorType=3;
                else Update(errorType,orderRegister[0],value);
            }
            else errorType=4;
        }
    
    public:
        World() {}
        ~World() {}
        void Run(){
            string order,answer;
            int errorType;
            while( Input(order) ){
                if(order=="退出") break;
                Understand(errorType,answer,order);
                if(errorType!=0) errorRepository.ErrorOutput(answer,errorType);
                if(answer.size()) cout<<answer<<endl;
            }
        }
};
#endif

六、编译脚本

现在的新代码中,包含了以下的六个部分

  1. ErrorRepository.h
  2. NumberRepository.h
  3. VariableRepository.h
  4. ValueRepository.h
  5. World.h
  6. baihua-lang.cpp

其中,编译 2、3 为编译 4 的先决条件;编译 1、4 为编译 5 的先决条件;编译 5 为编译 6 的先决条件

故此编写编译脚本时因注重以上顺序

编译脚本.bat

@echo off
title 编译脚本

if exist baihua-lang.exe (
	echo 程序 baihua-lang.exe 已存在
	pause>nul
	exit
)
if not exist baihua-lang.cpp (
	echo 源代码 baihua-lang.cpp 丢失
	pause>nul
	exit
)
if not exist World.h.gch (
	if not exist World.h (
		echo 源代码 World.h 丢失
		pause>nul
		exit
		)
	if not exist ErrorRepository.h.gch (
		if exist ErrorRepository.h (g++ ErrorRepository.h) else (
			echo 源代码 ErrorRepository.h 丢失
			pause>nul
			exit
		)
	)
	if not exist ValueRepository.h.gch (
		if not exist ValueRepository.h (
			echo 源代码 ValueRepository.h 丢失
			pause>nul
			exit
		)
		if not exist VariableRepository.h.gch (
			if exist VariableRepository.h (g++ VariableRepository.h) else (
				echo 源代码 VariableRepository.h 丢失
				pause>nul
				exit
			)
		)
		if not exist NumberRepository.h.gch (
			if exist NumberRepository.h (g++ NumberRepository.h) else (
				echo 源代码 NumberRepository.h 丢失
				pause>nul
				exit
			)
		)
		g++ ValueRepository.h
	)
	g++ World.h
)
g++ -o baihua-lang.exe baihua-lang.cpp
echo 编译完成
pause>nul

七、思考题想法

对于分支与循环语句的实现,均需要用一个逻辑语句的储存器,和一个语句的储存器实现

对于比较简单的分支语句,需要先判定逻辑语句是否正确,再根据逻辑的结果,对应执行语句

而循环语句分为“当型”与“直到型”,均需要储存下语句。

“当型”先判定逻辑语句,当逻辑语句的值正确时,重复执行循环语句,直到逻辑语句为错误

“直到型”先执行循环语句,再判定逻辑语句,当逻辑语句的值错误时,重复执行循环语句,直到正确

而为避免死循环导致 baihua-lang.exe 崩溃,需设定一个循环次数的上界,当达到上届后自动抛错“循环溢出”

因此,为实现上述功能,最重要的包括两点:

  1. 实现“两栖式”读取指令:既可从外界读入,也可从语句储存器中读入
  2. 实现逻辑判断能力

其中逻辑判断能力实现比较复杂,例如以下情况:

如果
钱包 大于 五
且
钱包 小于 十
或
微信 大于等于 六
异或
银行卡 小于等于 八
或
非
支付宝 等于 三
则
......

因此,实现分支与循环语句需要较大的代码量

People &Me=FeiMa;
Me.say("关于此,我确信已发现了一种美妙的证法,可惜这里空白的地方太小,写不下。");

我已想到了一个比较可能的实现方法,等我比较空闲了以后再来实现此功能

原文地址:https://www.cnblogs.com/JustinRochester/p/12300895.html