2019软工实践_作业2

GitHub
恳请测试的同学,一定要记得看README,去多下载一个我的.json文件和python放同一子目录下再进行评测,不然就爆0了。当然,如果爆0了,恳请告知我一声
这是我博客开通到现在,写得最难受还最长的博客,真是累了

PSP2.1Personal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划1530
Estimate估计这个任务需要多少时间14001580
Developm开发210600
Analysis需求分析(包括学习新技术)150150+
Design Spec生成设计文档6060
Design Review设计复审1010
Coding Standard代码规范(为目前的开发制定合适的规范)1010
Design具体设计60120
Coding具体编码210600
Code Review代码复审1530
Test测试(自我测试,修改代码,提交修改)120240
Reporting报告6060
Test Report测试报告120120
Size Measurement计算工作量120120
Postmortem & Process Improvement Plan事后总结,并提出过程改进计划3030
合计14001580

解题思路:
一、思考
1、难度1!2!:字符串处理
2、难度3!:补全之后的字符串处理(补全之后,调用2!级别难度代码即可)
二、查找资料
1、学习了一下正则在python下的使用(主要匹配手机号、门牌号、路名等)。
2、下载了一个四级字典来暴力匹配省(直辖市自治区)、市、地区等等。

实现过程:
一、思考(1!2!难度)
1、预处理:摘掉头部(','),去掉尾部('.'),拿出手机,放心食用。
2、省、市……分别封装,直到所下载字典的无法继续匹配的最低一级。
3、1中剩余部分,根据所到级数(记一个标志看到哪一级停止)来寻找对应关键字(镇、街道……),从而获得该级地址,若此时获得的为4级地址,则结束,余下部分即为5级地址。
4、根据输入难度,决定是否解析5级地址至7级。
5、3!的难度即为调用在线api之后,丢入2!难度处理代码(先得到5级地址,进而解析为7级地址)。
二、编码
1、面对样例编程的C++代码夭折。
2、Lv.5 $\rightarrow$ Lv.7 $\rightarrow$ 3!难度的python代码作为提交。
3、函数流图:1329707-20190917102727905-936546280.png

4、关键接口(代码过于冗长,故以接口说明替代):1329707-20190917105033519-1855622553.png

单元测试:
一、手动测试
2!李四,福建省福州13756899511市鼓楼区鼓西街道湖滨路110号湖滨大厦一层.
'地址': ['福建省', '福州市', '鼓楼区', '鼓西街道', '湖滨路', '110号', '湖滨大厦一层']
1!张三,福建福州闽13599622362侯县上街镇福州大学10#111.
'地址': ['福建省', '福州市', '闽侯县', '上街镇', '福州大学10#111']
2!王五,福建省福州市鼓楼18960221533区五一北路123号福州鼓楼医院.
'姓名':'王五'
3!小美,北京市东15822153326城区交道口东大街1号北京市东城区人民法院.
'地址': ['北京市', '北京市', '东城区', '交道口街道', '交道口东大街', '1号']}(调用api补全缺失最小一级,而输入串又有缺失。解决思路:可将五级地址丢入api补全以后再解析,同时将5级剩余的详细地址存住,最后加上7级解析剩余地址的结果,即为答案)
1!小陈,广东省东莞市凤岗13965231525镇凤平路13号.
'手机': '13965231525'

二、单元测试代码

class Test_For_UnitTest(unittest.TestCase):
    
    @classmethod
    def tearDownClass(self):
        pass
    
    @classmethod
    def setUpClass(self):
        pass
    
    
    def test_get_name(self):
        _in = open('./input.txt')
        _out = open('./output_name.txt')
        for _data, _label in zip(_in.readlines(), _out.readlines()):
            _predict = get_name(_data)
            self.assertEqual(_label.rstrip('\n'), _predict)
        _in.close()
        _out.close()
    
    def test_auto_geo_completion(self):
        _in = open('./input_broken.txt')
        _out = open('./output_completion.txt')
        for _broken in _in.readlines():
            nw = _broken.rstrip('\n')
            ret = _auto_geo_completion(nw)
            _out.write(ret+'\n')
        _in.close()
        _out.close()

    def test_get_tel(self):
        _in = open('./input.txt')
        _out = open('./output_tel.txt')
        for _data, _label in zip(_in.readlines(), _out.readlines()):
            _predict = ''.join(get_tel(_data))
            self.assertEqual(_label.rstrip('\n'), _predict)
        _in.close()
        _out.close()

