寒假作业2/2 任务一:再次阅读《构建之法》,提出疑问 任务二:wordCount开发

这个作业属于哪个课程 2021春软件工程实践|W班(福州大学)
这个作业的要求在哪里 寒假作业2/2
这个作业的目标 阅读构建之法,完成wordCount程序,撰写博客
其他参考文献 博客园、csdn
GitHub项目地址 https://github.com/LchCangHai/wordcount1

读《构建之法》提出的问题

1.单元测试 中提出的问题

出处:2.1单元测试部分。说其功能是:让自己负责的模块功能定义尽量明确,模块内部的改变不会影响其他模块,而且模块的质量能得到稳定的、量化的保证。 (以及后文的详细介绍,这里不全部引用)

我理解的是测试可以从单元测试到回归测试再到功能测试,变得主要是范围,都还是以单元测试为基础。

我想问的是:单元测试是主要用来测试代码功能是否正确还是主要用来记录相应模块功能并在今后的更新中保持稳定的。如果是用来测试代码功能的话,那要如何确定测试内容,如何保证测试是正确的?书中说单元测试应该由本人完成,但有时候开发人员自身意识不到的问题再测试时也是意识不到的,是否需要多人协助检查或者提前商定标准?如果是用来记录模块功能并在更新中保持稳定的,那么在大型的软件中,每个模块甚至每个模块的不同版本都可能是不同人写的,如何保证测试代码、方式的统一以及解决更新功能修改代码后单元测试代码的不匹配问题?

单元测试代码是必要的吗?还是说有选择性的,只需要在关键的,预料到可能修改的模块上进行测试?

2.软件工程技能的判定中的问题

出处:3.3 巴克斯顿说技能的反面是“Problem Solving”—“解决问题”;

问题:他的意思是:当需要花时间对问题进行解决时,就不算是精通技能。要到将低层次问题的解决熟练到纳入本能了才算是精通。但有的时候会感觉基础技能(也就是低级技能)都掌握的很熟练了,会在看到问题的第一时间想到要怎么解决,但仅仅只是知道解决方法,可能具体细化要某个API,就是会知道这个API的存在,但使用时总得去查询一番(是较少用到或者比较复杂)。这样的情况,算不算精通?有多个老师曾表达过过类似的意思:现在软件编写的语言,种类很多很多,而且编译器也可以完成很多事情,有一些API等知识不必要记得清清楚楚,只需要知道有,了解下,需要时随时可以查。这样的情况我也是可以快速的知道要怎么解决,但只是其中的需要背的部分需要查一下,也算是精通吧。

3.goto的使用问题

出处:4.3.2:函数最好有单一的出口,为了达到这一目的,可以使用goto。只要有助于程序逻 辑的清晰体现,什么方法都可以使用,包括goto

goto一直以来被认为是“有害的”,Dijkstra也曾写过《go to statement considered harmful》。它也有好处:比如避免函数中出现多个return(就是文中说的可以有单一出口),直接跳出多层循环等。

但第4节中主要写代码规范,goto在我看来用起来确实方便但确实看起来很不友好。我觉得他虽然有好处但也尽量不要去使用。

4.团队集体分工

出处:5.2软件团队模式

软件团队是要有各种分工,各司其职。但我们在学校学习的话。情况又会很不一样。又会在有一个学生的身份。若是有人想尽可能的了解,多学习每个职能要做的事的话,会不会贪多嚼不烂?有些人基础较差,成为整个团队进度短板要如何处理?一个团队个分工人数不均衡或者出现偏差(比如产品3个,但没有美术)这要如何解决?

有没有适用于校园的,给自发组建团队或被动组建的团队的一个开发模式?团队成员刻意放纵无所作为,找各种理由推脱不肯配合团队怎么办?还要考虑各方面因素制约。如何真正地约束团队成员?

还有就是:团队模式和团队开发模式有什么区别?

5.创新的问题

看完16章的思考

创新,很多时候只是灵感乍现,如何让想法落地,让想法成为真的创新实践?就算有想法有决心,路在哪里,怎么走?

冷知识

在程序中bug一词用于技术错误。这一术语最初由爱迪生在1878年提出的,但当时并没有流行起来。在这的几年之后,美国上将Grace Hopper在她的日志本中,写下了她在Mark II电脑上发现的一项bug。不过实际上,她说的真的是“虫子”问题,因为一只蛾子被困在电脑的继电器中,导致电脑的操作无法正常运行。如图片所见,她写道“这是我在电脑上发现的第一个bug”。

任务二:wordCount开发

词频统计程序:github地址

1. PSP表格

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

2. 前置概述

使用c++语言进行编程,使用visual studio 2019编译器

共有4个分支:masterunitTest、file、task

  • 其中master即为任务要求代码,直接下载使用即可;
  • unitTest是任务代码的基础上加上了单元测试项目;
  • file是将读入文件与写入文件的地址作为输入。再对文件内容进行统计与结果输出。
  • task是在发现程序使用起来和任务要求不同,从master中分出分支修改后再并回到master中(可以忽略它);

寒假作业2/2
任务一:再次阅读《构建之法》,提出疑问
任务二:wordCount开发

1.git和github使用

git使用可视化工具sourcetree墙裂推荐!);

同时也尝试过大多基本会使用到的git命令行代码;

首次自己完成 .gitignore 文件;

其中多次格式美化,是在觉得自己代码已经完善后提交,但接下来又发现新的问题了。

2.代码规范规定

上传至github中的codestyle.md文件中:链接

目前自身代码不是很清晰,会逐步添加进去。

3.解题思路

题目要求分析

1.输入文件和输出文件以命令行参数传入

最开始觉得是以文件读取和写入的方式来搞,写好了复查的时候发现要求是在命令行。命令行指定的文件是代码的输入和输出部分;

