小學四則算法

1. 題目要求

     本次做業要求兩我的合做完成,駕駛員和導航員角色自定,鼓勵你們在工做期間角色隨時互換,這裏會佈置兩個題目,請各組成員根據本身的愛好任選一題。咱們在剛開始上課的時候介紹過一個小學四則運算自動生成程序的例子,請實現它,要求:
   可以自動生成四則運算練習題
   能夠定製題目數量
   用戶能夠選擇運算符
   用戶設置最大數(如十之內、百之內等)
   用戶選擇是否有括號、是否有小數
   用戶選擇輸出方式(如輸出到文件、打印機等)
   最好能提供圖形用戶界面(根據本身能力選作,以完成上述功能爲主)git

2.任務分配

     在此次咱們第一次進行兩人合做的編程方式,咱們經過互相的瞭解,將咱們的利益最大化,讓每一個人均可以在此次的結對編程的過程當中獲得長進,學習到新的知識,培養兩我的之間的默契。根據咱們每一個人的實際狀況,咱們的需求,咱們分配的角色狀況以下:
   駕駛員:何聰
   領航員:王軍宏
工做照:
算法

3.代碼審查表

功能模塊名稱  
審查人 王軍宏  審查日期 2018.4.16  
代碼名稱 隨機產生小學四則運算  代碼做者  何聰
文件結構
重要性 審查項 結論
頭文件和定義文件的名稱是否合理? 是  
  頭文件和定義文件的目錄結構是否合理?
  版權和版本聲明是否完整?
重要 頭文件是否使用了 ifndef/define/endif 預處理塊?
  頭文件中是否只存放「聲明」而不存放「定義」      
程序的版式
重要性 審查項 結論
  空行是否得體?
  代碼行內的空格是否得體?
  長行拆分是否得體?
  「{」 和 「}」 是否各佔一行而且對齊於同一列?
重要 一行代碼是否只作一件事?如只定義一個變量,只寫一條語句。
重要 If、for、while、do等語句自佔一行,不論執行語句多少都要加 「{}」。
重要 在定義變量(或參數)時,是否將修飾符 * 和 & 緊靠變量名?註釋是否清晰而且必要?  
重要 註釋是否有錯誤或者可能致使誤解? 
重要 類結構的public, protected, private順序是否在全部的程序中保持一致?   
命名規則
重要性 審查項 結論
重要 命名規則是否與所採用的操做系統或開發工具的風格保持一致?
  標識符是否直觀且能夠拼讀? 是 
  標識符的長度應當符合「min-length && max-information」原則? 是 
重要 程序中是否出現相同的局部變量和所有變量?  
  類名、函數名、變量和參數、常量的書寫格式是否遵循必定的規則?
  靜態變量、全局變量、類的成員變量是否加前綴?      
表達式與基本語句
重要性 審查項 結論
重要 若是代碼行中的運算符比較多,是否已經用括號清楚地肯定表達式的操做順序? 是 
  是否編寫太複雜或者多用途的複合表達式?  
重要 是否將複合表達式與「真正的數學表達式」混淆?  
重要 是否用隱含錯誤的方式寫if語句? 例如  
  (1)將布爾變量直接與TRUE、FALSE或者一、0進行比較。  
  (2)將浮點變量用「==」或「!=」與任何數字比較。
  (3)將指針變量用「==」或「!=」與NULL比較。  
  若是循環體內存在邏輯判斷,而且循環次數很大,是否已經將邏輯判  
  斷移到循環體的外面?  
重要 Case語句的結尾是否忘了加break? 無 
重要 是否忘記寫switch的default分支?
重要 使用goto 語句時是否留下隱患? 例如跳過了某些對象的構造、變量的初始化、重要的計算等。      
常量
重要性 審查項 結論
  是否使用含義直觀的常量來表示那些將在程序中屢次出現的數字或字符串?
  在C++ 程序中,是否用const常量取代宏常量? 否 
重要 若是某一常量與其它常量密切相關,是否在定義中包含了這種關係?  
  是否誤解了類中的const數據成員?由於const數據成員只在某個對象 否 
  生存期內是常量,而對於整個類而言倒是可變的。 是       
函數設計
重要性 審查項 結論
  參數的書寫是否完整?不要貪圖省事只寫參數的類型而省略參數名字。 是 
  參數命名、順序是否合理? 是 
  參數的個數是否太多? 否 
  是否使用類型和數目不肯定的參數?  
  是否省略了函數返回值的類型? 否 
  函數名字與返回值類型在語義上是否衝突? 否 
