1. 程式人生 > >軟工作業-四則運算生成器

軟工作業-四則運算生成器

發現 turn ESS gcd 完全 工作量 wrong 開發 用戶

git地址:https://www.cnblogs.com/Nigghod/p/9714359.html

組員:洪崇偉、林浩

一、PSP表-預估耗時

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

二、解題思路

  • 首先這個題目有分數的要求,所以在一開始就應該考慮到分數的實現,因為java不存在可以處理分數運算的類,這個應該在一開始就考慮清楚,我們的處理方式是將整數與分數作為一個對象來處理,即整數就是分母為1的分數。
  • 計算便可以直接用分數的運算了。
  • 因為題目要求命令行,所以我們找了腳本的方法來實現命令行操作

三、設計實現過程

  • 有三個包,分別為service(服務包),model(實體包),main(主程序)
  • service裏有四個類,分別包括了計算操作,分數實體類處理操作,檢查操作與生成式子操作
  • model裏有三個類,計算等式的計算類,分數類,結果集合
  • main裏主要是以上兩個包中的方法對應整合

四、代碼說明

整個程序難點就在於分數的運算,整數,小數運算可以用自帶的操作運算符,但是在java裏面卻沒有一個基本類型去表示分數。很多人可能會考慮到把分數化成小數再運算再轉為分數,其實不然,有很多情況下是無法進行轉換的,因為java語言中的double類型所表示的精度也是有限的。於是結合java面向對象的思想特征,應該先定義要給分數類,並封裝相關的運算方法。代碼如下

分數類:

/*
 * 構建一個分數類,用來表示分數,封裝相關的方法
 */
public class Fraction {

    private int denominator;// 分母
    private int nominator;// 分子

    // 構建一個分數
    public Fraction(int denominator, int nominator) {
        super();
        this.denominator = denominator;
        this.nominator = nominator;
    }

    public Fraction(int nominator) {
        this.denominator = 1;
        this.nominator = nominator;
    }

    public Fraction() {
        super();
    }

    // 判斷構建的是一個分數還是一個整數,不超過limit的數值
    public Fraction(boolean l, int limit) {
        Random r = new Random();
        // 這是一個分數
        if (l == true) {
            int index = r.nextInt(limit);
            int index2 = r.nextInt(limit);

            while(index==0) {
                index = r.nextInt(limit);
//              System.out.println("會生成0:"+index);
            }
//          System.out.println("不會生成0:"+index);
            this.denominator = index;
            this.nominator = index2;

            // 這是一個整數
        } else {
            int index = r.nextInt(limit);
            this.denominator = 1;
            this.nominator = index;
        }
    }

    public int getDenominator() {
        return denominator;
    }

    public void setDenominator(int denominator) {
        this.denominator = denominator;
    }

    public int getNominator() {
        return nominator;
    }

    public void setNominator(int nominator) {
        this.nominator = nominator;
    }



    // 加法運算
    public Fraction add(Fraction r) {
        int a = r.getNominator();// 獲得分子
        int b = r.getDenominator();// 獲得分母
        int newNominator = nominator * b + denominator * a;
        int newDenominator = denominator * b;
        Fraction result = new Fraction(newDenominator, newNominator);
        return result;
    }

    // 減法運算
    public Fraction sub(Fraction r) {
        int a = r.getNominator();// 獲得分子
        int b = r.getDenominator();// 獲得分母
        int newNominator = nominator * b - denominator * a;
        int newDenominator = denominator * b;
        Fraction result = new Fraction(newDenominator, newNominator);
        return result;
    }

    // 分數的乘法運算
    public Fraction muti(Fraction r) { // 乘法運算
        int a = r.getNominator();// 獲得分子
        int b = r.getDenominator();// 獲得分母
        int newNominator = nominator * a;
        int newDenominator = denominator * b;
        Fraction result = new Fraction(newDenominator, newNominator);
        return result;
    }