PS:为什么有了单元测试代码,仍然还要手动测试呢?其实,单元测试的代码写起来很快,但是我解决报错问题解决了很久,仍然没有找到解决方案。报错的原因就是某些自定义函数和系统库均会报错(NameError)。吐了啊
正常情况(仅测试名字时:)
1329707-20190917213900896-1280387843.png
全部模块测试:
1329707-20190917213238820-918253161.png
我not defined你个大头鬼啊
三、尝试解决过程:
1、调整顺序被测试函数和测试函数顺序(不要问为什么有这么蠢的操作,虽然事后我也觉得蠢,但是我真的去做过了)。结果:失败。
2、我瞟了一眼warning平时都不看warning的,无非也就是警告文件没关,你都给我异常了,我拿什么给你关文件啊。结果:补一下try-catch。

class Test_For_UnitTest(unittest.TestCase):
    
    @classmethod
    def tearDownClass(self):
        pass
    
    @classmethod
    def setUpClass(self):
        pass
    
    
    def test_get_name(self):
        _in = open('./input.txt')
        _out = open('./output_name.txt')
        for _data, _label in zip(_in.readlines(), _out.readlines()):
            _predict = get_name(_data)
            self.assertEqual(_label.rstrip('\n'), _predict)
        _in.close()
        _out.close()
    
    def test_get_tel(self):
        _in = open('./input.txt')
        _out = open('./output_tel.txt')
        #_see = open('./see.txt')
        for _data, _label in zip(_in.readlines(), _out.readlines()):
            try:
                _predict = ''.join(get_tel(_data))
                #_see.write(predict+'\n')
                self.assertEqual(_label.rstrip('\n'), _predict)
            except Exception as NameError:
                _in.close()
                _out.close()
        _in.close()
        _out.close()
        #_see.close()
    
    
    def test_auto_geo_completion(self):
        _in = open('./input_broken.txt')
        _out = open('./output_completion.txt')
        for _broken in _in.readlines():
            nw = _broken.rstrip('\n')
            try:
                ret = _auto_geo_completion(nw)
                _out.write(ret+'\n')
            except Exception as NameError:
                _in.close()
                _out.close()
        _in.close()
        _out.close()

结果:
1329707-20190917215748481-723052599.png
!!!我可以,我真的可以,它好了。其实,并没有好,因为我在try的时候尝试写文件(_see.txt,test_get_tel()代码中注释部分),结果发现文件里并没有东西,只是因为加了异常处理使得程序能够跑完而已。(表面ok,其实还是gg)

性能改进:
1、主要是想把python写得短一点,葬送了4小时无果,不太熟悉此类代码如何减少编码复杂度。
2、因为输入的极短(每条地址信息),几乎没有考虑过时间复杂度,满屏的库操作。
3、函数开销(三幅图分别对应1,2,3难度,在命令行得到结果后,写入文件):
1329707-20190917230021196-351053125.png
1329707-20190917230031203-502994200.png
1329707-20190917230040899-2103010031.png
可以看出3难度下,调用高德地图在线api时,程序的时间开销瞬间上涨。
参数解释:
ncalls:表示函数调用的次数。
tottime:表示指定函数的总的运行时间,除掉函数中调用子函数的运行时间。
percall:(第一个 percall)等于 tottime/ncalls。
cumtime:表示该函数及其所有子函数的调用运行的时间,即函数开始调用到返回的时间。
percall:(第二个 percall)即函数运行一次的平均时间,等于 cumtime/ncalls。
filename:lineno(function):每个函数调用的具体信息。
参考:使用 profile 进行python代码性能分析
大量数据结果:
1329707-20190917225502775-1756075404.png
没想到就160条数据,就得跑这么久

异常处理:
1、对于3!难度下,若样例前4级地址缺失,代码会报错无法运行,针对此在得知该条信息的需求难度时,若为3!,先进行补全。
2、字典的键值错误(KeyError),主要是通过最后一个样例发现(广东省……),于是增加了标志信息。
3、上面单元测试的异常处理,能算么?满(恬)怀(不)期(知)待(耻)
4、发现评测时会出现ValueError,故捕获了之后就能正常输出。