重要 是否將正常值和錯誤標誌混在一塊兒返回?正常值應當用輸出參數得到,而錯誤標誌用return語句返回。 否 
重要 在函數體的「入口處」,是否用assert對參數的有效性進行檢查?  
重要 使用濫用了assert? 例如混淆非法狀況與錯誤狀況,後者是必然存在的而且是必定要做出處理的。  
重要 return語句是否返回指向「棧內存」的「指針」或者「引用」? 是 
  是否使用const提升函數的健壯性?const能夠強制保護函數的參數、返回值,甚至函數的定義體。「Use const whenever you need」      
內存管理
重要性 審查項 結論
重要 用malloc或new申請內存以後,是否當即檢查指針值是否爲NULL?(防止使用指針值爲NULL的內存) 否 
重要 是否忘記爲數組和動態內存賦初值?(防止將未被初始化的內存做爲右值使用) 否  
重要 數組或指針的下標是否越界? 否 
重要 動態內存的申請與釋放是否配對?(防止內存泄漏)  
重要 是否有效地處理了「內存耗盡」問題?  
重要 是否修改「指向常量的指針」的內容?  
重要 是否出現野指針?例如(1)指針變量沒有被初始化;(2)用free或delete釋放了內存以後,忘記將指針設置爲NULL。  
重要 是否將malloc/free 和 new/delete 混淆使用?  
重要 malloc語句是否正確無誤?例如字節數是否正確?類型轉換是否正 確?  
重要 在建立與釋放動態對象數組時,new/delete的語句是否正確無誤?       
C++ 函數的高級特性
重要性 審查項 結論
  重載函數是否有二義性?  
重要 是否混淆了成員函數的重載、覆蓋與隱藏?  
  運算符的重載是否符合制定的編程規範?  
  是否濫用內聯函數?例如函數體內的代碼比較長,函數體內出現循環。  
重要 是否用內聯函數取代了宏代碼?       
類的構造函數、析構函數和賦值函數
重要性 審查項 結論
重要 是否違背編程規範而讓C++ 編譯器自動爲類產生四個缺省的函數:  
  (1)缺省的無參數構造函數;  
  (2)缺省的拷貝構造函數;
  (3)缺省的析構函數; 是 
  (4)缺省的賦值函數。
重要 構造函數中是否遺漏了某些初始化工做?  
重要 是否正確地使用構造函數的初始化表?  
重要 析構函數中是否遺漏了某些清除工做?  
  是否錯寫、錯用了拷貝構造函數和賦值函數?  
重要 賦值函數通常分四個步驟:
  (1)檢查自賦值; 是 
  (2)釋放原有內存資源;  
  (3)分配新的內存資源,並複製內容;  
  (4)返回 *this。是否遺漏了重要步驟?  
重要 是否正確地編寫了派生類的構造函數、析構函數、賦值函數?  
  注意事項:
  (1)派生類不可能繼承基類的構造函數、析構函數、賦值函數。  
  (2)派生類的構造函數應在其初始化表裏調用基類的構造函數。  
  (3)基類與派生類的析構函數應該爲虛(即加virtual關鍵字)。  
  (4)在編寫派生類的賦值函數時,注意不要忘記對基類的數據成員從新賦值       
類的高級特性
重要性 審查項 結論
重要 是否違背了繼承和組合的規則?  
  (1)若在邏輯上B是A的「一種」,而且A的全部功能和屬性對B而言都有意義,則容許B繼承A的功能和屬性。  
  (2)若在邏輯上A是B的「一部分」(a part of),則不容許B從A派生,而是要用A和其它東西組合出B。     
其它常見問題
重要性 審查項 結論
重要 數據類型問題:
  (1)變量的數據類型有錯誤嗎?  
  (2)存在不一樣數據類型的賦值嗎?  
  (3)存在不一樣數據類型的比較嗎?  
重要 變量值問題:
  (1)變量的初始化或缺省值有錯誤嗎?  
  (2)變量發生上溢或下溢嗎?  
  (3)變量的精度夠嗎?  
重要 邏輯判斷問題:
  (1)因爲精度緣由致使比較無效嗎?  
  (2)表達式中的優先級有誤嗎? 否 
  (3)邏輯判斷結果顛倒嗎?  
重要 循環問題:
  (1)循環終止條件不正確嗎?  
  (2)沒法正常終止(死循環)嗎?  
  (3)錯誤地修改循環變量嗎?  
  (4)存在偏差累積嗎?  
重要 錯誤處理問題:
  (1)忘記進行錯誤處理嗎?  
  (2)錯誤處理程序塊一直沒有機會被運行?  
  (3)錯誤處理程序塊自己就有毛病嗎?如報告的錯誤與實際錯誤不一致,處理方式不正確等等。 否 
  (4)錯誤處理程序塊是「馬後炮」嗎?如在被它被調用以前軟件已經出錯。  
