程序设计第三次作业---C++计算器雏形
- 题目:程序设计第三次作业
 
- 程序设计第三次作业附加
 
我的程序设计第三次作业附加 代码规范
更新时间:2016/3/23
第一次尝试:(此时题目尚未更改,我按要求把-和数字连起来输出)
我在学习完相关的C++类的知识还有STL中stack和queue的用法之后,大概捋清了题意。用不到十五分钟的时间敲出了代码1的大体,但还是由于自己对相关知识的不熟悉,修改大大小小的错误花费了我一天的时间,代码1如下。
- 代码1
 .cpp:
#include<stdlib.h>
#include<iostream>
#include<string>
#include<queue>
#include<cstdio>
using namespace std;
class Scan
{
	// private section
	public:
		void ToStringQueue(string input);
};
void Scan::ToStringQueue(string input)
{
	string s;//Define a string "s",and store(存储) something by using it.
	queue<string>que;//Define a queue "que".
	int m,n,i,j,t=0;
	n=input.size();//Define int n,"n = string's length".
	for(i=0;i<n;i++)//mistake:i=1;i<=n;
	{
		if(input[i]=='-')
		{
			t=1;
			que.push(s);//
			s.clear();//clear s 
			s+=input[i];
			continue;
		}
		else
		{
			if(t==1)// '-'
			{
				if(input[i]=='('||input[i]==')'||input[i]=='+'||input[i]=='*'||input[i]=='/')
				{
					t=0;
					que.push(s);
					s.clear();
					s+=input[i];
					que.push(s);//que.push(input[i]);
					s.clear();
				}
				else
				{
					s+=input[i];
			        continue;
				}
			}
			else
			{
				if(input[i]=='('||input[i]==')'||input[i]=='+'||input[i]=='*'||input[i]=='/')
				{
					que.push(s);
					s.clear();
					s+=input[i];
					que.push(s);//que.push(s);
					s.clear();
				}
				else
				{
					s+=input[i];
					continue;
				}
			}
		}
	}
	m=s.size();
	if(m!=0)que.push(s);//check s
	s.clear();
	while(que.empty()==0)//mistake:j<n;
	{
		cout<<que.front()<<endl;
		que.pop();
	}
}
int main()
{
	int n=0,i,j;
	Scan sc;//实例化对象miss
	string b;
	cin>>b;
	sc.ToStringQueue(b);
	return 0;
}
//example:
//-100+(98-97)*2
//-100+(98+97)*2
//100*(99-98)
//100*10+1000
//100*10-100*(98*97-1)+100
//
- 输入样例:
 100*10-100*(98*97-1)+100- 运行结果:
 
思路分析:
大概来阐述一下我的思路,在
void Scan::ToStringQueue(string input)接收完队列input以后,首先定义一个空字符串s和一个类型为queue<string>的队列que(定义语句:queue<string>que;),其次利用string的函数input.size()把字符串input的长度(一共有几个字符)赋值给int类型的n,再利用for循环对一个个字符进行判断。我还定义一个int类型的变量t:
int t=0;,并且利用t的值进行状态判断。这里使用bool变量进行判断也是可以的。如果此时for循环处理的字符
input.[i]是-(这里利用==判断两个字符是否相同),把t的值转换成1。而语句que.push(s);s.clear();是把s之前储存的内容push到队列que,再利用函数s.clear()清除字符串里的字符。然后把-存储到字符串s里:s+=input[i];(这里我犯了一个错误),continue。然后对下一个元素进行判断,如果是数字再利用+=存储到字符串s,利用continue,一直到下一个字符串input的元素不是数字为止,比如-100+,s存储到-100的时候,把s的内容push到队列里,再清空字符串s,对下一个元素进行判断。如果不是数字,而是+,/,*,(,)这样的符号的话,和上面同理,先把s先前存储的内容push,清空s,再利用+=把此时判断的字符存储到字符串s上,再push到队列que,清空s。
- 简单来说,如果遇到
 -,把t的值转为1,再看下一个元素,如果下一个元素是数字再进行存储的操作;如果是其他字符就把s存储的先前内容push,清空s,再把s现在要存储的东西push,清空s,把t的值转为0。- 在for循环结束后,有如下代码:
 
	m=s.size();
	if(m!=0)que.push(s);//check s
	s.clear();
