zoukankan      html  css  js  c++  java
  • 1.0总结


    GitHub develop分支

    团队:
    盖嘉轩 031602211
    许郁杨 031602240

    1.0主要是描述一下程序本身,也就是代码和编码规范部分,完整的总结会在完善一些细节后再提交。其实原定是应该在13号提交这篇文章的,只是我写的部分出了点问题。。

    其实因为两个人的时间安排问题,导致刚开始没办法常在网上交流,很多问题都是在电话里解决的,难道要把通话记录当证据吗。。总之先用文字阐述一下这个程序的实现过程。


    分工

    一开始我们就确定了要使用多源文件编写。各个文件的功能分类是我提出方案,然后两个人再一起讨论确定下来的。最初的源文件是这样分类的:

    文件名 功能
    head 头文件
    main 初始界面
    calculate 计算器主体
    fraction 生成、计算分数
    stack 计算表达式

    实际上这个分类是有点问题的,很多功能都集中在了个别文件里,只是这个问题写到了stack我们才发现,而且模块分类上也有很多毛病,这也导致了后面千奇百怪的bug。。这个后面会具体说。

    最初的分工是这样的:我负责fraction和stack,队友负责其他部分。这也算是最后分工。

    1.0的文件分类:

    文件名 功能
    head 头文件
    main 初始界面
    generate 生成表达式
    fraction 生成、计算分数
    stack 计算表达式
    verify 检验答案并输出

    实现过程中,最初的calculate包括的功能比想象中的多得多。。编写时已经特地分流一些给其他文件了,还是显得有些臃肿,所以最后还是硬拆了一个文件出来。分工没什么变化。

    功能模块的分类会在实现思路部分具体地说。


    日程规划

    不得不感叹一下两个人都很忙啊。。前期基本没时间做,差不多8号之后才开始写代码。

    简单的日程规划:

    此处输入图片的描述

    最后。。嗯,晚了一天。。

    不过,实际的情况是基本符合规划的。只是13号在测试时发现了一个大坑,填到了今天才勉强填好。


    编程规范

    编程规范是在模块分类后才一一确定下来的,不过由于变量超乎想象的多,所以变量命名有点混乱。

    变量的命名都尽量使用了英文或英文缩写,这里写一下部分变量:

    变量名 作用
    i、j 循环变量
    flag 标志作用
    tmp 临时变量
    frac 分数
    low、high 数字范围
    answer 结果
    sign 符号
    para 参数
    infix 中缀表达式
    postfix 后缀表达式
    point 栈顶指针

    函数的命名我们充分发挥了想象力。。

    函数名 作用
    getRand 获取随机数
    getAndCalculate 获取表达式
    transEquation 中缀转为后缀
    countEquation 计算后缀的值
    ifOnly 判断重复
    checkAndOutput 检验答案并输出正确答案
    finalOut 输出正误个数
    gcd 最大公约数
    fixUp 维护分母为正
    getFrac 生成分数
    transFrac 整数转化为分数
    simplify 分数化简
    transString 分数转为字符串(不判断整数)
    transToString 分数转为字符串(判断整数)
    operator +、-、*、/ 分数四则运算

    实现思路和遇到的困难

    这里主要讲我负责的分数和栈的部分。

    最初看到这个题目,觉得最难的部分就是如何处理分数。分数牵扯到了方方面面,比如:

    • 如何生成分数;
    • 如何保存分数(表达式中分数的格式);
    • 分数与整数的关系;
    • 如何计算分数;
    • 如何在栈中处理分数;

    因为曾经看过c++的分数类,所以决定对分数进行单独处理,用一个独立的文件负责分数的生成、格式转换和计算。

    为了能方便地分离生成表达式与计算表达式两部分,我们决定将所有数字在计算时当作分数处理(整数/1),将分子与分母分离,对所有数据都进行整型和字符串两种格式的保存。

    字符串类型上,因为string能比较方便地进行字符串的组合连接,所以没有使用char[]。但是怎么把数字转换成字符串就有点麻烦了。的确c++中数字和字符串相互转换的方法很多,但是多数还是需要char[]的。我想找到一个除itoa()外的能直接通过string作转换的通用方法,以便处理小规模的数据,毕竟itoa()是平台相关的。最终找到了stringstream,真的超级方便,可以适用于string与各种数据类型的转换。不过这么方便让我不禁怀疑起其中的坑。。不过使用到现在只知道要注意多次转换需要clear(),之后还会继续学习串流的知识。

    我通过随机生成两个整数,将大的作为分母,小的作为分子来生成分数。分子分母分别保存后,分数的计算也就简单了。比如加法,答案的分子就是x的分子 × y的分母 + x的分母 × y的分子,分母就是x的分母 × y的分母,最后再化简,就得到了得数。

    关于如何在栈中处理分数,我是将所有分数都作加括号处理,而表达式中的括号则用中括号来代替,这样就能将分数作为一个整体来处理。

    的部分,其实就是将生成的中缀表达式转换成后缀后再计算。不过这次我试用了c++的堆栈容器stack,的确让代码简洁清楚了些。计算后缀时,因为参数和得数都是分数形式的,所以用了分数类数组来作为栈。

    表达式计算这部分,除了转换为后缀,也有直接用中缀计算的方法,但是个人觉得后缀的方法更加简洁通用。

    除了上面说的,实际实现过程中还碰到了不少问题,特别是在写类的时候,不作不会死。。。其实简单用函数就可以了,只是我对操作符重载挺好奇的,就在这试了一下,结果意外地成功了,虽然还是碰到了很多问题。类的知识我还有好些没能理解,应该说对于类本身我就不是很理解,感觉我就像是在把类当作函数用。。

    还有就是计算后缀时,用作将字符转换为数字的变量需要用double,用int计算结果会出错。最初我不知道问题出在哪,所以去翻了网上一些计算中缀表达式的程序,发现了这个差别,只是不知道为什么要用double。。

    其他一些零碎的问感觉不是很有写出来的必要,如果有需要补充,会添加在下次的总结中。


    代码(个人负责部分)

    其他需要注释的部分会在下次添加

    /*************************************************************
    文件名:fraction.cpp 
    作者:许郁杨 日期:2016/02/16
    描述: 分数类 
    主要功能包括:分数的生成、转换和四则运算 
    *************************************************************/
    
    #include"head.h"
    
    int Fraction::greatestCommonDivisor(int x,int y) //最大公约数 
    {
    	if (y==0) return x;
    	else return greatestCommonDivisor(y,x%y);
    }
    Fraction::Fraction(){ }
    Fraction Fraction::getFrac(int l,int h) //生成分数 
    {
    	Fraction frac;
    	int tmp1,tmp2;
    	char tmpc[MAX];
    	stringstream tmps5,tmps6;
    	tmp1=getRand(l,h);
    	tmp2=getRand(l,h);
    	frac.numerator=Min(tmp1,tmp2);
    	frac.denominator=Max(tmp1,tmp2);
    	tmps5<<frac.numerator;
    	tmps5>>frac.numerators;
    	tmps6<<frac.denominator;
    	tmps6>>frac.denominators;
    	return frac;
    }
    Fraction Fraction::transFrac(int up,int down) //整数转换为分数 
    {
    	Fraction frac;
    	stringstream tmps9,tmps10;
    	frac.numerator=up;
    	frac.denominator=down;
    	tmps9<<frac.numerator;
    	tmps9>>frac.numerators;
    	tmps10<<frac.denominator;
    	tmps10>>frac.denominators;
    	return frac;
    }
    void Fraction::fixUp(Fraction frac) //维护分母为正 
    {
    	if (frac.denominator<0)
    	{
    		frac.denominator=-frac.denominator;
    		frac.numerator=-frac.numerator;
        }
    }
    Fraction Fraction::simplify(Fraction frac) //分数化简 
    {
    	int tmp;
    	char tmpc[MAX];
    	stringstream tmps7,tmps8;
    	fixUp(frac);
    	if (frac.numerator==0) frac.denominator=1;
    	else
    	{
    		tmp=greatestCommonDivisor(fabs(frac.numerator),fabs(frac.denominator));
    		frac.numerator/=tmp;
    		frac.denominator/=tmp;
    	}
    	tmps7<<frac.numerator;
    	tmps7>>frac.numerators;
    	tmps8<<frac.denominator;
    	tmps8>>frac.denominators;
    	return frac;
    }
    string Fraction::transString(Fraction frac) //分数转为字符串(不判断整数)
    {
    	string str;
    	str="("+frac.numerators+"\"+frac.denominators+")";
    	return str;
    }
    string Fraction::transToString(Fraction frac) //分数转为字符串(判断整数)
    {
    	string str;
    	if (frac.denominator==1) str=frac.numerators;
    	else str="("+frac.numerators+"\"+frac.denominators+")";
    	return str;
    }
    const Fraction operator +(Fraction frac1,Fraction frac2) //加法 
    {
    	Fraction answer;
    	answer.numerator=frac1.numerator*frac2.denominator+frac1.denominator*frac2.numerator;
    	answer.denominator=frac1.denominator*frac2.denominator;
    	return answer.simplify(answer);
    }
    const Fraction operator -(Fraction frac1,Fraction frac2) //减法 
    {
    	Fraction answer;
    	answer.numerator=frac1.numerator*frac2.denominator-frac1.denominator*frac2.numerator;
    	answer.denominator=frac1.denominator*frac2.denominator;
    	return answer.simplify(answer);
    }
    const Fraction operator *(Fraction frac1,Fraction frac2) //乘法 
    {
    	Fraction answer;
    	answer.numerator=frac1.numerator*frac2.numerator;
    	answer.denominator=frac1.denominator*frac2.denominator;
    	return answer.simplify(answer);
    }
    const Fraction operator /(Fraction frac1,Fraction frac2) //除法 
    {
    	Fraction answer;
    	answer.numerator=frac1.numerator*frac2.denominator;
    	answer.denominator=frac1.denominator*frac2.numerator;
    	return answer.simplify(answer);
    }
    
    /*************************************************************
    文件名:stack.cpp 
    作者:许郁杨 日期:2016/02/16
    描述: 栈 
    主要功能包括:中缀转换为后缀、计算后缀 
    *************************************************************/
    
    #include"head.h"
    
    stack<char> stored; //运算符栈 
    void transEquation(string infix,char postfix[]) //中缀转为后缀 
    {
    	int i=0,j=0;
    	while (infix[i]!='')
    	{
    		if ((infix[i]>='0')&&(infix[i]<='9')) //判断数字 
    		{
    			while ((infix[i]>='0')&&(infix[i]<='9'))
    			{
    					postfix[j]=infix[i];
    					i++;
    					j++;
    			}
    			postfix[j]='!'; //标识单个整数 
    			j++;
    		}
    		if (infix[i]=='(') //判断分数 
    		{	
    			while (infix[i]!=')') //将分数作为整体 
        		{
    	    		postfix[j]=infix[i];
    		    	i++;
        			j++;
        		}
        		postfix[j]=infix[i];
        		i++;
        		j++;
    		}
    		if ((infix[i]=='+')||(infix[i]=='-')) //判断'+'、'-' 
    		{
    			while ((!stored.empty())&&(stored.top()!='['))
    			{
    				postfix[j]=stored.top();
    				j++;
    				stored.pop();
    			}
    			stored.push(infix[i]);
    		}
    		if ((infix[i]=='*')||(infix[i]=='/')) //判断'*'、'/' 
    		{
    			while ((!stored.empty())&&(stored.top()!='[')&&((stored.top()=='*')||(stored.top()=='/')))
    			{
    				postfix[j]=stored.top();
    				j++;
    				stored.pop();
    			}
    			stored.push(infix[i]);
    		}
    		if (infix[i]=='[') stored.push(infix[i]); //判断'[' 
    		if (infix[i]==']') //判断']' 
    		{
    			while (stored.top()!='[')
    			{
    				postfix[j]=stored.top();
    				j++;
    				stored.pop();
    			}
    			stored.pop();
    		}
    		i++;
    	}
    	while (!stored.empty()) //残余运算符 
    	{
    		postfix[j]=stored.top();
    		j++;
    		stored.pop();
    	}
    	postfix[j]=''; //终止符 
    }
    Fraction figure[MAX]; //数栈 
    string countEquation(string infix) //计算后缀的值 
    {
    	int i=0,point=-1;
    	char postfix[MAX];
    	transEquation(infix,postfix);
    	while (postfix[i]!='')
    	{
    		if ((postfix[i]>='0')&&(postfix[i]<='9')) //整数入栈 
    		{
    			double k=0; //int会计算出错 
    			while ((postfix[i]>='0')&&(postfix[i]<='9'))
    			{
    				k=10*k+postfix[i]-'0';
    				i++;
    			}
    			point++;
    			figure[point]=figure[point].transFrac(k,1);
        	}
        	else
        	if (postfix[i]=='(') //分数入栈 
        	{
    			double up=0,down=0; //int会计算出错 
    			i++;
    			while (postfix[i]!='\')
    			{
    				up=10*up+postfix[i]-'0';
    				i++;
    			}
    			i++;
    			while (postfix[i]!=')')
    			{
    				down=10*down+postfix[i]-'0';
    				i++;
    			}
    			point++;
    			figure[point]=figure[point].transFrac(up,down);
    		}
    		else
    		{
    			point--;
    			switch (postfix[i])
    			{
    				case '+':figure[point]=figure[point]+figure[point+1];
    				         break;
    				case '-':figure[point]=figure[point]-figure[point+1];
    				         break;
    				case '*':figure[point]=figure[point]*figure[point+1];
    				         break;
    				case '/':figure[point]=figure[point]/figure[point+1];
    			}
    		}
    		i++;
    	}
    	return figure[point].transToString(figure[point]);
    }
    

    提交记录

    因为时间关系,所以提交GitHub很多是由我来做的,实际情况可以看队友博客。如果需要其他的证据截图,会在下次博客补充。

    此处输入图片的描述
    此处输入图片的描述

  • 相关阅读:
    剑指OFFER 连续数组的最大和
    剑指OFFER 两个链表的第一个公共结点
    剑指OFFER 替换空格
    剑指OFFER 二叉树中和为某一值的路径
    剑指OFFER 二叉树的镜像
    剑指OFFER 从上往下打印二叉树
    剑指OFFER 删除链表中重复的结点
    剑指OFFER 数组中只出现一次的数字
    剑指OFFER 调整数组顺序使奇数位于偶数前面
    C 语言 sleep 函数
  • 原文地址:https://www.cnblogs.com/S031602240/p/6399917.html
Copyright © 2011-2022 走看看