1. 程式人生 > >小學四則運算生成Java實現 (彭迪彬 李尤)

小學四則運算生成Java實現 (彭迪彬 李尤)

sof 容易 char 分鐘 體會 stack 功能 完成 sig

Java實現小學四則運算

by 彭迪彬 李尤

GitHub項目地址:https://github.com/2youyou/JDBC

項目要求

題目:實現一個自動生成小學四則運算題目的命令行程序

  • 功能列表
  1. [完成] 使用 -n 參數控制生成題目的個數。
  2. [完成] 使用 -r 參數控制題目中數值的範圍。
  3. [完成] 生成的題目中計算過程不能產生負數。
  4. [完成] 生成的題目中如果存在形如e1 ÷ e2的子表達式,那麽其結果應是真分數
  5. [完成] 程序一次運行生成的題目不能重復,生成的題目存入執行程序的當前目錄下的Exercises.txt文件。
  6. [完成] 每道題目中出現的運算符個數不超過3個。
  7. [完成] 在生成題目的同時,計算出所有題目的答案,並存入執行程序的當前目錄下的Answers.txt
    文件。
  8. [完成] 程序應能支持一萬道題目的生成。
  9. [完成] 程序支持對給定的題目文件和答案文件,判定答案中的對錯並進行數量統計。

設計實現

1.表達式生成:根據輸入的兩個參數決定表達式的數量及數值範圍,隨機生成數值範圍內的自然數及運算符,隨機插入左括號並在相應的位置插入右括號。

2.分數:專門寫了一個類Fraction來生成分數,並且有約分的功能。

3.負數:負數只會在減法運算時產生,每當遇到減法運算時,若運算結果出現負數,就重新生成新的表達式。

4.計算:將整數也當成分數進行計算,先將上面生成的表達式(以字符串存儲)分別入數字棧和符號棧,再根據符號優先級計算最終結果。

技術分享圖片

代碼

分數類

public class Fraction {
    int x;              //分子
    int y;              //分母
    private Fraction temp;

    public Fraction(int a, int b) {
        x = a;
        y = b;
    }

    Fraction add(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.y + y * r.x;
        temp.y = y * r.y;
        return temp;
    }

    Fraction minus(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.y - y * r.x;
        temp.y = y * r.y;
        return temp;
    }

    Fraction multiply(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.x;
        temp.y = y * r.y;
        return temp;
    }

    Fraction divide(Fraction r) {
        temp = new Fraction(0, 0);
        temp.x = x * r.y;
        temp.y = y * r.x;
        return temp;
    }

    String print() {
        /**
         * 計算結果化簡
         */
        if (x == 0) {
            return "0";
        } else {
            int n;
            if (x > y)
                n = x;
            else
                n = y;
            int maxn = 0;
            for (int i = 1; i <= n; ++i) {              //約分
                if (x % i == 0 && y % i == 0)
                    maxn = i;
            }
            int a = x / maxn;
            int b = y / maxn;
            if (a == b)
                return "1";
            else if(b==1)
                return a+"";
            else
                return a + "/" + b;
        }
    }
}

表達式生成

public class CreateExercise {
    String[] sign = {"+", "-", "x", "÷", "/"};
    int range_num;
    Random random = new Random();

    
    public void setRange_num(int range_num) {
        this.range_num =range_num;
    }

    
    public String create() {
        String str = "";
        int local = random.nextInt(3);
        
        for (int j = 0; j < 3; j++) {
            if (local == 0 && j == 0) {
                str += "(";
            } else if (local == 2 && j == 1) {
                str += "(";
            }
            
            str += random.nextInt(range_num) % range_num + 1;   //產生指定範圍隨機數
            if (local == 0 && j == 1) {
                str += ")";
            }
            if (local == 2 && j == 2) {
                str += ")";
            }
            
            String signElement = sign[random.nextInt(5)];//產生隨機運算符號
            str += signElement;
            if (signElement == "/") {
                str += random.nextInt(range_num) % range_num + 1;
                signElement = sign[random.nextInt(5)];
                while (true) {
                    if (signElement != "/") {
                        str += signElement;
                        break;
                    }
                    signElement = sign[random.nextInt(5)];
                }
            }

        }
        str = str.substring(0, str.length() - 1);
        return str;
    }

    public void belongFraction(String strfraction) {
        /**
         * 處理分數計算
         */
        String[] fractionlist = null;
        if (strfraction.contains("+")) {
            fractionlist = strfraction.split("\\+");
            CalculateFraction(fractionlist, 0);
        } else if (strfraction.contains("-")) {
            fractionlist = strfraction.split("-");
            CalculateFraction(fractionlist, 1);
        } else if (strfraction.contains("x")) {
            fractionlist = strfraction.split("\\x");
            CalculateFraction(fractionlist, 2);
        } else if (strfraction.contains("÷")) {
            fractionlist = strfraction.split("÷");
            CalculateFraction(fractionlist, 3);
        }

    }