现在的格式是:Main.exe <input.txt> output.exe

感触:将各个功能模块拆开来真的有用!在我发现写错了的时候很好改!!

2.单词:至少以4个英文字母开头,跟上字母数字符号,单词以分隔符分割,不区分大小写

单词的判断方法思路

  1. 遍历判断分隔符:最开始只想到了空格符来判断(没看清题目),后来发现问题很多:分隔符范围较大、单词间分隔符数量不定、单词本身还需要再判定。思考后决定用正则表达式对分隔符全部转化为空格;
  2. 但!既然想到了正则表达式,我可以直接用正则判断并获取单词啊。于是最终方法得出了

使用的正则表达式:string regexp = "[a-zA-Z]{4}[a-zA-Z0-9]*";

3.输出频率最高的10个:1.频率相同的单词,优先输出字典序靠前的单词;2.输出的单词统一为小写格式

储存单词及其个数使用map。map<string, int>

需要进行排序:先根据单词(key)排序,再根据单词出现次数(value)排序;

key排序使用map中自带的方法,value排序则是将map移到vector上再操作。

单词存为小写:在把单词存入字典前先把起转化为全小写单词;

4.其他数量统计

行数统计:是通过逐行读取输入判断计算并最后累积得出最终结果,所以行数就只需要在逐行读取时累加;

字符统计:在逐行统计时直接使用 string.length() 再累加;

4.性能改进

就是最开始使用遍历字符串,逐一判断是否为单词组合部分。后面改变方法,使用正则表达式;

5.接口设计与实现

统计的原理是:逐行读取input文件中的内容。一行一行的判断然后累加;

代码组织

原先设计将每个功能模块的函数都分到不同文件夹中,但发现分开后单元测试会出现链接错误的error,所以最后还是把所有函数都放在了head.h中。

文件head.h中:

有一个类fun;类中有:

属性:myMap(存储单词及其数量的字典)、regexp(判断单词的正则表达时);

方法:

int countWord(string str){}(计算一行(一个字符串中)的单词数)

void getWord(string str){}(获取一行中的单词及其个数存入map中)

string myToLower(string str)(单词转化为小写)

void showWord()(输出单词及其数量)

Main.cpp文件中创建一个fun类,使用这些函数和属性。

接口关键代码展示

1.计算一行(一串字符串)中单词的数量

    //计算一行(一个字符串中)的单词数
    int countWord(string str) {
        regex words_regex(regexp); // 判断单词的正则表达式

        auto words_begin = sregex_iterator(
            str.begin(),
            str.end(),
            words_regex);
        auto words_end = sregex_iterator();

        return distance(words_begin, words_end);
    }

2.记录统计一行中的单词及其个数

//获取一行中的单词及其个数存入map中
    void getWord(string str) {
        regex words_regex(regexp); // 判断单词的正则表达式

        auto words_begin = sregex_iterator(
            str.begin(),
            str.end(),
            words_regex);
        auto words_end = sregex_iterator();

        for (sregex_iterator k = words_begin; k != words_end; ++k) {
            smatch match = *k;
            string match_str = myToLower(match.str());//获取单词
            //////////////将单词放进map统计数量/////////////////
            map<string, int>::iterator iter1;
            iter1 = mymap.find(match_str);
            if (iter1 == mymap.end()) {
                pair<string, int> value(match_str, 1);
                mymap.insert(value);
            }
            else {
                mymap[match_str]++;
            }
        }

    }

3.其余功能接口

    // 转化为小写
    string myToLower(string str) {
        for (int i = 0; i < str.length(); ++i) {
            str[i] = tolower(str[i]);
        }
        return str;
    }
    //输出单词及其数量
    void showWord() {
        map<string, int>::iterator strmap_iter2 = mymap.begin();
        for (;strmap_iter2 != mymap.end();strmap_iter2++)
        {
            cout << strmap_iter2->first << ' ' << strmap_iter2->second << endl;
        }
    }

6.单元测试

unitTest分支中

计算单词数量(10个例子)和获取单词(3个例子)两个功能进行了单元测试

计算单词数量中就包含了单词的判断;

刚写好单元测试的时候就判断出了写的算法有大问题。于是进行了修改

寒假作业2/2
任务一:再次阅读《构建之法》,提出疑问
任务二:wordCount开发

最后结果:

寒假作业2/2
任务一:再次阅读《构建之法》,提出疑问
任务二:wordCount开发

有对其中“获取一行中的单词及其个数存入map中”这个方法进行重新的设计。因为原来的方法没有返回值。其本质代码没变

 //测试用函数
    int getWordTest(string str, string exam) {
        regex words_regex(regexp); // 判断单词的正则表达式
        map<string, int> mymap;
        auto words_begin = sregex_iterator(
            str.begin(),
            str.end(),
            words_regex);
        auto words_end = sregex_iterator();

        for (sregex_iterator k = words_begin; k != words_end; ++k) {
            smatch match = *k;
            string match_str = myToLower(match.str());//获取单词
            //////////////将单词放进map统计数量/////////////////
            map<string, int>::iterator iter1;
            iter1 = mymap.find(match_str);
            if (iter1 == mymap.end()) {
                pair<string, int> value(match_str, 1);
                mymap.insert(value);
            }
            else {
                mymap[match_str]++;
            }
        }
        return mymap[exam];
    }

7.心路历程与收获

  1. 一定要先仔细的梳理一遍题目要求!单设计代码的时候如果有发现什么规则。规范第一步先去看看题目中有没有,不要自己瞎琢磨。不然写好后要不停改;

  2. 单元测试真的挺香的,我原先觉得写这个就是浪费时间。现在觉得,即使它没检查出不对,但在写单元测试的过程中会对自己的代码更了解,会更容易发现问题进而去完善;