第一次个人编程作业
这个作业属于哪个课程 | 软件工程2024 |
---|---|
这个作业要求在哪里 | 个人项目 |
这个作业的目标 | 1.让学生熟悉一个项目进行的流程; 2.熟悉使用PSP表格记录设计和记录过程; 3.熟悉用git管理项目代码 |
Github链接:GitHub仓库
一、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
Planning | 计划 | 40 | 45 |
· Estimate | · 估计这个任务需要多少时间 | 40 | 45 |
Development | 开发 | 620 | 850 |
· Analysis | · 需求分析 (包括学习新技术) | 70 | 80 |
· Design Spec | · 生成设计文档 | 40 | 60 |
· Design Review | · 设计复审 | 30 | 40 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 30 |
· Design | · 具体设计 | 70 | 120 |
· Coding | · 具体编码 | 240 | 360 |
· Code Review | · 代码复审 | 60 | 80 |
· Test | · 测试(自我测试,修改代码,提交修改) | 80 | 80 |
Reporting | 报告 | 70 | 110 |
· Test Repor | · 测试报告 | 30 | 50 |
· Size Measurement | · 计算工作量 | 15 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 25 | 30 |
· 合计 | 720 | 1005 |
二、计算模块接口的设计与实现过程
设计阶段
在设计阶段,我们首先要考虑代码的组织结构、类与函数的关系,以及算法的关键步骤。对于这个特定的相似度计算程序,我们可以将其设计为以下几个部分:
-
主类(或主函数):负责程序的流程控制,包括接收用户输入、调用文件读取函数、调用相似度计算函数,以及显示结果。
-
文件读取函数:负责打开文件并读取其内容。这个函数应该接受一个文件名作为参数,并返回一个包含文件内容的字符串。如果文件无法打开,该函数应该抛出异常或返回错误代码。
-
相似度计算函数:这是程序的核心部分。它接受两个字符串作为参数,并返回一个表示这两个字符串相似度的浮点数。这个函数内部将实现最长公共子串的查找和相似度计算。
-
辅助函数(可选):可能包括用于标准化文本的函数(如去除标点符号、转换为小写等),以提高相似度计算的准确性。
实现阶段
在实现阶段,我们将根据设计来编写具体的代码。对于相似度计算函数,其关键步骤如下:
-
初始化最长公共子串长度:设置一个变量来记录最长公共子串的长度,初始值为0。
-
嵌套循环遍历两个字符串:使用两个嵌套的for循环来遍历两个输入字符串的所有字符组合。
-
查找公共子串:对于每一对字符组合,检查它们是否相等,并计算以这对字符为起点的最长公共子串的长度。如果找到更长的公共子串,则更新最长公共子串长度。
-
计算相似度:最长公共子串长度找到后,将其除以两个输入字符串中较短的一个的长度,得到相似度。
-
返回相似度:将计算得到的相似度作为函数的返回值。
独到之处
该程序的独到之处在于其简单而有效的相似度计算方法。通过使用最长公共子串作为相似度的衡量标准,该程序能够在一定程度上反映两个文本之间的相似性。虽然这种方法可能不是最准确的,但它实现起来相对简单,且对于某些应用场景来说已经足够满足需求。
此外,该程序还通过读取整个文件内容到内存中来简化处理过程。这种方法对于中小规模的文件来说是有效的,但如果处理大规模文件时可能会遇到内存限制的问题。在这种情况下,可以考虑使用逐行处理或分块处理等技术来优化程序性能。
三、计算模块接口部分的性能改进
改进思路
预处理文本:在进行相似度计算之前,对文本进行预处理,如去除停用词、标点符号、转换为小写等,以减少计算量并提高准确性。
四、计算模块部分单元测试展示
部分单元测试代码展示
以下是针对计算模块中的calculateSimilarity函数编写的单元测试代码示例,使用C++和Google Test框架:
、#include "similarity_calculator.h" // 假设相似度计算函数在这个头文件中声明
、#include <gtest/gtest.h>
TEST(SimilarityCalculatorTest, CalculateSimilarityWithExactMatch) {
std::string str1 = "Hello, world!";
std::string str2 = "Hello, world!";
double similarity = calculateSimilarity(str1, str2);
EXPECT_EQ(similarity, 1.0); // 期望相似度为1.0(完全匹配)
}
TEST(SimilarityCalculatorTest, CalculateSimilarityWithNoMatch) {
std::string str1 = "Hello";
std::string str2 = "World";
double similarity = calculateSimilarity(str1, str2);
EXPECT_EQ(similarity, 0.0); // 期望相似度为0.0(无匹配)
}
TEST(SimilarityCalculatorTest, CalculateSimilarityWithPartialMatch) {
std::string str1 = "Hello, world!";
std::string str2 = "Hello, planet!";
double similarity = calculateSimilarity(str1, str2);
// 由于最长公共子串是"Hello, ",所以相似度应该是7/13
EXPECT_DOUBLE_EQ(similarity, 7.0 / 13.0);
}
构造测试数据的思路
-
正常情况:构造完全匹配、部分匹配和不匹配的字符串对,以验证算法在各种情况下的正确性。
-
边界条件:测试空字符串、单个字符、非常长的字符串等情况,以确保算法能够处理极端情况。
-
特殊字符和格式:包含标点符号、数字、空格、换行符等特殊字符的字符串,以测试算法对这些字符的处理能力。
-
性能考量:构造大型字符串对来测试算法的性能和可扩展性。这可以帮助识别潜在的瓶颈和优化点。
-
错误处理:如果算法应该能够处理错误输入(如nullptr),则编写测试用例来验证这一点。
五、计算模块部分异常处理说明
文件读取异常
-
设计目标:处理当文件无法打开或读取时发生的错误情况。这种异常通常发生在尝试访问不存在、已损坏或权限不足的文件时。
-
单元测试样例:
TEST(FileReaderTest, ReadNonExistentFileThrowsException) {
std::string filename = "non_existent_file.txt";
EXPECT_THROW(readFileContent(filename), FileReaderException);
} -
错误场景:用户提供了一个不存在的文件名来进行相似度计算,程序尝试打开这个文件但失败了,于是抛出FileReaderException异常。
内存不足异常
-
设计目标:处理由于内存不足而无法完成计算任务的情况。这种异常在处理大量数据或非常长的字符串时可能会发生。
-
错误场景:用户尝试比较两个非常长的字符串的相似度,这导致了程序无法分配足够的内存来完成计算,于是抛出MemoryException异常。
算法内部错误异常
-
设计目标:处理在计算相似度时发生的算法内部错误,这些错误可能是由于不可预见的输入或算法实现中的逻辑错误引起的。
-
单元测试样例:
TEST(SimilarityAlgorithmTest, CalculateSimilarityWithSpecialCaseThrowsException) {
// 构造一个特殊案例的输入,该输入已知会导致算法内部错误
std::string str1 = "special_case_input_1";
std::string str2 = "special_case_input_2";
EXPECT_THROW(calculateSimilarityUsingSpecificAlgorithm(str1, str2), AlgorithmException);
} -
错误场景:程序内部在处理一种特殊的字符串组合时发生了未预料的逻辑错误,导致无法继续执行相似度计算,于是抛出AlgorithmException异常。