    public void CalculateFraction(String[] strlist, int flag) {
        /**
         * 分數的四種基本運算
         */
        String[] fraction1 = new String[2];
        String[] fraction2 = new String[2];
        
        if (strlist[0].contains("/"))
            fraction1 = strlist[0].split("/");
        else {
            fraction1[0] = strlist[0];
            fraction1[1] = "1";
        }
        if (strlist[1].contains("/"))
            fraction2 = strlist[1].split("/");
        else {
            fraction2[0] = strlist[1];
            fraction2[1] = "1";
        }

        Fraction fr1 = new Fraction(Integer.parseInt(fraction1[0]), Integer.parseInt(fraction1[1]));
        Fraction fr2 = new Fraction(Integer.parseInt(fraction2[0]), Integer.parseInt(fraction2[1]));
        fr1.print();
        
        switch (flag) {
            case 0:
                fr1.add(fr2).print();
                break;
            case 1:
                fr1.minus(fr2).print();
                break;
            case 2:
                fr1.multiply(fr2).print();
                break;
            case 3:
                fr1.divide(fr2).print();
                break;
        }
    }
}

計算

public class Calculator {

    /**
     * 數字棧:存儲表達式中的數字
     */
    private Stack<String> numStack = null;
    /**
     * 符號棧:存儲表達式中的運算符和括號
     */
    private Stack<Character> charStack = null;

    /**
     * 計算四則運算表達式,返回計算結果
     *
     */
    public String calculate(String numStr) {
        numStr = removeStrSpace(numStr);
        
        if (numStr.length() > 1 && !"=".equals(numStr.charAt(numStr.length() - 1) + "")) {
            numStr += "=";
        }
        if (!isStandard(numStr)) {
            return "0";
        }

        numStack = new Stack<String>();
        charStack = new Stack<Character>();
        StringBuffer temp = new StringBuffer();
        
        for (int i = 0; i < numStr.length(); i++) {
            char ch = numStr.charAt(i);
            if (isNumber(ch) || ch == ‘/‘) { 
                temp.append(ch);
            } else {
                String tempStr = temp.toString();
                if (!tempStr.isEmpty()) {
                    numStack.push(tempStr);
                    temp = new StringBuffer();
                }
                while (!comparePri(ch) && !charStack.empty()) {
                    String a = numStack.pop(); 
                    String b = numStack.pop();
                    Fraction f1 = null;
                    Fraction f2 = null;
                    if (a.contains("/")) {
                        String[] alist = a.split("/");
                        f1 = new Fraction(Integer.parseInt(alist[0]), Integer.parseInt(alist[1]));
                    } else {
                        f1 = new Fraction(Integer.parseInt(a), 1);
                    }
                    if (b.contains("/")) {
                        String[] blist = b.split("/");
                        f2 = new Fraction(Integer.parseInt(blist[0]), Integer.parseInt(blist[1]));
                    } else {
                        f2 = new Fraction(Integer.parseInt(b), 1);
                    }
                    switch (charStack.pop()) {
                        case ‘+‘:
                            numStack.push(f2.add(f1).print());
                            break;
                        case ‘-‘:
                            if ((f1.x/f1.y) >= (f2.x/f2.y)) {
                                return null;
                            
                        }
                            numStack.push(f2.minus(f1).print());
                            break;
                        case ‘x‘:
                            numStack.push(f2.multiply(f1).print());
                            break;
                        case ‘÷‘:
                            if (f1.x==0){
                                return null;
                            }
                            numStack.push(f2.divide(f1).print());
                            break;
                        default:
                            break;
                    }
                } 
                if (ch != ‘=‘) {
                    charStack.push(new Character(ch)); 
                    if (ch == ‘)‘) { 
                        charStack.pop();
                        charStack.pop();
                    }
                }
            }
        } 