利用函数
s.size()把此时字符串的长度赋值给int类型的m,判断m是否为0,如果不为0,说明s仍有存储的内容尚未push,把剩下的内容push并清空s。
根据上面的测试结果,读者会发现,输出结果多了几个行,而且输出并没有按照要求放在一个类里面。
为什么会多输出
\n(换行符)呢,这个问题我们先放一放。关于第二个问题,代码1是我的最开始的测试代码,它用于验证我的大体思路和想法是否正确,与最后的成品相比亘长且不成熟。
在代码1的编写过程中我遇到了大大小小的问题,下面即是我对这些问题的一个总结和反思。
问题1:for(i=1;i<=n;i++)
这个问题跟我之前给数组元素赋值有关,如果有n个变量需要我输入,我喜欢从a[1]开始(假如定义了一个数组a),这给我带来了方便,但是也是一个坏习惯。
而此处,我输入一个字符串input,比如是-100+99,-就是由input.[0]存储的。
我在发现这个问题以后把i=1改成了i=0,但是i<=n忘记更改了,于是输出结果的后面莫名多了个a。
最后发现它的时候我也是醉了...马虎过头。
- 解决方法:
 for(i=0;i<n;i++);
问题2:que.push(input[i]);
这是我之前的push语句,编译并没有通过,主要原因是队列que存储的是string类型,定义que的语句:
queue<string>que;,而input[i]是字符类型,与函数push传递的变量类型string不相符。
- 解决方法:自定义一个空字符串s,来储存字符
 input.[i]。
问题3:主函数里未实例化对象。
这是C++类的知识,在这个.cpp文件里我定义了一个Scan类,并且类外定义了函数:
void ToStringQueue(string input),这与::运算符有关。
而在主函数中如果想要调用Scan类中声明的函数ToStringQueue,就必须实例化一个Scan类的对象。
参考:实例化对象
- 解决方法:在主函数里面定义一个对象sc:
 Scan sc;然后通过调用函数ToStringQueue:
sc.ToStringQueue(input)- 关于"::"运算符
 
第二次尝试(-单独一行):
在第一次尝试的铺垫和理解下,第二次尝试我试着把class的声明内容放到.h文件内,而其他定义内容则在.cpp文件内进行。
代码2:
.h:
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
/*
 * No description
 */
class Print
{
	public:
		void pout();
};
class Scan
{
	// private section
	public:
	    void ToStringQueue(string input);
};
#endif // CALCULATOR_H
.cpp:
#include "calculator.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
queue<string>que;
string s;
void Scan::ToStringQueue(string input)
{
	int n=input.length(),m=0,i,j;//n是字符串input的长度
	for(i=0;i<n;i++)//n has no define;
	{
		if(input[i]=='-'||input[i]=='+'||input[i]=='('||input[i]==')'||input[i]=='/'||input[i]=='*')
		{
			que.push(s);
			s.clear();
			s+=input[i];
			que.push(s);
			s.clear();
		}
		else
		{
			s+=input[i];
			continue;
		}
	}
	m=s.size();//利用m判断s是否为空串
	if(m!=0)que.push(s);
	s.clear();
}
void Print::pout()//输出函数pout单独声明在out类里
{
	while(que.empty()==0)
	{
		cout<<que.front()<<endl;
		que.pop();
	}
}
int main()
{
	Scan Sc;//实例化对象Sc
	Print Put;//实例化对象Put
	string input;
	cin>>input;
	Sc.ToStringQueue(input);
	Put.pout();
	return 0;
}
//example:-100+(98-97)*2
- 输入样例:
 -100+(98-97)*2- 运行结果:
 
思路分析:
.cpp文件与代码1类似,我这里仍然利用一个空字符串s来存储input的元素。不同的是,由于题目做出了改变,我这里并没有对-进行判断。- 而代码2比较特别的地方在于它的
 .h文件,我把对于Scan和Print类的声明放在了.h文件里,那么.h文件里就是声明类的部分,.cpp文件里是定义函数及其他的部分。
