1. 程式人生 > >軟件工程第3次作業—四則運算(結對作業)

軟件工程第3次作業—四則運算(結對作業)

時也 包括 www 題目 abs 結對編程 要求 經歷 優先

作業要求的博客鏈接:https://edu.cnblogs.com/campus/nenu/2016CS/homework/2266

git倉庫地址:https://git.coding.net/pipifan/f4.git

本次作業是結對作業,我的結對夥伴是樊友朋同學,他的博客地址是:http://www.cnblogs.com/pipifan/p/9918250.html

項目概要:

  本次項目實現的是一個用於四則運算的控制臺程序,目前已實現的功能如下:

  1)支持整數和不含括號的四則運算且表達式可以重復。

  2)支持小數和含小括號的四則運算且表達式可以重復。

  3)表達式不重復且輸出結果顯示在控制臺,然後將控制臺顯示的結果輸出到指定位置的txt文件中。

 

項目詳情:

一、本次作業采用c/c++進行編程。首先分析項目要求概括功能:

  1)按照控制臺輸入的N ,生成N道由隨機生成的整數合法運算符組成的四則運算,並判斷用戶輸入答案的對錯。(輸入 f4 -n N)

  2)按照控制臺輸入的N ,生成N道由隨機生成的整數小數合法運算符括號組成的四則運算,並判斷用戶輸入答案的對錯。(輸入 f4 -c N )

  3)按照控制臺輸入的N 、文件路徑M,生成N道由隨機生成的整數小數合法運算符括號組成的不重復的四則運算,給出答案打印在控制臺輸出到文件M中。(輸入 f4 -c N -f M)

  4)如果輸入的N不合法會輸出相應的警告。

二、根據分析規劃函數模塊:

  1)字符串轉化為數字。

   2)判斷運算符號的優先級。

  3)處理四則運算表達式,計算表達式的值。

  4)處理控制臺輸入輸出,產生隨機數,隨機運算符號和隨機括號。

  5)主函數,負責調用各個模塊,控制流程。

三、部分模塊代碼實現過程:

  1)處理四則運算表達式。

首先分配兩個棧sp,sv,分別存儲運算符和運算數字,從左到右讀取中綴表達式,遇到操作數就入棧sv,遇到左括號或者當前運算符比sp棧頂優先級高的符號就入棧sp,遇到比sp棧頂優先級低的符號就出棧兩個操作數和棧頂操作符,結果再入棧sv,遇到右括號同時棧頂是左括號就出棧左括號。最後sv棧頂就是該表達式的結果。

具體代碼如下:

double solve(string s )
{
    stack<double> sv;
    stack<char> sp;
    char c;
    int k = 0, flag = 1;
    double x, y;
    sp.push(\0);
    c = s[k];
    while (flag)
    {
        if (c >= 0&&c <= 9 || c == .) {
            sv.push(toNum(s, k));
        }
        else if (c == \0&& sp.top() == \0) {
            flag = 0;
        }
        else if (c == ( || (priority(c) > priority(sp.top()))) {
            sp.push(c);
            k++;
        }
        else if (c == )&& sp.top() == () {
            sp.pop();
            k++;
        }
        
        else if (priority(c) <= priority(sp.top())) {
            x = sv.top();
            sv.pop();
            if(sv.empty()) y = 0;
            else {
                y = sv.top();
                sv.pop();
            }
            c = sp.top();
            if( c == / && fabs( x ) <= eps ){
                int num = rand() % 3;
                c = mp[num];
            }
            sp.pop();
            switch (c) {
                case +:y = x + y; break;
                case -:y = y - x; break;
                case *:y = x*y; break;
                case /:y = y / x; break;
            }
            sv.push(y);
        }
        c = s[k];
    }
    return sv.top();
}

  2)隨機生成整數數字和運算符。

實現功能一用rand隨機生成數字和運算符(提前存到數組中)。

這裏在實現過程中有兩點我們進行了思考:其一是單純用rand()會使每次的隨機數一樣,這樣就不符合隨機這個概念,所以我們加上 srand( (int)time(0) ),用時間做隨機數的種子,這樣可以保證每次的隨機數不同。其二是考慮到除數不能為0,所以我們判斷如果“/”後面的隨機數是0就再造數據直至不為0,保證了表達式的正確性。

