65
社区成员
题目修改自现代软件工程作业 – 计算最长英语单词链
实现一个计算最长英语单词链的程序
阅读《构建之法》第一章至第三章的内容,并在下方作业里体现出阅读后的成果。特别是第2章中的效能分析及个人软件开发流程(PSP)。
使用Visual Studio Community 2019/2022进行开发,采用C、C++或者C#语言实现,可以使用.Net Framework,运行环境为64-bit Windows 10/11。
附:下载链接:Visual Studio 2022, Visual Studio 2019,下载也推荐使用国内一些镜像站点(不是盗版)MSDN,I tell you
提交的代码要求经过Code Quality Analysis工具的分析并消除所有的警告。
完成项目的首个版本之后,请使用性能分析工具Studio Profiling Tools来找出代码中的性能瓶颈并进行改进。
使用单元测试对项目进行测试,并使用插件查看测试分支覆盖率等指标;并写出至少10个测试用例确保你的程序能够正确处理各种情况。
使用Github/Gitee/GitLink(以下简写为Github)来管理源代码和测试用例,代码有进展即签入Github,签入记录不合理的项目会被助教抽查询问项目细节。
按照要求发布博客,利用在构建之法中学习到的相关内容,结合个人项目的实践经历,撰写解决项目的心路历程与收获。博客与Github项目明显不符的作业将取消作业成绩。
实现一个命令行程序Wordlist.exe
,对于包含有 N个不同的英语单词的文本,要求程序可以快速找出最长的能首尾相连的英语单词链,注意每个单词最多使用一次,且单词大小写不敏感:
其中,单词的定义为:被非英文字符间隔的连续英文字符序列
单词链的定义为:由至少2个单词组成,前一单词的尾字母为后一单词的首字母,且不存在重复单词
例如,给出单词文本为:
Hello WoRld! Softw
are_eng1neer
从文本中可以提取出的单词为:
hello world softw are eng neer
在默认情况下,输入的单词文本需要保证无法构成英语单词环(即末尾单词尾字母为首个单词首字的母单词链),对于不满足该条件的单词文本,程序不应该求解,而是给予用户错误提示。
我们对最长的定义分为两种:最多单词数量和最多字母数量。
在命令行中使用-n
参数统 计该单词文本中共有多少条单词链,包含嵌套单词链。
如:
Wordlist.exe -n absolute_path_of_word_list
注意:
输入示例:
woo oom moon noox
这里在满足不包含单词环的情况下,包含6个单词链,请在返回单词链数6的同时,每一行输出一条单词链,不同单词用空格分隔。
输出示例:
6
woo oom
moon noox
oom moon
woo oom moon
oom moon noox
woo oom moon noox
在命令行中使用 -w
参数加文件名的形式计算最多单词数量的英语单词链,并将结果输出至文件,例如:
Wordlist.exe -w absolute_path_of_word_list
程序将从路径中读取单词文本,并将最长单词链输出至与Wordlist.exe
同目录的solution.txt
中,每次生成的txt文件需要覆盖上次生成的txt文件。
假如可能有多组最长的相连英语单词串,选取其中任意一组作为结果即可
程序需要对异常情况进行处理,包括但不限于:文件不存在、文件不合法等,对于上述异常情况,程序需要向用户返回提示信息
输入示例:
Algebra
Apple
Zoo
Elephant
Under
Fox
Dog
Moon
Leaf
Trick
Pseudopseudohypoparathyroidism
输出示例:
将结果输出到文件中,每行仅包含一个单词,单词为小写,例如:
algebra
apple
elephant
trick
在命令行中使用-m
参数加文件名的形式计算首字母不能相同的包含单词数量最多的单词链,并将结果输出至文件,例如:
Wordlist.exe -m absolute_path_of_word_list
程序将从路径中读取单词文本,并将最长单词链输出至与Wordlist.exe
同目录的solution.txt
中,每次生成的txt文件需要覆盖上次生成的txt文件。
假如可能有多组最长的相连英语单词串,选取其中任意一组作为结果即可
程序需要对异常情况进行处理,包括但不限于:文件不存在、文件不合法等,对于上述异常情况,程序需要向用户返回提示信息。
输入示例:
Algebra
Apple
Zoo
Elephant
Under
Fox
Dog
Moon
Leaf
Trick
Pseudopseudohypoparathyroidism
输出示例:
将结果输出到文件中,每行仅包含一个单词,单词为小写,例如:
apple
elephant
trick
注:-m 参数不要求与后面的 -h,-t,-r 复合使用。
在命令行中使用 -c
参数加文件名的形式计算字母最多的英语单词链,并将结果输出至文件,例如:
Wordlist.exe -c absolute_path_of_word_list
如果有多组字母数最多的单词链,选取其中任意一组作为结果即可。
程序需要对异常情况进行处理,包括但不限于:文件不存在、文件不合法等,对于上述异常情况,程序需要向用户返回提示信息。
输出示例:
pseudopseudohypoparathyroidism
moon
在命令行中使用 -h
参数加字母的形式,指定单词链的首字母,例如:
Wordlist.exe -h e -w absolute_path_of_word_list
输出示例:
elephant
trick
在命令行中使用 -t
参数加字母的形式,指定单词链的尾字母,例如:
Wordlist.exe -t t -w absolute_path_of_word_list
输出示例:
algebra
apple
elephant
需要注意的是,-h
和-t
参数允许复合使用,此时需要同时满足首字母和尾字母的条件。
本需求为性能测试部分主要考察内容,请同学们选取合适的算法并进行优化
在命令行中使用 -r
参数,表示允许单词文本隐含单词环,注意文本中每种单词只能使用一次(即使单词在文本中多次出现,也只能使用一次),如:
Wordlist.exe -r -w absolute_path_of_word_list
输入示例:
Element
Heaven
Table
Teach
Talk
输出示例:
table
element
teach
heaven
此处table
-element
构成单词环,如果命令行中不包含 -r 参数,则此种情况应当做异常处理。
现在已经有了一个单词链计算程序的命令行版本,如果想让大家都能实际使用它,还需要一个简单的界面。请为你们的程序做一个GUI界面(不限制GUI的编程语言),并附上一个简单的使用说明。界面需正确实现下述功能,会按点给分:
-n -w -m -c -h -t -r
这七个参数的功能,对于异常情况需要给予用户提示(3')【注意】选择完成本附加题目的同学,需要将GUI与单词计算模块作为两个工程开发,后者可以作为依赖库为前者提供调用接口,但不可以把两个工程直接混在一起。 GUI相关的部分也需要提供新的可执行文件,放在根目录的guibin/文件夹下。
项目要求:
git tag step1
标记第一阶段已经完成,并在Push到Github上时使用--tags
参数把tag也推送到Github,例如git push origin --tags
。在第一阶段中,我们使用各种语言实现了一个命令行求解最长单词链的小程序。下面我们将逐步将我们的小程序升级为能稳定运行,给用户提供服务的软件。
大家的代码都各有特色,大家写的“软件”也有一定的用处。如果现在我们要把这个功能放到不同的环境中去(例如,命令行,Windows图形界面程序,网页程序,手机App),就会碰到困难:许多同学的代码都散落在各个函数中,很难把剥离出来作为一个独立的模块运行以满足不同的需求。
我们看到,不同的代码解决不同层面的问题:
我们希望把计算单词链的功能独立出来,成为一个独立的模块(class library, DLL, 或其它),这样的话,命令行和GUI的程序都能使用同一份代码。为了方便起见,我们称之为计算核心"Core模块",这个模块至少可以在几个地方使用:
但我们知道软件并非只有计算核心,实际的软件是交付给最终用户的软件,除了计算核心外,还需要有一定的界面和必要的辅助功能。那么这个Core模块和使用它的其他模块之间是什么关系呢?它们要通过一定的 API(Application Programming Interface) 来和其他模块交流。这个API接口应该怎么设计呢?为了简单,我们可以从下面的最简单的接口开始:
int gen_chain(char* words[], int len, char* result[]);
这个函数接受三个参数,words
为输入的单词列表,len
为单词列表的长度,result
存放单词链,函数返回值为单词链长度。
假设我们用类Core
封装了这个接口,我们的测试程序可以是非常简单的:
char* input[4] = {"END", "OF", "THE", "WORLD"};
char* result[4] = {0};
/* 调用Core中封装好的函数 */
int len = Core::gen_chain(input, 4, result);
Assert(len == 2);
当然,我们这里的判断并不充分,仅判断了单词链长度,没有判断单词链的合法性,但同学们在测试时不能这样“偷懒”。
我们要把第一阶段中实现的功能封装成独立的模块并一一进行测试,比如读取单词文本文件、输出打印等。建议大家在每一步只增量修改一个模块并做测试。这里的测试包括新模块的单元测试与原功能的回归测试。每实现一个新的功能,要保证以前运行正确的例子继续是正确的。通过这样的回归测试,可以保证自己实现的系统始终是满足预定状态约束的。(请看书中关于单元测试,回归测试的内容)在确认修改的功能正确之后再签入代码。
项目要求:
core.dll
的动态链接库模块中,并通过下述API接口或者自定义类似API接口与命令行测试程序和GUI程序等进行交互。int gen_chain_word(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
接口,计算最多单词数量的最长单词链,其中前三个参数已经在上文进行了说明,head
和tail
分别为单词链首字母与尾字母约束(如果传入0,表示没有约束),当enable_loop
为true
时表示允许输入单词文本中隐含“单词环”int gen_chains_all(char* words[], int len, char* result[])
接口,参数的意义同上,函数返回所有符合定义的单词链,函数返回值为单词链的总数int gen_chain_word_unique(char* words[], int len, char* result[])
接口,参数的意义同上,函数返回首字母不同的,单词数最多的单词链,函数返回值为单词链的长度int gen_chain_char(char* words[], int len, char* result[], char head, char tail, bool enable_loop)
接口,计算最多字母数量的最长单词链,参数意义同gen_chain_word
gen_chain_word
,gen_chains_all
,gen_chain_word_unique
和gen_chain_char
或其他自定义接口进行测试,确保模块的所有API接口都进行了单元测试,把单元测试代码Push到Github上(注意避免把单元测试的结果Push到Github上)git tag step2
标记第二阶段已经完成,并在Push到Github上时使用--tags
参数把tag也推送到Github,例如git push origin --tags
。博客要求:
在上面我们只讨论了正确的输入下,我们对于程序输出的期待。但如果程序的输入出现了错误,比如命令行参数是其他字符,或者有多个无意义参数等等,你又该怎么办呢?要怎样才能告诉函数的调用者“你错了”?又该如何方便地告诉函数的调用者“哪里错了”?在这种时候,我们一般会定义各种异常(Exception),让Core
在碰到各种异常情况的时候,能给调用者充分的错误信息。当然,我们同样要进行增量修改:
项目要求:
Core
模块中实现抛出异常的功能,并撰写测试用例:传进去一个错误的参数或给出一个错误的单词文本,期望能捕获这个异常。如果没有,测试就报错。git tag step3
标记第三阶段已经完成,并在Push到Github上时使用--tags
参数把tag也推送到Github。博客要求:
在前面的工作中,有的小组使用命令行,保证了程序的正确性,还有的小组在此基础上绘制了GUI界面。既然各组同学都写了高质量的各个模块,而且模块之间的关系是明确定义的,一致的,那么,小组A的测试模块就可以测试小组B的核心模块;小组C的用户界面模块就可以和小组B的核心模块结合起来,正常运行。对吧?
那么现在,请你(假设为A)寻找另外一个小组(假设为B),与他们交换核心模块与界面模块,并测试一下下面的情况:
项目要求:
根据与合作小组对接过程中出现的问题,寻找并改进模块中的bug。这部分修改需要另开一个新的分支dev-combine
,并Push到Github上。
博客要求:
在博客中指明合作小组两位同学的学号,分析两组不同的模块合并之后出现的问题,为何会出现这样的问题,以及是如何根据反馈改进自己模块的。
测试内容
我们都知道健壮性对于软件来说是非常必要的,所以本次自动测试我们也会加入各种各样出错情况的测试。助教测试时将会选择不同种类的出错场景,要求开发者程序不会崩溃的情况下,能够尽可能精确报错(就像编译器一样)。你可以有“容错性”的出错设计,但必须输出必要的提示或说明。
测试包括三部分:正确性测试(正确场景)、鲁棒性测试(错误场景)、性能测试,提交的所有程序都要进行正确性测试和鲁棒性测试,正确性测试通过的程序需要进行性能测试。
测试须知
提交到 Github 上的项目请注意以下几点:
/bin/
的文件夹(这里 /
表示项目的根目录),该文件夹中必须含有可执行文件与相关的所有依赖库。Wordlist.exe
,位于 /bin/
中。solution.txt
与可执行文件在同一目录下,生成文件时请使用相对路径!一个示例组织目录如下所示:
WordListProject/ # 项目名字可自行指定
├── bin # 助教测试用文件夹
│ ├── Wordlist.exe
│ ├── core.dll # 核心模块 DLL
| └── lib.dll # 其他模块 DLL
├── README.md
├── src
│ └── main.cpp
└── test # 测试相关代码
└── test_wordlist.cpp
注:GitHub 上的项目不应包含 WordListProject
文件夹,而应包含其内容。即项目主页应该看到 bin/
,README.md
,src/
和 test/
,而非仅有 WordListProject/
。
发表在你的个人博客上,也可以同时转发到你的团队博客上来增加你们团队博客的人气。博客共 50 分,具体要求如下:
源代码管理评分(10'):
该评分主要通过源代码管理中的commit注释信息,增量修改的内容,是否有运行说明,每个阶段是否打上了标签等内容给分。
第一阶段(35'):
该评分将进行这-n -w -m -c -h -t -r
七个参数的正确性测试,对于前六个参数,输入的单词数量范围为0-10000;对于-r
参数,输入单词数量的范围为0-100,要求程序在 300s 内给出结果,超时则认定运行结果无效。
第二、三阶段(35'):
将针对上述七个参数进行鲁棒性测试,可能测试的内容包括且不限于:
错误的命令、错误的参数、大小写、错误的参数组合、错误的文件格式等。
要求必须正常结束,崩溃不得分。
错误无任何提示,不得分。
错误种类较多,提示合理,得正分。
性能评分(20')
当第一阶段评分等于35分时才可以参与性能评分环节,所以请各位同学务必保证自己程序的正确性,该阶段没有时间的最小要求限制。
性能评分将采取档级评分制度,助教将根据同学们的程序跑同一数据耗费的时间长度将程序分为若干档,每一档的同学得到的分数为20/档级数。
附加需求:GUI(10')
该评分将进行用户交互界面的测试
第四阶段:模块松耦合(10')
在结对项目博客中按照阶段四的博客要求添加相应内容(5') 最终的对接效果(5')
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | ||
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | ||
· Design Spec | · 生成设计文档 | ||
· Design Review | · 设计复审 (和同事审核设计文档) | ||
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | ||
· Design | · 具体设计 | ||
· Coding | · 具体编码 | ||
· Code Review | · 代码复审 | ||
· Test | · 测试(自我测试,修改代码,提交修改) | ||
Reporting | 报告 | ||
· Test Report | · 测试报告 | ||
· Size Measurement | · 计算工作量 | ||
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | ||
合计 |
助教在测试时,将以命令行运行可执行文件的方式进行批量测试,参数及其约定如下:
参数名字 | 参数意义 | 范围限制 | 用法示例 |
---|---|---|---|
-n | 需要求出单词文本能够构成所有单词链的数目 | 绝对或相对路径 | 示例:Wordlist.exe -n input.txt [表示从input.txt中读取单词文本,计算能构成单词链的总数目] |
-w | 需要求出单词数量最多的单词链 | 绝对或相对路径 | 示例:Wordlist.exe -w input.txt [表示从input.txt中读取单词文本,计算单词数量最多的单词链] |
-m | 需要求出单词链中每个单词首字母不同,包含单词数量最多的单词链 | 绝对路径或相对路径 | 示例:Wordlist.exe -m input.txt [表示从input.txt中读取单词文本,计算首字母不同而包含单词数量最多的单词链] |
-c | 需要求出字母数量最多的单词链 | 绝对或相对路径 | 示例:Wordlist.exe -c input.txt [表示从input.txt中读取单词文本,计算字母数量最多的单词链] |
-h | 指定单词链首字母 | a-z,A-Z | 示例:Wordlist.exe -h a -w input.txt [表示从input.txt中读取单词文本,计算满足首字母为a的、单词数量最多的单词链] |
-t | 指定单词链尾字母 | a-z,A-Z | 示例:Wordlist.exe -t a -c input.txt [表示从input.txt中读取单词文本,计算满足尾字母为a的、字母数量最多的单词链] |
-r | 允许单词文本中隐含单词环 | 示例:Wordlist.exe -r -w input.txt [表示从input.txt中读取单词文本,计算单词数量最多的单词链,即使单词文本中隐含单词环也需要求解] |
にげるんだよ!