- 注意,我创建的
 .h和.cpp文件都是在同一个工程里(工程1),在.cpp文件的开头需加上:#include"calculator";来包括calculator.h这个头文件。
问题:
因为在这之前有了代码1的实战体验,敲代码2比较快,而且错误少。在刚开始我忘记对n进行赋值(但是对它进行了初始化:
n=0;),导致队列que内并没有push进东西:
没有输出说明循环出了问题,很快我就找到了n并解决了这个问题:n=input.length();
到目前为止,我把代码的大体已经成功的写出来了,现在仍然还有等待我解决的问题和要求:
要求:
- 1)当输入的数字超过10位(包括小数位)时,报错。
 
- 能够处理不超过10位的任意实数。
 
- 代码要有注释,变量名要有意义。
 
- 遵循编码规范(编码规范参考),可读性尽可能好。
 
以下是我根据要求做出的改进:
代码3:
.h文件:
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
/*
 * No description
 */
class Print
{
	public:
		void pout();
};
class Scan
{
	// private section
	public:
	    void ToStringQueue(string input);
};
#endif // CALCULATOR_H
.cpp文件:
#include "calculator.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
queue<string>que; // Define a <string>queue "que";
string s; // Define string s;用空串s存储字符
int t=0; // Define t=0; 来计算位数 
// Using global variables here. 
void Scan::ToStringQueue(string input)
{
    int n=input.length(),m=0,i,j;
	for(i=0;i<n;i++) // Error: n has no define;
	{
		if(t>10) // such as "10,000,000,000"
		{
			cout<<"Error"<<endl;
			break;
		}
		if(input[i]=='-' || input[i]=='+' || input[i]=='(' || input[i]==')' || input[i]=='/' || input[i]=='*')
		{
			t=0; // stop the count
			que.push(s);
			s=""; // 相当于s.clear(); 
			s+=input[i];
			que.push(s);
			s="";
		}
		else
		{
			t++;
			s+=input[i];
			continue;
		}
	}
	if(t<=10) // ifnormal
	{
		m=s.size();
	    if(m!=0)que.push(s);
	    s="";
	}
}
void Print::pout()
{
	if(t<=10) // ifnormal
	{
		while(que.empty()==0) // "que" is not empty
	    {
		    cout<<que.front()<<endl;
		    que.pop();
	    }
	}
}
int main()
{
	Scan Sc; // Define a "Scan" object:"Sc".
	Print Put; // Define a "Print" object "Put".
	string input;
	cin>>input;
	Sc.ToStringQueue(input);
	Put.pout();
	return 0;
}
//-100+(98-97)*2
代码3我使用的是定义全局变量的方法,这样在
void Scan::ToStringQueue(string input)里把应该存储到队列que的值push到que内,再在void Print::pout中输出队列que存储的内容。
这样是比较方便,但是全局变量有其短缺和不利的地方。那么就有我改进它的理由。
第三次尝试:
“调用Scan对象的ToStringQueue(string input),将输入传进去以获取队列。接着将这个队列传入Print对象的方法中,得到输出。”
代码4:
.h文件:
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
/*
 * No description
 */