重要 文件I/O問題:
  (1)對不存在的或者錯誤的文件進行操做嗎?  
  (2)文件以不正確的方式打開嗎?  
  (3)文件結束判斷不正確嗎? 否 
  (4)沒有正確地關閉文件嗎?

4.測試結果,及樣例

(1)界面設計測試

     在界面中咱們考慮到了許多在輸入時出現的小問題,併爲這些狀況的產生作了相應的處理,其程序以下:編程

void jiemian()
{
    cout << "選擇須要生成的題量(本程序最多可產生1000個算式):";
    cin >> n;//記錄須要的算式的個數
    for (; n == 0;)
    {
        cout << "請輸入大於0的數:";
        cin >> n;
    }
    cout << "選擇數值範圍:";
    cin >> min >> max;//記錄所取數的範圍
    for (; min > max;)
    {
        cout << "您輸入的數大小順序有誤,請從新輸入:";
        cin >> min >> max;
    }
    cout << endl;
    cout << "選擇運算符:" << endl;
    cout << "(1)選擇加法輸入1,不然輸入0:";
    cin >> fuhao[0];
    cout << "(2)選擇減法輸入1,不然輸入0:";
    cin >> fuhao[1];
    cout << "(3)選擇乘法輸入1,不然輸入0:";
    cin >> fuhao[2];
    cout << "(4)選擇除法輸入1,不然輸入0:";
    cin >> fuhao[3];
    for (; fuhao[0] == 0 && fuhao[1] == 0 && fuhao[2] == 0 && fuhao[3] == 0;)
    {
        cout << "請選擇至少一個符號:" << endl;
        cout << "(1)選擇加法輸入1,不然輸入0:";
        cin >> fuhao[0];
        cout << "(2)選擇減法輸入1,不然輸入0:";
        cin >> fuhao[1];
        cout << "(3)選擇乘法輸入1,不然輸入0:";
        cin >> fuhao[2];
        cout << "(4)選擇除法輸入1,不然輸入0:";
        cin >> fuhao[3];
    }
    cout << "選擇是否保留小數並選擇保留小數點後幾位(不保留輸入0.有則輸入它的最大保留位數(4之內)):";
    cin >> q;//記錄數中所能包含小數的最小位數
    for (; q <0 || q>4;)
    {
        cout << "請輸入一個0-4的整數:";
        cin >> q;
    }
    cout << "選擇運算長度:";
    cin >> maxlong;//記錄算式的長短
    for (; maxlong < 2;)
    {
        cout << "請輸入一個有效的數:";
        cin >> maxlong;
    }
    cout << "選擇是否有括號(有則輸入1,無則輸入0):";
    cin >> kh;
}

將其在程序上運行一遍所得結果以下圖所示:
數組

(2).核心算法

     咱們採用的算法就是隨機的產生一個數字,數字的範圍由輸入界面的min,max和小數點的位數q來限制,而後咱們在隨機出一位符號來構成,在輸入數的前面咱們在隨機出現左括號,當咱們產生左括號後,咱們在下個數後遍能夠隨機產生右括號,可是限制條件是一個數不可能同時出現左右括號,以及第一個數出現左括號,那麼它的右括號便不能出如今最後一位數的位置,便可。程序以下:函數

