寒假作业2/2

这个作业属于哪个课程 2021春软件工程实践 W班 (福州大学)
这个作业要求在哪里 寒假作业2/2
这个作业的目标 阅读《构建之法》并提问、完成词频统计个人作业
其他参考文献

目录:

  1. 阅读《构建之法》并提问
  2. WordCount编程

1. 阅读《构建之法》并提问

过早优化、泛化是思维误区,但是我感觉等整个项目代码都敲完再去优化的话会有一种牵一发而动全身的感觉(优化了某一部分的代码导致其它地方出现问题),那么优化最好的时机是什么时候?

  • 在书本的p53有提及到,过早优化、泛化是思维误区。
  • 就我个人经验而言,到了项目的最后阶段再优化可能已经太迟了,想要优化的话往往都到了需要重构的地步。

就一个团队而言,每个人的工作都应该是要并行的吗?

  • 书本中的p270提到。一些没有经验的工程师觉得,“我现把代码写好,然后有一些会画图的人来把姐妹改一改就好了。。“,这种想法是非常幼稚和有害的。
  • 就我个人经验而言,因为我平常从事的都是前端的工作,个人认为会比较受限于后端和UI设计的进度。我通常都要等到后端的接口写好了我才能去编写逻辑代码,等UI设计出来后我才能编写界面的代码。如果要与后端和UI设计同时进行工作的话,到了最后我前端很可能要做大幅度的修改迎合他们,但等他们做完了我再做,往往奋战到最后的都是我。所以我就不太清楚要如何推进工作会比较好。

想在程序里加点需求没有提出的功能是否妥当?

  • 书本中p334中提到。小飞认为某某模块很烂,需要重写,而且现在又有了新的技术十分时髦,可以做很酷的效果,为什么不呢?
  • 确实,很多程序员(包括我)都有这样的冲动,之前开发过的一个购买网课的系统,灵机一动加了个购物车,当时在我心里看起来很炫酷,但是我现在发现有点傻,因为购买网课的人很少会一次过购买多个课程,有点多余。

互联网行业发展十分迅速,在进行技术选型的时候,我们应该选择稳定的技术还是有小风险的最新的技术?

  • 国内的一些网站也许是为了易维护和省钱而迟迟不换掉flash,即使是在adobe提前宣布停用flash都不换,直到出现了问题才换。
  • 思考了一下这个问题,或许我们可以在一开始选择稳定的技术,在发布以及正式上线之后,可以随着互联网的发展去更新技术。
  • 也许到万不得已的时候,需要开展一个全新的项目去取代旧有项目。

关于结对编程,这会使得参与的成员压力过大,导致反作用吗?

  • 结对编程有全天候注视自己的队友,会感动压力,灵感也会少很多。
  • 有时候我编程的时候遇到难题,我会让自己离开电脑一段时间,如何就会有灵感,茅塞顿开了。
  • 而且俗语也有说一山不容二虎,队员可能会因为一些问题产生矛盾。。
  • 不过结对编程肯定是有他的好处的,但也不知道怎么权衡什么时候应该结对,什么时候应该单独。

冷知识

  • git是一个十分伟大的版本管理软件,然而他背后却有着耐人寻味的故事:

你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。
不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。
安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。
Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:
Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。
Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。
https://www.liaoxuefeng.com/wiki/896043488029600/896202815778784

  • 个人感想:优秀的开源软件(git)的确可以干掉收费软件,再次体现了开源的优势(免费、可修改)
  • 虽然故事听得很爽,但是犯法的事情千万不要做哦(指破解软件)!

2. WordCount编程

Github项目地址

Github项目地址

PSP表格

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

解题思路描述

  • 就作业要求中的统计字符数、统计单词数、统计最多的10个单词及其词频,首先想到的是要把这三个功能分拆为三个函数,并放在不同文件中,利用export和require引入不同的这三个模块
  • 不知道node的单元测试怎么做,得第一时间去找资料

代码规范制定链接

代码规范

设计与实现过程

整体设计

  • 分为WordCount(主函数),analyseChar(统计字符数)、analyseLine(统计行数)、analyseMostFrequentWords(统计最多的10个单词及其词频)、analyseWord(统计单词数)四个文件
  • WordCount负责读入文件

文件的读入

  • 利用fs.readFileSync一次性全部读入

识别单词的核心代码

  • 考虑到分隔符有不同类型,不能粗暴正则匹配
  • 使用逐字扫描,判断单个字的类型
  • 如果是字母的话进行单词长度的统计
  • 如果碰到数字,判断是否已有4个字母,否则进入判断失败状态,直到遇到分割符为止
  • 碰到分割符的时候,如果单词长度大于4则加入单词数组,否则将所有状态标志和计数重置
  • 在读入最后一个字的时候需要判断当前计数并且是否加入单词数组
  • 统计最多的10个单词及其词频同理,但稍有改动,这里就不贴它的代码了