    // 分數除法運算
    public Fraction div(Fraction r) {
        int a = r.getNominator();// 獲得分子
        int b = r.getDenominator();// 獲得分母
        int newNominator = nominator * b;
        int newDenominator = denominator * a;
        Fraction result = new Fraction(newDenominator, newNominator);
        return result;
    }

    // 用輾轉相除法求最大公約數
    private static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    // 對分數進行約分
    public void Appointment() {
        if (nominator == 0 || denominator == 1)
            return;
        // 如果分子是0或分母是1就不用約分了
        long gcd = gcd(nominator, denominator);
        this.nominator /= gcd;
        this.denominator /= gcd;
    }
    
    public int existZero(){
        if(this.nominator<0||this.denominator<0){
            return 0;
        }else {
            return 1;
        }
    }
}

在這個分數類中,我們定義了分子和分母,然而整數其實一個分數,只不過這個分數的分母為1.
至於運算也只是根據運算法則對分數的分子分母進行運算,保證了



在程序中,運算符的出現數量以及類型是隨機的,用數組進行存儲,用一個隨機數Ramdon

char[] c = { ‘+‘, ‘-‘, ‘*‘, ‘÷‘ }//顯示四種基本運算符

r.nextInt(4);//隨機生成四種基本運算符的一種

int s = r.nextInt(3);// 生成運算符的數量

用這種隨機數的形式保證了式子的隨機性。隨機出現,隨機生成,之後只要根據多項式的運算,對式子進行運算即可。之後將生成的式子放在一個list裏面,包括運算符以及分數類

運算

四則運算具有優先級,先算乘除再算加減,如果采用if表達的形式,對優先級進行判定,那麽三個運算符就有64種情況,很明顯是不可采取的,容易使代碼失去可讀性,在這種情況下,我們采取遞歸來解決這種問題。

//分式的計算方法
    public Fraction calculate(List l){
        
        int muldiv = MulDivExist(l);
        if(muldiv != -1){
            String s = MulDiv(l,muldiv);
            if(s.equals("error")){
                return null;
            }
        }else {
            String s = AddSub(l);
            if(s.equals("error")){
                return null;
            }
        }
        if (l.size() == 1) {
            return (Fraction) l.get(0);
        }
        return calculate(l);
    }
    
    /*
     * 判斷分式裏面是否有乘除
     * 有乘除返回乘除的位置,沒乘除返回-1
     */
    public int MulDivExist(List list){
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("*") || list.get(i).equals("/")) {
                return i;
            }
        }
        return -1;
        
    }
    
    //計算分式的乘除,計算結果往前放
    public String MulDiv(List l,int muldiv){
        String fuhao = (String) l.remove(muldiv);
        Fraction first = (Fraction) l.get(muldiv-1);
        Fraction last = (Fraction) l.get(muldiv);
        l.remove(muldiv);
        if (fuhao.equals("*")) {
            Fraction result = first.muti(last);
            l.set(muldiv - 1,result);
            if(result.existZero()==0){
                return "error";
            }
        }
        if (fuhao.equals("/")) {
            Fraction result = first.div(last);
            l.set(muldiv - 1,result);
            if(result.existZero()==0){
                return "error";
            }
        }
        return "right";
        
    }
    
    //計算分式的加減,計算結果往前放
    public String AddSub(List list){
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).equals("+")) {
                Fraction first = (Fraction) list.get(i-1);
                list.remove(i);
                Fraction last = (Fraction) list.get(i);
                list.remove(i);
                Fraction result = first.add(last);
                list.set(i - 1, result);
                i--;
                if(result.existZero()==0){
                    return "error";
                }
            }
            if (list.get(i).equals("-")) {
                Fraction first = (Fraction) list.get(i-1);
                list.remove(i);
                Fraction last = (Fraction) list.get(i);
                list.remove(i);
                Fraction result = first.sub(last);
                list.set(i - 1, result);
                i--;
                if(result.existZero()==0){
                    return "error";
                }
            }
        }
        return "right";
    }
    