try:
        _json_info = {'姓名':'','手机':'','地址':[]}
        s = input()
        s = s[:len(s)-1]
        _op, s = get_op(s, '!')
        _op = int(_op)
        
        _name = get_name(s)
        #print(_name)
        _json_info['姓名'] = _name
        s = s[s.find(',')+1:]
        
        _tel = ''.join(get_tel(s))
        #print(_tel)
        _json_info['手机'] = _tel
        s = clr(s,_tel)
        
        if (_op==1):
            _addr, _res = _analysis_lv5(s, dic)
            _addr.append(_res)
            _json_info['地址'] = _addr
        elif (_op==2):
            _addr, _res = _analysis_lv5(s, dic)
            _more_detail = _analysis_lv7(_res)
            _addr = _addr + _more_detail
            _json_info['地址'] = _addr
        else:
            _full_addr = _auto_geo_completion(s)
            #print('full_addr = ',_full_addr)
            _addr, _res = _analysis_lv5(_full_addr, dic)
            _more_detail = _analysis_lv7(_res)
            _json_info['地址'] = _addr + _more_detail
    except ValueError:
        print(json.dumps(_json_info, ensure_ascii=False))
    else:
        print(json.dumps(_json_info, ensure_ascii=False))

心路历程和收获:
我会说我都要写吐了?
  显然,低估了题目难度。我拿到题目之后第一反应就是NLP啊,这一个礼拜做个鬼啊,但是在看了一眼各种友好的输入约束,我就直接想——这不就是个几乎不用考虑时间复杂度的字符串处理吗?可以顺心所欲地用stl了,于是我在作业布置的当天(2019.9.10)敲了一下午,晚上回来准备就寝之时,被群友告知C++要注意编码(需要utf-8,而默认是GBK),可以使用python等云云。差点翻身下来打技术助教舍友周六早上(2019.9.14,期间都在准备网络赛事宜)犹豫了一下,再加上看到了同学python简短的代码,我上了python的船,开始怎么简单编码怎么来,怎么好写怎么来,没多久就写完了5级解析(然而,按照同学的说法我写的python一点都不pythonable),自己想想怎么这么丑呢?算了,有空改,于是乎我在(2019.9.15)晚上,看着我冗长的代码,我进行了重构!4小时后,我放弃了,我把自己秀没了,乖乖地用了周六写的代码,加了点东西,写出了7级解析。接下来是附加题,本来不想做附加题的,因为显然就是NLP的课题,但是!群里说可以调在线api!我真是服了,然后(当然不是马上),我就去调了api,勉强算是写完了附加题(效果不好,没想到怎么处理最后补全之后解析,具体建筑物缺失的问题)。
  就是在得知可以调用在线api的时候,技术助教(也就是我隔壁床的狗头舍友)发了一个分享,果然biLSTM的大字赫然在列(为什么需要有记忆性?盲猜是因为补全的话,显然是需要记忆性的,例如:福州闽侯,倘若没有记忆性,万一还有别的地方叫闽侯,结果就可能不唯一或是出错,这显然不是我们想要的,因为我们在之前限定了是福州的闽侯),而这不调用接口肯定难度上升很多,并且效果会差很多,甚至写出个不work的东西。而在我看来,如果不调用接口,我的想法就目前是爬下完整字典,然后匹配了。 而至于调用api,则是将残缺地址先转为经纬度,然后再从经纬度转回完整地址(两次api调用)。
  (吐槽,非战斗人员请迅速撤离)据说评测没有部分分,就是地址一旦出错,该点爆0,建议修改评测机制(建议改成地址部分分)。泱泱华夏,地名汤汤,鄙人才疏学浅,如何毫厘不差?感觉自己就是个天坑,从第一次用SIFT提取32x32的灰度图的沙雕操作之后,怎么打python怎么踩坑,当然有才疏学浅的原因,奇怪的是每次踩坑都有“沙雕”网友也踩过,总能找到一些类似的解决方案,试着试着就过了,在自闭中冷静,在冷静中自闭(哇,自闭了,怎么会有这种错误$\rightarrow$xswl,有个沙雕网友也遇到了,那应该是能解决了$\rightarrow$服了啊,怎么对我没用啊$\rightarrow$......解决了,可是为什么?)真的打的我好气,边骂边打。未完待续仍有一丝侥幸,最后自闭实属原地自爆。

转载于:https://www.cnblogs.com/FormerAutumn/p/11520213.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值