s = "";
        if( flag == 1 ){
            for(int i = 0 ; i < 7 ; i++ ){
                if( (i % 2) == 0 ){
                    int num = rand() % 10;
                    int len = s.size();
                    while( len > 0 && num == 0 && s[len - 1] == / )
                        num = rand() % 10;
                    stringstream ss;
                    ss << num;
                    string tmp;
                    ss >> tmp;
                    s += tmp;
                }
                else{
                    int num = rand() % 4;
                    s += mp[num];
                }
            }
        }

3)隨機生成帶括號的整數和小數的數據。

生成小數時用兩個隨機數相除。

這裏的難點是隨機加括號,我們采取的辦法是先隨機加左右括號,最後檢查是否匹配,缺少匹配的括號就在表達式首尾加上相應的括號。

            for(int i = 0 ; i < 7 ; i++ ){
                string tmp;
                if( (i % 2) == 0 ){
                    int num = rand() % 10;
                    if( (num % 3) == 0 && num > 0 ){
                        int num1 = rand() % 100;
                        int num2 = rand() % 100;
                        while( num2 == 0 )
                            num2 = rand() % 100;
                        double tmp_num = num1*1.0 / num2;
                        stringstream ss;
                        ss << tmp_num;
                        ss >> tmp;
                        x[xx++] = tmp_num;
                    }
                    else{
                        int len = s.size();
                        while( len > 0 && num == 0 && s[len - 1] == / )
                            num = rand() % 10;
                        stringstream ss;
                        ss << num;
                        ss >> tmp;
                        x[xx++] = num*1.0;
                    }
                    if( num < 3 && i < 6 && i > 0 ){
                        s += "(";
                        cnt1++;
                    }
                    s += tmp;
                    if( num > 6  && i > 0 && i < 6){
                        if( cnt1 > 0 )
                            cnt1--;
                        else
                            cnt2++;
                        s += ")";
                    }
                }
                else{
                    int num = rand() % 4;
                    s += mp[num];
                }
            }

四、測試階段:

1)功能1

技術分享圖片

技術分享圖片

2)功能2

技術分享圖片

技術分享圖片

3)功能3

技術分享圖片

技術分享圖片

五、項目進行過程

1.給出結對編程的體會

結對編程的好處是可以集思廣益,尤其針對本次作業的一些小的細節可以做到查漏補缺。

比如輸入參數錯誤時的文字提示,功能一和功能二的提示是不同的;比如除數不能為0;比如保留小數的位數時要考慮是否為有限小數,無限小數要保留三位;比如功能三輸出時要對齊......等等的一些問題。在解決功能二中如何隨機加括號,並且如何匹配正確時,兩個人也進行了各自的思考然後加以討論得出解決方案。在查找相應的參考資料時也更便捷。

由於結對編程需要共用一個設備,所以編程以平時寫代碼能力比較強的樊友朋同學為主,我負責收集整理資料、提供思路,發現、修改細節問題和測試數據。我對結對編程的體會應該是分清主次,各司其職,需要默契,在討論中解決問題和完善項目。

2. 至少3項在編碼、爭論等活動中花費時間較長,給你較大收獲的事件。

1). 用棧處理四則運算表達式。具體思路可以參考3.1

2). 隨機生成小數和括號。主要的難點是隨機加括號,並且要保證括號的正確性。具體思路參考3.3

3). 功能二和三要求表達式不重復。初看作業要求中的不重復概念是要運用交換律、結合律等數學定理來檢測題的不重復性,但是這樣比較麻煩。所以經過討論後,我的夥伴提出為了保證題的不重復,可以檢查每個題中的數字是否相同。因為題目本身是由隨機數、隨機括號、隨機運算符組成的,它重復的概率很低,所以我們把表達式的數字排序,包裝放在set裏面,檢查數字重復時就重新生成表達式,這樣不會影響隨機性,也不會有重復的表達式,最重要的是降低算法復雜度(相對檢查交換律的那種算法)

4). 花費時間長的地方還有5.1提到的細節問題,為了盡力滿足作業要求,我們用了大概兩至三個小時去修改細節。

六、給出照片1張,包括結對的2位同學、工作地點、計算機,可選項包括其他能表達結對編程工作經歷的物品或場景。

技術分享圖片

軟件工程第3次作業—四則運算(結對作業)