class Print
{
	public:
		void pout(queue<string>que);//type:queue<string>
};
class Scan
{
	// private section
	public:
	    queue<string>ToStringQueue(string input);//type:queue<string>
};
#endif // CALCULATOR_H
.cpp文件:
#include "calculator.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
int t=0;//Using global variable "t";
queue<string>Scan::ToStringQueue(string input)//type:queue<string>
{
    int n=input.length(),m=0,i,j;
    queue<string>que;
    string s;
    for(i=0;i<n;i++) // Error: n has no define;
    {
	    if(t>10) // such as "10,000,000,000"
	    {
		    cout<<"Error"<<endl;
		    break;
	    }
	    if(input[i]=='-' || input[i]=='+' || input[i]=='(' || input[i]==')' || input[i]=='/' || input[i]=='*')
	    {
		    t=0; // stop the count
		    que.push(s);
		    s=""; // s.clear(); 
		    s+=input[i];
		    que.push(s);
		    s="";
	    }
	    else
	    {
		    t++;
		    s+=input[i];
		    continue;
	    }
    }
    if(t<=10) // ifnormal
    {
        m=s.size();
	    if(m!=0)que.push(s);
	    s="";
    }
    return que;
}
void Print::pout(queue<string>que)
{
    if(t<=10) // ifnormal
    {
	    while(que.empty()==0) // "que" is not empty
	    {
	        cout<<que.front()<<endl;
            que.pop();
	    }
    }
}
int main()
{
    queue<string>que;//type:queue<string>
    Scan Sc; // Define a "Scan" object:"Sc".
    Print Put; // Define a "Print" object "Put".
    string input;
    cin>>input;
    que=Sc.ToStringQueue(input);
    Put.pout(que);
    return 0;
}
//-100+(98-97)*2
问题探究:
这次与前两次不同的地方在于函数ToStringQueue的类型:
queue<string>。在调用该函数之后,函数返回存储字符串的队列que,再传入到函数pout。
这里我也是栽了几次,首先最开始时我是这样定义的:queue ToSringQueue<string input>,编译器很明显告诉我没有queue这个类型,不让我通过。
在定义存储string类型的时候我使用了queue<string>que;这样的语句,类比于之前定义整形变量的int a;的“类型+变量名”,我想queue<string>会不会就是我想要的答案呢?经过编译器的肯定,证明了我的想法是正确的。
解决方法:
queue<string>Scan::ToStringQueue(string input)
样例测试:
样例:
-100+(98-97)*2
运行结果:
在尝试过程中,测试样例无一是令我满意的,原因我之前也有提到,就是多输出了空行。
那么问题出在哪里呢,我首先怀疑的是输入字符串之后我敲的一个回车,但我很快发现这并不影响。于是就把问题锁定在了队列queue上了,输出有空行,说明在入队的时候出现了情况。
测试:
样例1:-100+(98-97)*2
输出结果:
样例2:
10000000000000-10
输出结果:
样例3:
10+(999-10000000000*2)-10*1
输出结果:
由样例2和样例3可以验证“当输入的数字超过10位(包括小数位)时报错”这一部分是正确的。
经过比较长时间的寻找,我得到了答案:
原来的代码片段:
            t=0; // stop the count
		    que.push(s);
		    s=""; // s.clear(); 
		    s+=input[i];
		    que.push(s);
		    s="";
在我停止计数以后(即遇到了除数字之外的其他字符),队列que进行了
que.push(s)操作,倘若之前s存储了内容(比如100),这当然是正确的。那么如果s是空串,传入队列que的也是空字符串,也就造成了输出结果存在空行的情况。
解决方法:
            t=0; // stop the count
		    if(s!="")
                    que.push(s);
		    s=""; // s.clear(); 
		    s+=input[i];
		    que.push(s);
		    s="";
测试:
样例1:-100+(98-97)*2
输出结果:
样例2:
100*10-100*(98*97-1)+100
输出结果:
可以看到,成功解决了输出之间夹空行的问题。
最终的代码:
.h.文件:
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
/*
 * No description
 */