int main()
{
    void jiemian();
    fp = fopen("1.txt", "w");將咱們所產生的算式放到指定的文件
    int panduan(int x);
    int i, lon, j, b, jia = -1, jia_n = 0, lef = 1, zh = 0;
    int qw=0;
    jiemian();
    srand((unsigned)time(NULL)); //用時間作種,每次產生隨機數不同
    double num;
    int s = 0;
    int sa = 0;
    for (i = 0; i < n; i++)//由此來控制咱們所需的算式的個數
    {
        
        cout << "(" << i + 1 << ")" << ":";
        fprintf(fp, "( %d ):", i + 1);
        lon = rand() % (maxlong - 1) + 2;
        s = 0;
        for (j = 0; j < lon - 1; j++)//產生不超過咱們所規定的最大長度的算式
        {
            if (lon > 2 && kh == 1)
                jia = rand() % 2;
            if (j == lon - 2 && s == 1)//判斷隨機的左括號是否能夠加
                jia = 0;
            else
                jia = jia;
            if (jia == 1)
            {
                cout << "(";
                fprintf(fp, "(");
                jia_n = jia_n + 1;//記錄算式中多出的左括號的個數
                if (j == 0)
                    s = 1;//記錄下第一個數有右括號的寫入
                lef = 0;
            }
            jia = -1;
            num = rand() / (double)(RAND_MAX / (max - min));//產生第一個隨機數
            num = num + min;
            for (; sa == 1 && num == 0;)//若是符號爲/,那麼他的除數不爲0;
            {
                num = rand() / (double)(RAND_MAX / (max - min));
                num = num + min;
            }
            qw = rand() % q ;
            cout << fixed << setprecision(qw) << num;
            if (qw == 0)
                fprintf(fp, "%.0f", num);
            if (qw == 1)
                fprintf(fp, "%.1f", num);
            if (qw == 2)
                fprintf(fp, "%.2f", num);
            if (qw == 3)
                fprintf(fp, "%.3f", num);
            if (kh == 1 && lef != 0 && jia_n > 0)//隨機產生右括號,並判斷是否能夠加入算式
                jia = rand() % 2;
            if (jia == 1)
            {
                cout << ")";
                fprintf(fp, ")");
                jia_n = jia_n - 1;
                lef = 1;
                if (jia_n == 0)
                    s = 0;
            }
            jia = -1;
            b = rand() % fh;
            for (; fuhao[b] == 0;)
            {
                b = rand() % fh;
            }
            if (s == 1 && j == lon - 2)
            {
                for (; jia_n >= 1; jia_n--)
                {
                    cout << ")";
                    fprintf(fp, ")");
                }
            }
            sa = panduan(b);
        }
        num = rand() / (double)(RAND_MAX / (max - min));//補上算式的最後一個數
        num = num + min;
        qw = rand() % q ;
        cout << fixed << setprecision(qw) << num;
        if(qw==0)
            fprintf(fp, "%.0f", num);
        if (qw == 1)
            fprintf(fp, "%.1f", num);
        if (qw == 2)
            fprintf(fp, "%.2f", num);
        if (qw == 3)
            fprintf(fp, "%.3f", num);
        for (; jia_n >= 1; jia_n--)//補上未完成的括號
        {
            cout << ")";
            fprintf(fp, ")");
        }
        cout << "=" << endl;
        fprintf(fp, "=\n");
    }
    fclose(fp);
    system("PAUSE ");
    return 0;
}
int panduan(int x)//隨機選擇符號
{
    int data=0;
    switch (x)
    {
    case 0:cout << "+", fprintf(fp, "+"); break;
    case 1:cout << "-", fprintf(fp, "-"); break;
    case 2:cout << "*", fprintf(fp, "*"); break;
    case 3:cout << "/", fprintf(fp, "/"), data=1; break;
    default:
        break;
    }
    return data;
}

在這裏咱們選擇條件組合斷定,在VS上的運行狀況以下圖所示:

在文件中顯示的狀況以下:
工具

5總結

     俗話說」三個臭皮匠,賽過一個諸葛亮」,你們在作同一件事時,集思廣義發揮各自優勢,能夠在編程遇到問題的時候能夠有不一樣的想法,使問題獲得更加快速的解決。除此以外咱們還能夠在編寫有些算法的時候,不止侷限於只會一兩種算法的狀況,咱們能夠在更多不一樣的算法中選擇一個最簡單最容易的一個算法,這樣不只能夠提升咱們的默契,還能夠從本身夥伴那兒學習到本身不會的知識。同時咱們還能夠相互督促,相互督促可使咱們都能集中精力,更加認真的工做,在咱們同時編程的時候,夥伴之間會發現咱們的一些小毛病而且當場指出,使咱們改變了許多不良好的習慣,在上次做業的時候,咱們曾經互相看過對方的程序因此咱們也並不是都是不熟悉,在此次合做中咱們也曾發生了一些想法不一樣的狀況經過此次磨合,咱們也創建了一些默契,相信咱們在相互合做的同時,會使咱們愈來愈默契,也會是咱們相互學習的好機會,也爲咱們進入公司時,適應結對編程的方式,使咱們的實戰獲得了提升。下面是咱們此次的問題及解決方法和心得體會。
     在初次談到這個問題,咱們首先是打算作一個兩位數的四則運算,完成咱們整個程序的構建,再在這個基礎上完成多位運算和隨機加括號的操做,便完成了咱們的程序操做。在咱們的合做下很快便實現了二則運算的全部問題,在編寫隨機多位的狀況下,發現咱們的核心算法須要大改,不過咱們的努力也沒有白費,咱們只是將咱們的核心算法通過討論便加了進去,一個隨機產生小學四則運算的C++程序便出來了而後咱們進行了代碼的完善,去除了一些沒必要要的操做。接下來咱們開始準備作界面,因爲我兩都基本沒有接觸過這方面的知識,一切都顯得很陌生,開始打算用QT作,結果發現本身摸索出來的界面不能行,就改爲用VS2013自帶的MFC作一個界面,可是又遇到了許多的問題,致使咱們的程序運行起來,沒有錯,但實際執行老是不能實現,總之此次的界面設計最終破產,打算在以後好好補補這方面的知識,爭取在下次咱們在本身的技能上加入這個能力。學習

附錄:

源程序
測試程序開發工具

相關文章
相關標籤/搜索