第四次作业-四则运算
前言
• 作业要求地址:https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/997
• 结对伙伴:宋雨http://www.cnblogs.com/songyuu/
• git:https://git.coding.net/Vrocker/f4.git
要求一
• 具体要求:参考《构建之法》第4章两人合作,结对编程上述功能,要求每人发布随笔1篇 (代码是共同完成的,博客是分别完成的)。 (1) 给出每个功能的重点、难点、编程收获。(2)给出结对编程的体会,以及 (3) 至少5项在编码、争论、复审等活动中花费时间较长,给你较大收获的事件。
一、每个功能的重点、难点
1.功能一
实现这个功能的重点是在于随机生成参与运算的数字和四个运算符。在Python中,random模块可以用于生成随机数。这样很方便的使用了这一模块下的部分函数,来实现这一功能。
部分代码如下:
def _f4(self): from random import randint as r from random import uniform as ru from fractions import Fraction as f ops = ['+', '-', '*', '/'] bra = ['(', '', ')'] _l1 = r(0, 1) _l2 = r(0, 1) _l3 = r(0, 1) _r1 = r(1, 2) _r2 = r(1, 2) _r3 = r(1, 2)
2.功能二
这个功能中加入了括号的运算。括号参与运算是最大的难点。
本科期间学习的数据结构这门课程中,在讲到栈与队列的时候,逆波兰即后缀表达式是栈在表达式求知中的应用。这个应用是栈比较常见的,而且十分常用。所以看到这个要求加上题目中老师的提醒,我就开始翻找数据结构书来寻找相应的方法。同时用栈可以很好的解决括号匹配,这样一来两者结合可以通过栈来完成需求。经过查阅数据结构书加上自己的思考尝试解决这一难题。
括号匹配算法总结如下:
1、初始化一个空栈,开始顺序读入括号。
2、如果是右括号,则或者是置于栈顶的最急迫期待得以消解,或者是不合法的情况(括号序列不匹配,退出程序)。
3、若是左括号,则作为一个新的更急迫的期待压入栈中,自然使原有的在栈中的所有未消解的期待的急迫性降了一级。算法结束,栈为空,否则括号序列不匹配。
根据这一算法,可以解决括号匹配的问题。
同时表达式求值的实现是栈应用的一个典型的例子。中缀表达式不仅依赖运算符的优先级,同时还要处理括号。后缀表达式的运算符在操作数后面。在后缀表达式中已考虑了运算符的优先级,没有括号,只有操作数和运算符。并且中缀表达式和后缀表达式可以相互转换。通过后缀表达式计算相应式子的值,其过程为:
1、顺序扫描表达式的每一项
2、如果是操作数,则将其压入栈中
3、如果是操作符<op>,则连续从栈中推出两个操作数,x和y,形成运算的指令x<op>y
4、将计算的结果重新压入栈中。
5、当表达式所有的项豆扫描并且处理完毕后,栈顶存放的就是最后的计算结果。
根据书中的介绍,栈可以很好的解决这一需求。在最开始分配任务的时候,我一开始想用c++来编写程序,因为自己相对于其他语言比较熟悉c++,所以在考虑这一个问题的时候我当时也是想要采用c++来实现。自己根据书中的范例代码并且查阅了网上相关代码,尝试进行改写,来满足题目需求。
首先读取随机生成的表达式,然后用上次作业用到的vector,构造vector<string>储存成普通的中缀表达式然后遍历vector<string>再用刚刚提到的算法利用栈来构造后缀表达式。最后遍历构造好的vector<string>中存放在后缀表达式对弹出栈的两个操作数进行运算,将结果压入栈中。则最后剩余在栈中的一项就是计算的最终结果。c++的部分代码如下:
1 for (auto &oper : vec) { 2 if (oper == "+" || oper == "-") { 3 while (true) { 4 if (sta.empty() || sta.top() == "(") 5 break; 6 p_vec.push_back(sta.top()); 7 sta.pop(); 8 } 9 sta.push(oper); 10 } 11 else if (oper == "*" || oper == "/") { 12 while (true) { 13 if (sta.empty() || sta.top() == "+" || 14 sta.top() == "-" || sta.top() == "(") 15 break; 16 p_vec.push_back(sta.top()); 17 sta.pop(); 18 } 19 sta.push(oper); 20 } 21 else if (oper == ")") { 22 while (true) { 23 if (sta.top() == "(") { 24 sta.pop(); 25 break; 26 } 27 p_vec.push_back(sta.top()); 28 sta.pop(); 29 } 30 } 31 else sta.push(oper); 32 } 33 while (!sta.empty()) { 34 p_vec.push_back(sta.top()); 35 sta.pop(); 36 }
不仅仅是计算,括号的生成顺序也是这个要求的难点。也可以定义栈用逆波兰的方法来解决。
因为我和我的结对伙伴都想用python来完成这次作业,所以如果要用栈那么c++的代码就要改写成python的。一开始在看题目要求的时候,观察到操作数是固定的四个数字,就采用直接固定四个操作数,没有采用栈而是直接判断括号的优先级,来生成括号并且计算。在之后的python编写中因为我自己对python学习还是太少,编写不出python的逆波兰方法。在尝试多次后,甚至想把作业重新用c++编写,但是换编程语言的工作量太大,所以就用比较原始的方法完成这个需求。
在此之后还会不断尝试用python实现逆波兰。
3.功能三
这个功能的主要难点是在于命令行参数的实现。
在上次作业中,c++语言的命令行参数花费了我很多时间才会完全使用。python中可以用argparse标准库解决。它是解析命令行参数和选项的标准模块。在学习argparse模块的使用方法后,实现了python 的命令行参数功能。总体感觉比c++要简便不少。
部分代码如下:
1 import argparse 2 parser = argparse.ArgumentParser() 3 parser.add_argument("-c", "--cin") 4 args = parser.parse_args() 5 if args.cin == None: 6 f4()._f4_input() 7 else: 8 f4()._f4_integer_parser(args.cin)
4.功能四
这个功能中,最重要的就是支持分数。这个功能宋雨同学花费了很长时间解决了这个难点很多的功能。经过他的讲解介绍,用fractions标准库可以实现这一功能。采用python内置函数eval()。但eval()函数的输出为浮点数,故采用这一函数生成的分数分子分母位数都很多。测试之后发现不满足题目的要求,所以在宋雨同学的解决意见下限制输出的位数。经过实验,发现可以实现。所以采用了这一方法。
部分代码如下:
1 def _f4_answer(self,eq): 2 from fractions import Fraction as f 3 _answer = f(eval(eq)).limit_denominator(1000) 4 _answer = str(_answer) 5 return(_answer)
二、体会
在开始编写程序的时候,决定选择了python进行编写。因为我对python掌握很不好,所以本次主要的代码都是由宋雨同学完成,给我了很多帮助,我也在结对编程的过程中不断学习python。
因为我自己的原因,相比较python,c++我掌握的相对好一些,所以在遇到难题的时候想把代码换成c++。但是因为工作量太大,而且我认识到不能回避难题,所以接着用python进行编写。这次结对编程我体会到相互合作的重要性,结对伙伴不仅能给自己帮助也能共同进步一起努力。
三、花费时间较长,给你较大收获的事件
1、一开始没有采用面向对象来编写代码,在讨论之后将代码改成oop方式。
2、栈的问题花费的时间很多,对python 掌握还是太差。
3、分数功能的实现,包括查资料测试都花费了很多时间。
4、进行单元测试的时候框架换了很多。
5、一起编写的时候,宋雨同学对我讲解了很多python的知识。让我对python的学习有了信心。
要求二
地点:寝室
电脑:msi、win10
软件:pycharm
语言:python
照片:
测试截图
1
2
3