項目成員:劉躍群(3116005189)、張凱亮(3116005205)
項目倉庫:https://github.com/wean2016/Myapp
PSP2.1表格
| PSP2.1 | Personal Software Process Stages | 預估耗時(分鍾) | 實際耗時(分鍾) |
|---|---|---|---|
| Planning | 計划 | 60 | 45 |
| · Estimate | · 估計這個任務需要多少時間 | 60 | 45 |
| Development | 開發 | 1420 | 1985 |
| · Analysis | · 需求分析 (包括學習新技術) | 100 | 200 |
| · Design Spec | · 生成設計文檔 | 20 | 20 |
| · Design Review | · 設計復審 (和同事審核設計文檔) | 30 | 30 |
| · Coding Standard | · 代碼規范 (為目前的開發制定合適的規范) | 100 | 100 |
| · Design | · 具體設計 | 50 | 55 |
| · Coding | · 具體編碼 | 1000 | 1400 |
| · Code Review | · 代碼復審 | 100 | 60 |
| · Test | · 測試(自我測試,修改代碼,提交修改) | 20 | 30 |
| Reporting | 報告 | 80 | 130 |
| · Test Report | · 測試報告 | 30 | 32 |
| · Size Measurement | · 計算工作量 | 20 | 21 |
| · Postmortem & Process Improvement Plan | · 事后總結, 並提出過程改進計划 | 30 | 77 |
| 合計 | 1560 | 2260 |
設計實現過程及代碼說明
1. 生成表達式樹
**
* 構造表達式
* @param symbolSum 剩余符號數
* @param bound 表達式鍾的數字不大於 bound
* @return 生成的節點樹的根
*/
def createNodeTree(symbolSum:Int, bound:Int):Node = {
if (symbolSum == 0){
// 中間節點構造完成,生成葉子節點
Node(Number.randomNumber(bound), null, null, 0, null)
}else {
val left = Random.nextInt(symbolSum)
val right = symbolSum - left - 1
var leftNode = createNodeTree(left,bound)
var rightNode = createNodeTree(right,bound)
// 本節點的符號
var symbol = Symbol.values()(Random.nextInt(Symbol.values().length))
// 如果是除號且右節點是 0 ,那么重新生成一個不是除號的符號
if (symbol == Symbol.DIV && rightNode.value.a == 0){
while (symbol == Symbol.DIV){
symbol = Symbol.values()(Random.nextInt(Symbol.values().length))
}
}
// 本節點的結果
var value = Calculate.calc(leftNode.value,rightNode.value,symbol).get
if (Number.isNeg(value)) {
// 如果運算結果是負數,交換左右節點,並把結果取絕對值
value = Number.abs(value)
val tempNode = leftNode
leftNode = rightNode
rightNode = tempNode
}
val high = math.max(leftNode.high, rightNode.high) + 1
// 生成本節點
Node(value,leftNode,rightNode,high,symbol)
}
}
2. 表達式樹比較去重
override def equals(obj: Any): Boolean =
obj match {
case that: Node =>
if (that.symbol == null) {if (symbol == null ) value.equals(that.value) else false}
else if (symbol == null) false
else if (left.equals(that.left) && right.equals(that.right) && symbol==that.symbol) true
else if ((symbol==Symbol.ADD || symbol==Symbol.MULT) && left.equals(that.right) && right.equals(that.left)) true
else false
case _ => false
}
override def hashCode(): Int = {
var result = value.hashCode()
result = 31 * result + (if(left == null) 0 else left.hashCode())
result = 31 * result + (if(right == null) 0 else right.hashCode())
result = 31 * result + (if(symbol == null) 0 else symbol.hashCode())
result
}
3. 打印表達式
def printNode(node: Node):String = {
if (node == null) return ""
val midValue = if (node.symbol == null) node.value.toString else node.symbol.value
// 左右的原始值
var leftValue = printNode(node.left)
var rightValue = printNode(node.right)
// 判斷是否要加括號,如果要,就加上去
if (node.symbol != null && (node.symbol == Symbol.MULT || node.symbol == Symbol.DIV)){
val leftSymbol = node.left.symbol
val rightSymbol = node.right.symbol
if (leftSymbol != null && (leftSymbol == Symbol.ADD || leftSymbol == Symbol.SUB)) leftValue = Bracket.leftBracket.value + leftValue + Bracket.rightBracket.value
if (rightSymbol != null && (rightSymbol == Symbol.ADD || rightSymbol == Symbol.SUB)) rightValue = Bracket.leftBracket.value + rightValue + Bracket.rightBracket.value
}
leftValue + midValue + rightValue
}
4. 從字符串生成表達式樹(用於計算成績時構造表達式樹)
def fromString(s:String): Node = {
// 克隆輸入的字符串,實現函數式編程
var value = new String(s.toCharArray)
// 給符號都加上空格
Symbol.values().foreach(symbol => {
if (symbol.value.equals("+")){
value = value.replaceAll("\\+", " %s ".format(symbol.value))
}else{
value = value.replaceAll(symbol.value, " %s ".format(symbol.value))
}
})
// 給括號都加上空格
Bracket.values().foreach(bracket => value = value.replaceAll("\\" + bracket.value, " %s ".format(bracket.value)))
//去空格生成元素數組
val factor = value.split(" ").toStream.map(s => s.trim).filter(s => !s.isEmpty).toList
val nodeStack: util.Stack[Node] = new util.Stack[Node]
val symbolStack: util.Stack[String] = new util.Stack[String]
for (f <- factor) {
// 嘗試轉換成數字
val value = Number.fromString(f)
if (value.isDefined) {
// 是數字
nodeStack.push(Node(value.get, null, null, 0, null))
} else {
// 是符號
var signal = true
while (!symbolStack.empty() && !f.equals(Bracket.leftBracket.value) && !((f.equals(Symbol.MULT.value) || f.equals(Symbol.DIV.value)) && (symbolStack.peek().equals(Symbol.ADD.value) || symbolStack.peek().equals(Symbol.SUB.value))) && !(!f.equals(Bracket.rightBracket.value) && symbolStack.peek().equals(Bracket.leftBracket.value)) && signal) {
val symbol = symbolStack.pop()
if (symbol.equals(Bracket.leftBracket.value) && f.equals(Bracket.rightBracket.value)) {
signal = false
} else {
val rightNode = nodeStack.pop()
val leftNode = nodeStack.pop()
val sym = symbol match {
case "+" => Symbol.ADD
case "-" => Symbol.SUB
case "×" => Symbol.MULT
case "÷" => Symbol.DIV
case _ => sys.error("非法運算符!")
}
val result = Calculate.calc(leftNode.value, rightNode.value, sym)
if (result.isEmpty) {
sys.error("存在不符合規范的算術表達式!")
}
val high = math.max(leftNode.high, rightNode.high) + 1
nodeStack.push(Node(result.get, leftNode, rightNode, high, sym))
}
}
if (!f.equals(Bracket.rightBracket.value)) {
symbolStack.push(f)
}
}
}
while(!symbolStack.isEmpty){
val symbol = symbolStack.pop()
if (!symbol.equals(Bracket.leftBracket.value)){
val rightNode = nodeStack.pop()
val leftNode = nodeStack.pop()
val sym = symbol match {
case "+" => Symbol.ADD
case "-" => Symbol.SUB
case "×" => Symbol.MULT
case "÷" => Symbol.DIV
case _ => sys.error("非法運算符!")
}
val result = Calculate.calc(leftNode.value, rightNode.value, sym)
if (result.isEmpty) {
sys.error("存在不符合規范的算術表達式!")
}
val high = math.max(leftNode.high, rightNode.high) + 1
nodeStack.push(Node(result.get, leftNode, rightNode, high, sym))
}
}
nodeStack.pop()
}
測試運行
生成問題

生成答案

生成成績

把一些題目的答案改成錯誤

項目小結
通過結對編程,兩個人互相修改代碼,共同提高了能力,也寫出了更好的代碼。確實是種不錯的寫代碼方式