class Input
{
	public:
		string Get();
};
class Print
{
	public:
		void pout(queue<string>que);//type:queue<string>
};
class Scan
{
	// private section
	public:
	    queue<string>ToStringQueue(string input);//type:queue<string>
};
#endif // CALCULATOR_H
.cpp文件
#include "calculator2.h" // class's header file
#include<string>
#include<queue>
#include<stdlib.h>
#include<iostream>
using namespace std;
int t=0;//Using global variable "t";
string Input::Get()
{
	string s;
	cin>>s;
	return s;
}
queue<string>Scan::ToStringQueue(string input)//type:queue<string>
{
    int n=input.length(),m=0,i,j;
    queue<string>que;
    string s;
    for(i=0;i<n;i++) // Error: n has no define;
    {
	    if(t>10) // such as "10,000,000,000"
	    {
		    cout<<"Error"<<endl;
		    break;
	    }
	    if(input[i]=='-' || input[i]=='+' || input[i]=='(' || input[i]==')' || input[i]=='/' || input[i]=='*')
	    {
		    t=0; // stop the count
			if(s!="")//avoid null string
		    que.push(s);
		    s=""; // s.clear(); 
		    s+=input[i];
		    que.push(s);
		    s="";
	    }
	    else
	    {
		    t++;
		    s+=input[i];
		    continue;
	    }
    }
	que.push(s);
	s.clear();
    /*if(t<=10) // ifnormal
    {
        m=s.size();
	    if(m!=0)que.push(s);
	    s="";
    } */
    return que;
}
void Print::pout(queue<string>que)
{
    if(t<=10) // ifnormal
    {
	    while(que.empty()==0) // "que" is not empty
	    {
	        cout<<que.front()<<endl;
            que.pop();
	    }
    }
}
int main()
{
    queue<string>que;//type:queue<string>
    Scan Sc; // Define a "Scan" object:"Sc".
    Print Put; // Define a "Print" object "Put".
    Input Ge;// Define a "Input" object "Ge".
    string input;
    input=Ge.Get();//receieve input.
    que=Sc.ToStringQueue(input);//receieve que.
    Put.pout(que);//printf que.
    return 0;
}
//-100+(98-97)*2
最后我把输入也放在单独的一个class
Input里面了。
- 小结:在我做完这篇随笔的时候,寒假已经接近尾声了,回顾这个不短不长的寒假,我或多或少收获了一些东西。拿这篇随笔来说,我动笔的时候我还不清楚说在这次作业里需要用到queue和string的一些功能,但现在也算是了解了它。当时容易犯下的诸如忘记在main函数里实例化类的对象这样的错误,现在已经很少见了。这次作业让我第一次对项目有所了解和感触,完成它的期间我参阅了一些对我帮助颇深的资料,也有很多贵人相助,感激之余也对下学期的学习生活充满了遐想,自己通过下学期的学习会达到一个怎样的高度?我所面对的挑战和压力(像这次作业)我能不能很好的解决它?像这次的作业,从发布时的一无所知,到翻阅资料初有了解,再到通过不断的实践完成它,我想,这虽不足以回答我的问题,但是就像是一个引领的坐标,使我有了走下去的动力和勇气。
 

                                                                                                                  2016/2/22
附:本文参考资料及书籍:
- 1.面向对象程序设计-C++网易公开课 授课人:翁恺
 - 2.C++远征封装篇 授课人:james_yuan
 - 3.Git教程 廖雪峰的官方网站
 - 4.《挑战程序设计竞赛》第二版
 
感谢 @Sxiaopeng 和 @chs97 朋友的帮助和指导
附:git操作:
- git config --global user.name"Wasdns"
 - git config --global user.email"952693358@qq.com"
 - git init(可以省略,因为之前对
 object-oriented已经执行了该命令)- git add calculator
 - git commit -m'calculator'
 - git push -u origin master
 
程序设计第三次作业---C++计算器雏形的更多相关文章
- 程序设计第三次作业--C++计算器初始部分
		
面向对象程序设计作业3--C++计算器初始部分 Github 链接:https://github.com/luojingzhao/object-oriented/tree/master/calcula ...
 - 第五次程序设计作业 C++计算器雏形 调用文件输入输出
		
一.C++计算器作业系列链接 第三次作业:C++计算器雏形 第三次作业附加:代码规范 第四次作业:命令行的调用及计算 MyGithub 二.本次作业相关 要求:第五次程序设计作业 根据这一次的作业要求 ...
 - C语言程序设计第三次作业--选择结构(1)
		
Deadline: 2017-10-29 22:00 一.学习要点 掌握关系运算符和关系表达式 掌握如何判断两个实数相等 掌握常用数学函数的使用 掌握逻辑运算符和逻辑表达式 理解逻辑运算的短路特性 掌 ...
 - 第三次作业— C++计算器项目的初始部分
		