        return numStack.pop(); 
    }


    private String removeStrSpace(String str) {
        return str != null ? str.replaceAll(" ", "") : "";
    }

 
    private boolean isStandard(String numStr) {
        if (numStr == null || numStr.isEmpty()) 
            return false;
        Stack<Character> stack = new Stack<Character>(); 
        boolean b = false; 
        for (int i = 0; i < numStr.length(); i++) {
            char n = numStr.charAt(i);
            if (!(isNumber(n) || "(".equals(n + "") || ")".equals(n + "")
                    || "+".equals(n + "") || "-".equals(n + "")
                    || "x".equals(n + "") || "÷".equals(n + "") || "/".equals(n + "")
                    || "=".equals(n + ""))) {
                return false;
            }
            
            if ("(".equals(n + "")) {
                stack.push(n);
            }
            if (")".equals(n + "")) { 
                if (stack.isEmpty() || !"(".equals((char) stack.pop() + "")) 
                    return false;
            }
            
            if ("=".equals(n + "")) {
                if (b)
                    return false;
                b = true;
            }
        }
        if (!stack.isEmpty())
            return false;
        if (!("=".equals(numStr.charAt(numStr.length() - 1) + "")))
            return false;
        return true;
    }


    private boolean isNumber(char num) {
        if (num >= ‘0‘ && num <= ‘9‘)
            return true;
        return false;
    }

    /**
     * 比較優先級:如果當前運算符比棧頂元素運算符優先級高則返回true,否則返回false
     */
    private boolean comparePri(char symbol) {
        if (charStack.empty()) { 
            return true;
        }


        char top = charStack.peek();
        if (top == ‘(‘) {
            return true;
        }
        switch (symbol) {
            case ‘(‘: 
                return true;
            case ‘x‘: {
                if (top == ‘+‘ || top == ‘-‘) 
                    return true;
                else
                    return false;
            }
            case ‘÷‘: {
                if (top == ‘+‘ || top == ‘-‘) 
                    return true;
                else
                    return false;
            }
            case ‘+‘:
                return false;
            case ‘-‘:
                return false;
            case ‘)‘: 
                return false;
            case ‘=‘:
                return false;
            default:
                break;
        }
        return true;
    }

    String getFinalResult(String str) {
        if (!str.contains("/"))
            return str;
        String[] part = str.split("/");
        int a = Integer.parseInt(part[0]);
        int b = Integer.parseInt(part[1]);
        
        if (a == b)
            return "1";
        else if (a > b && a % b != 0) {
            return a / b + "’" + a % b + "/" + b;
        } else if (a < b && -a > b && (-a) % b != 0) {
            return "-" + (-a) / b + "’" + (-a) % b + "/" + b;
        } else if (b == 1)
            return a + "";
        else
            return a + "/" + b;
    }

}

生成txt文本

public class IO {
    File ExerciseFile = null;
    File AnswerFile = null;
    String filename = "";
    BufferedWriter ExerciseOut = null;
    BufferedWriter AnswerOut = null;

    public IO() {
        if (this.CreateFile()) {
            this.setOutBufferedWriter();
        } else
            System.out.println("創建文件失敗!");
    }