function analyseWord(text) {
    let words = []
    let wordLetters = 0
    let newWord = ''
    let beforeSeparatorDefeat = false
    for (let i = 0; i < text.length; i++) {
        const c = text[i]
        switch (true) {
            case isLetter(c):
                if(!beforeSeparatorDefeat){
                    wordLetters++
                    newWord += c
                    if (i === text.length - 1 && wordLetters >= 4) {
                        words.push(newWord)
                    }
                }
                break;
            case isNumber(c):
                if (wordLetters < 4) {
                    wordLetters = 0
                    beforeSeparatorDefeat = true
                    continue
                } else {
                    newWord += c;
                    if (i === text.length - 1 && wordLetters >= 4) {
                        words.push(newWord)
                    }
                }
                break
            default:
                if (wordLetters >= 4) {
                    words.push(newWord)
                }
                wordLetters = 0
                newWord = ''
                beforeSeparatorDefeat = false
        }
    }
    return words.length
}

性能改进

  • 有考虑过使用文件流读取应对大文件情况,但显然效率不如一次性读入...
  • 如果有上述情况的话,可以利用已经写好的独立模块,写另外一个程序进行处理
  • 想分割文本并利用多线程提升效率,但node是单线程的
  • 所以还是一次性读入减少IO次数

单元测试

覆盖率:

思维导图

统计字符数的测试

测试了\r是否有被计入字符数

describe('test analyseChar', () => {
    it('hello\\r\\nworld! should be 13', () => {
        assert.strictEqual(analyseChar('hello\r\nworld!'), 13)
    })

    it('hello\\nworld! should be 12', () => {
        assert.strictEqual(analyseChar('hello\nworld!'), 12)
    })
})

统计单词数的测试

测试了以下情况:

  • 不含数字超过4字单词
  • 前4个字含有数字
  • 纯数字
  • 没有东西
  • 字母中间夹数字
  • 字母最后带数字
  • 分辨空格
describe('test analyseWord', () => {
    it('balabala should be one', () => {
        assert.strictEqual(analyseWord('balabala'), 1)
    })

    it('ba1abala should be zero', () => {
        assert.strictEqual(analyseWord('ba1abala'), 0)
    })

    it('1balabala should be zero', () => {
        assert.strictEqual(analyseWord('1balabala'), 0)
    })

    it('0 should be zero', () => {
        assert.strictEqual(analyseWord('0'), 0)
    })

    it('none should be zero', () => {
        assert.strictEqual(analyseWord(''), 0)
    })

    it('balabala1234balabala should be one', () => {
        assert.strictEqual(analyseWord('balabala1234balabala'), 1)
    })

    it('bala1234 should be one', () => {
        assert.strictEqual(analyseWord('bala1234'), 1)
    })

    it('balabala1234 balabala should be two', () => {
        assert.strictEqual(analyseWord('balabala1234 balabala'), 2)
    })

})

统计行数的测试

测试了没有字符、只有制表符、只有空格的空行是否有被计入

describe('test analyseLine', () => {
    let test_text = '\n\t\t\t\r\nbalabala\t\n balalbala123\n\nbala\nnnn123'
    it( `test_text should be 5`, () => {
        assert.strictEqual(
            analyseLine(test_text), 5)
    })
})

异常处理说明

  • 这次程序比较简单,有可能出现异常的地方在读入和写入,所以在这两个地方加入异常处理
//读入文件
try {
    let text = fs.readFileSync(path.resolve(commandPath, inputFile), 'utf-8')
} catch (exception) {
    console.log('Error:', exception.message)
    console.log('请检查文件是否存在/是否有足够权限')
}
//写入文件
try {
    fs.writeFileSync(resultFile, '')
    fs.appendFileSync(resultFile, `characters: ${charNumber}\n`)
    fs.appendFileSync(resultFile, `words: ${wordNumber}\n`)
    fs.appendFileSync(resultFile, `lines: ${lineCount}\n`)
    frequentWords.forEach((item) => {
        fs.appendFileSync(resultFile, `${item.word}: ${item.number}\n`)
    })
} catch (exception) {
    console.log('Error:', exception.message)
}

心路历程与收获

  • 复习了一下js的知识
  • 学会了单元测试这一个概念,特别是js的,以往我都是不停更改代码来看结果,没想到还有这么方便的东西,感觉自己都要变成精致的猪猪男孩了!
  • 写代码的时候想的没有那么全面,单元测试后才发现错误很多,可以看出来单元测试的必要性,并且要细心阅读题目!!!
posted @ 2021-03-05 21:56  CLH029  阅读(77)  评论(2编辑  收藏  举报