作业题目: C++计算器项目的初始部分 仓库 代码: Scan.h #ifndef SCAN_H #define SCAN_H #include<string> #include<i ...
 - C语言程序设计第三次作业
		
态度决定一切,无论做什么事情,秉持一个认真的态度,相信一定会让你受益无穷.当提交作业时,如果只是粘贴一下代码和运行结果,那么,你也只是写了一个程序而已,对你自己水平的提升帮助并不大,这次犯的错误或许下 ...
 - 面向对象课程 - 寒假第三次作业 - C++计算器项目初始部分
		
C++计算器项目初始部分 零.项目源文件地址 传送门:calculator 一.项目信息相关: 项目:Calculator 版本:1.0 日期:2016.2.16 实现: 基本的操作界面 对四则运算表 ...
 - C语言程序设计第三次作业——选择结构(1)
		
一.改错题 1.题目计算f(x)的值:输入实数x,计算并输出下列分段函数f(x)的值,输出时保留1位小数. 源程序(有错误的程序): #include <stdio.h> int main ...
 - C语言程序设计第三次作业——选择结构(一)
		
(一)改错题 错误信息: 错误原因:y=1/x后没加分号 改正方法:在其后加上分号 错误信息: 错误原因:if语句后接了:,使else语句找不到对应的if 改正方法:删掉if后的分号 错误信息: 错误 ...
 - C语言程序设计第三次作业  ——   选择结构(1)
		
(一)改错题 计算f(x)的值:输入实数x,计算并输出下列分段函数f(x)的值,输出时保留1位小数. (错误一) 错误原因及改正:第九行语句结尾缺少半角分号,添加分号即可改正 (错误二) 错误原因及改 ...
 
随机推荐
- 虚拟DOM与DOM diff算法
			
虚拟DOM是什么? 一个虚拟DOM(元素)是一个一般的js对象, 准确的说是一个对象树(倒立的) 虚拟DOM保存了真实DOM的层次关系和一些基本属性,与真实DOM一一对应,如果只是更新虚拟DOM, 页 ...
 - 寒假集训——搜索  D - Cubes for Masha
			
#include <stdio.h> #include <stdlib.h> #include <iostream> #include <string.h&g ...
 - Plotagraph软件五分钟光速速成傻瓜教程
			
http://bbs.dji.com/thread-144203-1-1.html 让照片变成动态的
 - go标准库的学习-bufio
			
参考https://studygolang.com/pkgdoc 导入方式: import "bufio" bufio包实现了有缓冲的I/O.它包装一个io.Reader或io.W ...
 - eig()函数求特征值、特征向量、归一化
			
在MATLAB中,计算矩阵A的特征值和特征向量的函数是eig(A),常用的调用格式有 5种:(1) E=eig(A):求矩阵A的全部特征值,构成向量E. 想求最大特征值用:max(eig(A))就好了 ...
 - LeetCode263:Ugly Number
			
public bool IsUgly(int num) { if(num<1) return false; while(num>1) { if(num%2==0) { num=num/2; ...
 - u-boot全面分析
			
uboot主Makefile分析1 uboot住Makefile分析参考:https://www.2cto.com/kf/201607/522424.html uboot version确定(Make ...
 - OpenStack中的虚拟机(/dev/mapper/centos-root)进行磁盘扩容
			
一.虚拟机上先扩展分区: 二.centos系统root登入,新建分区 2.1 [fdisk -l] 最大分区为/dev/sda2,说明新创建的分区将会是sda3(在后面的步骤会进行选择) 2.2 输入 ...
 - BZOJ4860 BJOI2017 树的难题 点分治、线段树合并
			
传送门 只会线段树……关于单调队列的解法可以去看“重建计划”一题. 看到路径长度$\in [L,R]$考虑点分治.可以知道,在当前分治中心向其他点的路径中,始边(也就是分治中心到对应子树的根的那一条边 ...
 - SpringMVC之单/多文件上传
			
1.准备jar包(图标所指必备包,其他按情况导入) 2.项目结构 3.SingleController.java(控制器代码单文件和多文件) package com.wt.uplaod; import ...
 
			
		