    public void setOutBufferedWriter() {
        try {
            this.ExerciseOut = new BufferedWriter(new FileWriter(ExerciseFile));
            this.AnswerOut=new BufferedWriter(new FileWriter(AnswerFile));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean CreateFile() {
        String relativelyPath=System.getProperty("user.dir");
        ExerciseFile = new File(relativelyPath+"\\Exercise" + ".txt");
        AnswerFile = new File(relativelyPath+"\\Answer" + ".txt");
        if (ExerciseFile.exists()) {
            ExerciseFile.delete();
        }
        if (AnswerFile.exists()) {
            AnswerFile.delete();
        }
        try {
            ExerciseFile.createNewFile();
            AnswerFile.createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    public boolean WriteToFile(String content, int flag) {
        try {
            switch (flag) {
                case 0:
                    ExerciseOut.write(content);
                    ExerciseOut.write("\r\n");
                    ExerciseOut.flush();
                    return true;
                case 1:
                    AnswerOut.write(content);
                    AnswerOut.write("\r\n");
                    AnswerOut.flush();
                    return true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    public boolean CloseOutBufferedWriter() {
        try {
            ExerciseOut.close(); 
            AnswerOut.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
}

主函數啟動類

public class MyApp {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.print("請輸入希望生成的題目數量:");
        int problems_num = scan.nextInt();
        System.out.print("請輸入題目的數值範圍n(範圍為0到n):");
        int range_num = scan.nextInt();
        System.out.print("是否開始答題(yes/no):");
            String is_doNow=scan.next();

        LinkedHashMap<Integer, String> rightAnswerMap = new LinkedHashMap<Integer, String>();
        LinkedHashMap<Integer,String> exerciseMap = new LinkedHashMap<Integer, String>();
        ArrayList<Integer> rightRecord = new ArrayList<Integer>();
        ArrayList<Integer> wrongRecord = new ArrayList<Integer>();
        CreateExercise ce = new CreateExercise();
        IO save = new IO();
        ce.setRange_num(range_num);
        
        for (int i = 1; i <= problems_num; i++) {
            String problem = ce.create();
            exerciseMap.put(i,problem);
            String ns = problem;
            int rightbrackets;
            int leftbrackets;
            
            if (problem.contains(")")) {
                rightbrackets = problem.indexOf(")");
                leftbrackets = problem.indexOf("(");
                if (rightbrackets != problem.length() - 1 && problem.charAt(rightbrackets + 1) == ‘/‘) {
                    StringBuilder sb = new StringBuilder(problem);
                    if (leftbrackets - 1 > 0 && problem.charAt(leftbrackets - 1) == ‘÷‘)
                        sb.replace(rightbrackets + 1, rightbrackets + 2, "x");
                    else
                        sb.replace(rightbrackets + 1, rightbrackets + 2, "÷");
                    ns = sb.toString();
                }
            }


            Calculator cal = new Calculator();
            String result = cal.calculate(ns);
            
            if (result != null) {
                result = cal.getFinalResult(result);
                System.out.println(i + ":  " + problem + "=" );
                rightAnswerMap.put(i, result);
                if (!save.WriteToFile(i + ":  " + problem + "=", 0)) {
                    System.out.println("生成Exercise文件失敗!");
                    System.exit(0);
                }
                if (!save.WriteToFile(+ i + ":  " + problem + "=" + result, 1)) {
                    System.out.println("生成Answer文件失敗!");
                    System.exit(0);
                }
            } else {
                i--;
            }

            

        }

        if(is_doNow.equals("yes")){
            System.out.println("請輸入答案(帶分數用“ ’ ”分隔,如1’1/2):");
            
            for (int i = 1; i <= problems_num; i++) {
                System.out.print( + i + ":  " + exerciseMap.get(i) + "=");
                String input = scan.next();
                if (rightAnswerMap.get(i).equals(input))
                    rightRecord.add(i);
                else
                    wrongRecord.add(i);
            }
            System.out.println("結果為:");

            if (rightRecord.size()!=0){
                System.out.print("正確題目:"+rightRecord.size()+" (");
                for (int i=0;i<rightRecord.size()-1;i++)
                    System.out.print(rightRecord.get(i)+",");
                System.out.println(rightRecord.get(rightRecord.size()-1)+")");
            }
            else
                System.out.println("正確題目:"+rightRecord.size());

            if (wrongRecord.size()!=0){
                System.out.print("錯誤題目:"+wrongRecord.size()+" (");
                for (int i=0;i<wrongRecord.size()-1;i++)
                    System.out.print(wrongRecord.get(i)+",");
                System.out.println(wrongRecord.get(wrongRecord.size()-1)+")");
            }
            else
                System.out.println("錯誤題目:"+wrongRecord.size());
        }
        if (save.CloseOutBufferedWriter())
            System.out.println("題目和答案文本創建成功");
        else
            System.out.println("題目和答案文本創建失敗");
    }
}

測試運行

生成10000道題目

技術分享圖片

部分題目及答案

技術分享圖片

點擊查看完整題目

點擊查看完整答案

答案統計

技術分享圖片

故意答錯 1,3題
技術分享圖片

代碼覆蓋率
技術分享圖片

PSP

PSP2.1

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃 60 120
· Estimate · 估計這個任務需要多少時間 30 60
Development 開發 1000 2200
· Analysis · 需求分析 (包括學習新技術) 100 180
· Design Spec · 生成設計文檔 60 100
· Design Review · 設計復審 (和同事審核設計文檔) 30 30
· Coding Standard · 代碼規範 (為目前的開發制定合適的規範) 30 30
· Design · 具體設計 120 150
· Coding · 具體編碼 600 1500
· Code Review · 代碼復審 50 60
· Test · 測試(自我測試,修改代碼,提交修改) 150 60
Reporting 報告 80 80
· Test Report · 測試報告 30 40
· Size Measurement · 計算工作量 20 10
· Postmortem & Process Improvement Plan · 事後總結, 並提出過程改進計劃 40 20
合計 1150 2400

總結

此次的結對編程,我主要負責表達式的生成、計算,李尤同學主要負責文件生成的IO,我們都相互審閱了對方的代碼。剛拿到題目的時候,我們的思路並不是很清晰,對於表達式的計算及負數的避免想要直接實現,嘗試過後發現過於麻煩,後來與李尤同學討論後發現使用數據結構的棧來實現會簡單很多,可以說這次的結對編程,通過兩個人的討論,使得項目的整體思路變得很清晰,實現起來也更加容易。此次編程較為遺憾的沒有實現題目的查重功能,我們以後會繼續努力完善這個功能。李尤同學的思維很活躍,經常有一些天馬行空的想法,在編寫程序的過程中給了我很多啟發。這次的結對編程,讓我體會到了合作的力量,同時也認識到了自己數據結構方面知識的不足,今後會多加學習這方面的知識,完善自己的不足。

小學四則運算生成Java實現 (彭迪彬 李尤)