第四次作业-四则运算

前言

  •  作业要求地址: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     }  
c++

  

  不仅仅是计算,括号的生成顺序也是这个要求的难点。也可以定义栈用逆波兰的方法来解决。

  因为我和我的结对伙伴都想用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

 

原文地址:https://www.cnblogs.com/vrocker/p/7650258.html