软件工程实践2018第二次作业——个人项目

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 40 50
· Estimate · 估计这个任务需要多少时间 40 50
Development 开发 300 820
· Analysis · 需求分析 (包括学习新技术) 30 20
· Design Spec · 生成设计文档 20 10
· Design Review · 设计复审 10 20
· Coding Standard · 代码规范 (为目前的开发制定合适的规范) 10 10
· Design · 具体设计 30 30
· Coding · 具体编码 150 400
· Code Review · 代码复审 30 30
· Test · 测试(自我测试,修改代码,提交修改) 60 300
Reporting 报告 40 90
· Test Report · 测试报告 30 60
· Size Measurement · 计算工作量 10 10
· Postmortem & Process Improvement Plan · 事后总结, 并提出过程改进计划 10 20
合计 380 960

解题思路

对于这个题目来说读取字符数和行数是基本操作,题目的难点在于按照给定规则对文本进行划词,以及在单词数量大时如何保证词频查找更新的空间复杂度,一开始时我想直接使用标准模板库中的map来进行单词频率的存储,但后来查看其底层实现时发现C++的map是基于BST实现的。虽然每次查找有O(logN)的时间复杂度,但当单词数量大时,时间复杂度依然太高,后来在 畅畅 同学的提醒下,我使用基于哈希实现的unordered_map来存储单词及其词频,通过牺牲空间复杂度换取每次查找O(1)的时间复杂度。

接口设计与实现过程

需求分析:

  • 读取txt文件中内容(基础功能)

  • 统计文件的字符数(基础功能)

  • 统计文件的单词总数(基础功能)

  • 统计文件的有效行数(基础功能)

  • 统计文中的各单词的出现次数,最终输出频率最高的10个(拓展功能)

  • 接口封装(拓展功能)

实现过程

程序基本框架:根据需求分析我将整个程序划分为5个主体部分

  • 文件读取与接口调用部分

  • 字符数统计部分

  • 行数统计部分

  • 单词统计部分

  • 单词词频统计部分

接口设计:

  • 字符数统计部分:int CountChar(char *filename)
  • 行数统计部分:int CountLine(char *filename)
  • 单词统计部分:int CountAllWords(char *filename)
  • 词频统计部分:vector<pair<string, int>> top10words(char *filename)

各部分实现:

  • 文件读取实现:通过使用C++的fstream类读取文件流

  • 字符数统计部分:每次从文件流中读取一个字符并计数

  • 行数统计部分:运用C++的getline函数从fstream对象中按行读取并计数

  • 单词统计部分:首先需要按照规则划分出每个单词,其次对每个单词进行计数

    • 划分单词:根据C++ fstream类的get()方法在按string读取时会自动按空格划分的特点,首先读入由空格划分的字符串,在此基础上需要对这个字符串内部再进行切分比如出现‘hello,world’时,程序应该准确讲此字符串划分为‘hello’和‘world’,我采用的方法是通过遍历字符串,并设置is_head,not_a_word两个flag来完成string内划词的操作
    • 流程图
      划词
    • 核心代码
      划词核心
  • 单词词频top10:使用基于priority_queue实现的小顶堆来维护top10词频队列,当队列中元素不足十个时直接入队,当队列中满时,对栈顶元素与欲入队元素进行比较,首先比较词频,词频相同比较字典序

    • 流程图
      top10 (2).png
    • 核心代码
      top10core

性能分析及改进

在性能分析中我循环执行如下文本1000000次

h
e
l
l
o
hello
hello123
123hello
  hello
HELLO
123file

performenceA

performenceB

performenceC

可以发现性能瓶颈主要出现在文件读取以及删除空格单词的词频上,通过代码逻辑优化后,执行同样的测试,运行时间缩短了3秒

opt

单元测试改进

十个单元测试

  • 文本不存在

  • 无参数输入

  • 空文本

  • 单词以数字开头

  • 大小写单词

  • 其他字符

  • 单词长度

  • 参数超过限制

  • 文本包含10个以上的不同单词

  • 文本只包含空格、换行符与制表符

  • 10个测试通过

TEST

代码覆盖率

代码覆盖率

代码覆盖率低的filedetect.cpp,是因为没被覆盖的代码是应对错误参数输入设计的

异常处理说明

  • 针对参数输入错误的异常处理:
    • 当无文本参数输入时
    • 当输入参数过多时
    • 文本文件不存或无法打开时

errorPrevent

  • 针对空文本输入时,防止优先队列容量为0时的访问越界情况:

errorPrevent1

心得体会

  • 通过这次个人项目,我逐步开始体会到软件工程这门课程的重要性,以前写一些大点的程序,在代码改动时整个项目就像倒过来的金字塔,改动任何一处都会导致一连串错误的发生。在这次实践中我按照PSP表格对项目的规划一步一步地完成了整个项目,而不像从前为了尽快完成作业,用一种写了再说的方式来对待项目。这种步步为营的计划也许就是软件工程中“工程”二字的含义所在吧。毫无疑问,按照这种方式写出的软件结构性更强,而且各个模块也能包产到户,使得项目进展更容易衡量。

  • 针对PSP表格中的结果,我对自己的时间预计出现了很大偏差,特别是测试部分的时间预估远远不足,这和我对单元测试的认识有很大关系,相信随着之后的学习,对项目的时间规划会越来越趋于实际

  • 以前对github以及git的认识一直停留在纸面上,虽然知道它们是coder强大的工具,但一直觉得自己写小项目用github有大材小用的感觉,直到这次在项目进行时全程按照规范,一旦项目进展立刻签入github,我才对github有了真正可感的认识。在实现业务逻辑时不用畏手畏脚的感觉实在太棒了。

  • 此外这也是第一次为自己写的软件写单元测试,终与知道还有这样相对轻松且自动化的测试方式

posted @ 2018-09-10 21:06  B3njamin  阅读(270)  评论(1编辑  收藏  举报