2016012063 小學四則運算練習軟件項目報告
源碼倉庫地址:https://coding.net/u/deervw/p/four-operations/git
一、需求分析
1.程序可從命令行接收一個輸入參數n,然後隨機產生n道加減乘除習題。
2.每個數字在 0 和 100 之間,運算符3到5個。
3.每道習題要包含3-5種運算符。
4.所出的練習題在運算過程中不得出現負數與非整數。
5.將學號與生成的n道練習題及其對應的正確答案輸出到文件“result.txt”中。
6.支持有括號的運算式,包括出題與求解正確答案;算式中存在的括號必須大於2個,且不得超過運算符的個數。
二、功能設計
1.基本功能:能夠根據用戶輸入的參數n隨機產生n道符合要求的練習題,自動算出答案,並將式子與答案以文檔的形式呈現。
2.擴展功能:支持有括號的運算式,包括出題與求解正確答案。(註意,算式中存在的括號必須大於2個,且不得超過運算符的個數。)
三、設計實現
我設計了一個主函數和兩個自定義函數,都在Main.java
1.判斷類
功能:帶參的有返回值的類,返回的是一個整數。通過返回的參數大小,從而來判斷符號的優先級。
關系:可以直接調用。
2.共因數divisor類
功能:帶參的有返回值的類,返回的是一個整數,該整數是兩個數的最大公因數。此類通過計算兩個數的最大公因數,生成化簡的最終結果。
關系:可以直接調用。
四、算法詳解
1.輸入產生的題目數n:判斷輸入的數是否為整數,若不是則拋出異常。
try { n=Integer.parseInt(args[0]); if(n<=0)System.out.println("請重新輸入整數!"); } catch (Exception e){ System.out.println("請重新輸入整數!"); }
2.生成隨機數和運算符:將運算符存在一個數組arr裏,以便可以隨機生成;用rand()方法可以隨機生成0-1之間的數;若不能整除或出現負數可以使用循環重新生成a和b,直到滿足要求為止。
char[] arr= {‘+‘,‘-‘,‘*‘,‘÷‘}; int temp=rand.nextInt(4); charArr=arr[temp]; int a=rand.nextInt(100);//數字範圍 int b=rand.nextInt(100); while(a%b!=0)//判斷整除 { a=rand.nextInt(100); b=rand.nextInt(99)+1;//不能出現0 } while(a<b)//判斷是否出現負數 { a=rand.nextInt(100); b=rand.nextInt(100); }
3.最大公因數:保證生成的為真分數,用輾轉相除法
public static int divisor(int x,int y){ while(1){ if(x%y==0)return y; int temp=y; y=x%y; x=temp; } }
4.計算後綴表達式:我創建了一個棧ljk,保存的是char類型的字符,然後我int了一個op1來分別保存兩個棧頂的操作數,所以這裏先用到了String.valueOf
方法先將char轉換成String,然後使用Integer.parseInt方法。
int op1 = Integer.parseInt(String.valueOf(lkj.pop())); int op2 = Integer.parseInt(String.valueOf(lkj.pop())); int op = op1 - op2; lkj.push(op);
五、測試運行
六、代碼展示
將中序表達式轉化為後序表達式:
static bool isNumber(string message) { //判斷是否為整數字符串 int result = -1; //result 定義為out 用來輸出值 try { result = Convert.ToInt32(message); return true; } catch { return false; } } //判斷操作符優先級大小 static bool comparePriority(string op1, string op2) { return getPriorityValue(op1) > getPriorityValue(op2); } private static int getPriorityValue(string op1) { throw new NotImplementedException(); } static Stack<string> changeExpression(List<string> beforeExps) { Stack<string> operand = new Stack<string>();//操作數 Stack<string> opertor = new Stack<string>();//操作符 //遍歷中序表示 int length = beforeExps.Count; int len = opertor.Count; //判斷是否為操作數 for (int i = 0; i < length; i++) { string c = beforeExps[i]; if (isNumber(c)) { //操作數 存在操作數棧 operand.Push(c); } else { //若運算符為"("直接存入到運算符棧中 if (c == "(") { opertor.Push(c); } else if (c == ")") { //該運算符為右括號")",則輸出運算符堆棧中的運算符到操作數堆棧,直到遇到左括號為止。 將"("出棧 while (opertor.Peek() != "(") { string stringvalue = opertor.Pop(); operand.Push(stringvalue); } opertor.Pop(); } else { // 該運算符為非括號運算符 if (len <= 0) { opertor.Push(c); continue; } //若運算符堆棧棧頂的運算符為括號,則直接存入運算符堆棧。 //符合為左括號 直接存入運算符 if (opertor.Peek() == "(") { opertor.Push(c); } else { //若比運算符堆棧棧頂的運算符優先級高或相等,則直接存入運算符堆棧。 if (comparePriority(c, opertor.Peek())) { opertor.Push(c); } else { // 若比運算符堆棧棧頂的運算符優先級低,則輸出棧頂運算符到操作數堆棧,並將當前運算符壓入運算符堆棧。 string stringvalue = opertor.Pop(); operand.Push(stringvalue); opertor.Push(c); } } } } } //當表達式讀取完成後運算符堆棧中尚有運算符時,則依序取出運算符到操作數堆棧,直到運算符堆棧為空。 while (len > 0) { string stringvalue = opertor.Pop(); operand.Push(stringvalue); } //反轉operand 獲取正常的後綴表達式 Stack<string> resultSt = new Stack<string>(); while (len > 0) { string stringvalue = operand.Pop(); resultSt.Push(stringvalue); } return resultSt; }
七、總結:
所謂“模塊化”就是要功能獨立,功能塊裏要明確和調試好函數之間的調用關系,以便降低程序的復雜度和更進一步的維護。比如,在我的程序中,產生隨機數和計算題目這兩個功能是分開的,只要在產生隨機數的程序裏確定好操作數是合法的,那麽計算部分可以和操作數之間做到完全獨立。
八、展示PSP
PSP2.1 | Personal Software Process Stages | 預估耗時(小時) | 實際耗時(小時) |
---|---|---|---|
Planning | 計劃 | 1 | 1 |
· Estimate | · 估計這個任務需要多少時間 | 20 | 24 |
· Analysis | · 需求分析 (包括學習新技術) | 1 | 4 |
· Design Spec | · 生成設計文檔 | 0.5 | 0.5 |
· Design Review | · 設計復審 (和同事審核設計文檔) | 0.5 | 0.5 |
· Coding Standard | · 代碼規範 (為目前的開發制定合適的規範) | 1.5 | 1 |
· Design | · 具體設計 | 2 | 2.5 |
· Coding | · 具體編碼 | 2 | 3 |
· Code Review | · 代碼復審 | 2 | 2 |
· Test | · 測試(自我測試,修改代碼,提交修改) | 2 | 1 |
Reporting | 報告 | 1 | 1.5 |
· Test Report | · 測試報告 | 1.5 | 2 |
· Size Measurement | · 計算工作量 | 1.5 | 1 |
· Postmortem & Process Improvement Plan | · 事後總結, 並提出過程改進計劃 | 1 | 1 |
2016012063 小學四則運算練習軟件項目報告