在遞歸計算之前,先做一個判定,對生成的式子進行判別,如果有乘除,那就優先采用乘除的遞歸,然後再用遞歸計算加減。這樣就保證了運算的優先級問題。

去重

式子需要去重,保證每次生成的都是不一樣的式子。去重采用了一種字符串比較的方法,在此之前,重寫了分數類的toString方法,只要兩個字符串組成字符完全相同即:組成兩個式子的字符完全一樣就可以說明這兩個式子是重復的。

重寫的toString方法

    @Override
    public String toString() {
        Appointment();
        if(this.denominator == 0){
            System.out.println(this.nominator + "|" + this.denominator);
            System.out.println("分母為0");
        }
        if (this.denominator == 1 || this.nominator == 0) {
            return "" + this.nominator;
        }else if (this.nominator > this.denominator) {
            if(nominator % denominator==0){
                return "" + nominator / denominator;
            }
            return "" + nominator / denominator + "," + nominator % denominator + "/" + denominator;
        }else{
            return "" + this.nominator + "/" + this.denominator;
        }
        
        
    }
    

去重代碼:

//查重,若有重復那就返回ture
    public boolean isRepeat(List<List<String>> list, List<String> set) {

        if (list.size() <= 0) {
            return false;
        }
        
        Iterator<String> iterator = set.iterator();

        
        for (List l_set : list) {
            if (l_set == null || l_set.size() != set.size() || l_set.size() <= 0 || set.size() <= 0) {
                continue;
            }
            int i = 0;
            while(iterator.hasNext()){
                if(l_set.contains(iterator.next())){
                    i = i+1;
                }
            }
            
            if(i == set.size()){
                return true;
            }
            
        }

        return false;
    }

通過這段去重代碼可以將重復的式子篩選出去,保證了整個文件中式子的獨立性。

計算結果的校驗

一個式子,等式右邊的就是結果,一個式子就是一個字符串,只要用字符串處理函數將等式右邊的結果截取出來,與用戶的輸入進行對比就能得出結果與否。
相關代碼如下:

while((str1=reader1.readLine())!=null&&(str2=reader2.readLine())!=null){
            if(!str1.trim().equals(str2.trim())){
                String[] str = str1.split("\\.");
                error = error + str[0]+ ",";
                errornum ++ ;
            }else {
                String[] str = str1.split("\\.");
                correct = correct + str[0] + ",";
                correctnum ++;
            }
            
        }
        
        if(error.equals("")){
            error = "Wrong: " + errornum + "";
        }else {
            error = "Wrong: " + errornum + "(" + error.substring(0,error.length()-1) + ")";
        }
        if(correct.equals("")){
            correct = "Correct: " + correctnum + "";
        }else {
            correct = "Correct: " + correctnum + "("+correct.substring(0, correct.length()-1)+")";
        }
        m.put("wrong", error);
        m.put("correct", correct);
        return m;
        }
    

將結果放在一個map中,便於讀取,得出結果。

五、測試運行

首先要將jar文件與bat文件放置在同一目錄下

技術分享圖片

進入命令行界面

技術分享圖片

若是未輸入參數,則

技術分享圖片

正確輸入生成題目的參數命令

技術分享圖片
技術分享圖片
技術分享圖片

正確輸入測試文件與答案文件

技術分享圖片

若是全對

技術分享圖片

出現錯誤

技術分享圖片

六、總結

問題

  • 一開始並沒有思考清楚題目中的分數問題,想著解決了分數就行,結果最後推倒重來。
  • 在程序開發過程中,雖然使用了git管理,但因為不熟悉發生了一些錯誤,修改的代碼上傳後下拉發生了沖突,以後在團隊編程中,要註意代碼改動情況,和隊員對接好。

結對好處

  • 可以互相發現對方的編碼問題,及時做出調整
  • 在討論中,不斷地想出新的點子來實現功能,很好的促進項目的開發

軟工作業-四則運算生成器