結對編程_四則表達式生成

簡介

本次開發的軟件是爲幫助小學老師解決出題麻煩且不高效的問題,通過和夥伴尉安瑞的共同合做,製做出了一個可以自動生成四則運算且能計算出結果的軟件。html

  • 操做系統:Window 10
  • 語言:C++,Java
  • 開發環境:CodeBlocks,Eclipse
  • 開發人員:Stone,尉安瑞
  • Github

一. 軟件功能介紹

  • 可以自動生成四則運算練習題
  • 能夠定製題目數量
  • 用戶能夠選擇運算符
  • 用戶設置最大數(如十之內,白之內等)
  • 用戶選者是否有括號、是否有小數
  • 用戶選擇輸出方式(如輸出到文件、打印機等)
  • 提供圖形界面





二. 設計方法

在一開始設計的時候,夥伴明確的指出採用MVC模式進行軟件設計,趕忙百度一下什麼是MVC,原來這是一種軟件設計典範,把軟件分紅三部分模型(Model)、視圖(View)和控制器(Controller)進行開發,這樣會更高效,視圖層和業務層分離,代碼耦合性低,複用性高,不至於出現改一處而改所有,讓軟件沒法開發下去。
c++

軟件分紅三個部分進行開發

  • Model(模型)模塊主要存放的是程序的原語操做部分
  • View(視圖)模塊是展現給用戶並讓用戶進行輸入操做的部分
  • Controller(控制器)模塊是處理輸入,並對底層操做進行控制的部分

1. 咱們按照這個模式進行討論,首先是對最底層M的設計

  • 隨機數的生成
  • 隨機操做數生成
  • 隨機操做符生成
  • 隨機括號的生成
  • 是否有小數
  • 四則運算

這些都是原子操做,只受C層控制,其中值得一提的是在設計隨機操做符生成時,夥伴想到使用8,4,2,1來表示+,-,*,/操做符的代號之和,以後只要知道代號和,就能獲得總共有幾個操做符參與運算,它們都是什麼,而且在進行存儲的時候就能夠和操做數一塊兒進行存儲,大大下降了存儲的複雜度。git

2. 其次是對C層的設計

  • 表達式及結果的生成和存儲
  • 查重操做

C層依賴於M層,只要進行對M層的調用,再進行存儲便可github

3. 最後是V 層的設計

  • 圖形話界面
  • 文件的生成

因爲是結對編程,M,C層的駕駛員主要由我負責,V層則由夥伴負責,這層主要設計請參考這裏算法

三. 代碼(部分代碼)

1. Model模塊

1.1 隨機數生成
/**
*Summary: 隨機數生成
*Parameters:
* range: 隨機數範圍
**/
int random(int range)
{
    //srand(time(0));
    return rand()%range;
}
1.2 隨機操做符生成
/**
*Summary:隨機操做符生成
*Parameters:
* opreation: 表示操做符代號之和
*   + :加法,用數字8表示
*   - :減法,用數字4表示
*   * :乘法,用數字2表示
*   / :除法,用數字1表示
*return: 操做符代號
**/
int getOperation(int opreation)
{
    int count = 0;
    int i = 0;
    int temp = opreation;
    for(;i<4;i++)
    {
        count+=(temp&1);
        temp = temp>>1;
    }
    count =  random(count);
    for(i = 1;i<=8;i=i*2)
    {
        if((opreation&i) != 0)
        {
            if(count==0)
            {
                return i;
            }
            else
            {
                count--;
            }
        }
    }
}
1.3 隨機操做數數生成
/**
*Summary:隨機操做數數生成
*Parameters:
* max: 操做數取值上限
* decimal: true 表示小數 false 表示整數
* negative: true 表示負數 false 表示正數
*return: 隨機操做數
**/
float getOperand(int max, bool decimal, bool negative)
{
    if(!decimal && !negative)
    {
        return random(max);
    }
    else if(!decimal && negative)
    {
        return -1*random(max);
    }
    else if(decimal && !negative)
    {
        return random(max)/100.0+random(99);
    }
    else
    {
        return -1*(random(max)/100.0+random(99));
    }
}
1.4 隨機括號生成
/**
*Summary:隨機括號生成
*Parameters:
* max: 表示左括號 max+1表示右括號
* left_bracket: 未匹配的左括號個數
*return: 括號代號
**/
int getBracket(int max, int &left_bracket)
{
    if(left_bracket == 0)
    {
        return max;
    }
    else
    {
        return max+random(2);
    }
}
1.5 運算
/**
*Summary: 進行運算
*Parameters:
* myExpression: 算術表達式
**/
float evaluateExpression(char* myExpression)
{
    // 算術表達式求值的算符優先算法
    // 設OPTR和OPND分別爲運算符棧和運算數棧,OP爲運算符集合
    SC *OPTR=NULL;                                   // 運算符棧,字符元素
    SF *OPND=NULL;                                   // 運算數棧,實數元素
    char tempData[20];                               // 用來存儲、轉換操做數
    float data, a, b;
    char theta, *c, Dr[] = {'#', '\0'};              // Dr[]的'#'用來使傳進來的字符串尾爲'#','\0'是字符串結束的標誌,控制其長度
    OPTR = push(OPTR, '#');
    c = strcat(myExpression, Dr);
    strcpy(tempData, "\0");                         //字符串拷貝函數,讓tempData[0]爲"\0"
    while (*c != '#' || OPTR->c != '#')
    {
        if (!in(*c, OPSET))
        {
            Dr[0] = *c;
            strcat(tempData, Dr);               //字符串鏈接函數
            c++;
            if(in(*c, OPSET))
            {
                data = atof(tempData);          //字符串轉換函數(double)
                OPND = push(OPND, data);
                strcpy(tempData,"\0");
            }
        }
        else if(in(*c, OPSET))                  // 不是操做數則進棧
        {
            switch(precede(OPTR->c, *c))
            {
            case '<': // 棧頂元素優先級低
                OPTR = push(OPTR, *c);
                c++;
                break;
            case '=': // 脫括號並接收下一字符
                OPTR = pop(OPTR);
                c++;
                break;
            case '>': // 退棧並將運算結果入棧
                if(OPTR->c == '#')
                {
                    cout << "輸入錯誤!";
                    break;
                }
                theta= OPTR->c;
                OPTR = pop(OPTR);

                if(OPND==NULL)
                {
                    flag = false;
                    return 0;
                }
                b = OPND->f;
                OPND = pop(OPND);

                if(OPND==NULL) 
                {
                    flag = false; 
                    return 0;
                }
                a = OPND->f;
                OPND = pop(OPND);

                float p = operate(a, theta, b);             //p用來記錄運算後的結果
                if(!flag) return 0;                         //若運算不合法跳出函數
                OPND = push(OPND, p);
                break;
            } //switch
        }
    } //while
    return OPND->f;
} //evaluateExpression

2. Controller模塊

2.1 查重
/**
*Summary: 查重
*Parameters:
* rep: 生成的表達式倉庫
* flag: 新生成的表達式下標
*return: false表示沒有重複的表示式 true反之
**/
bool recheck(Repertory* rep,int flag)
{
  for(int i = 0; i<flag; i++)//遍歷flag行以前的表達式
  {
      bool target = true;
      for(int j = 0; j<(rep->col-1); j++)
      {
          if((rep->array[i][j]!=rep->array[flag][j]))
          {
              target = false;
          }
      }
      if(target)
      {
          return true;
      }
  }
  return false;
}
2.2 輸出到控制檯
/**
*Summary: 輸出到控制檯
*Parameters:
* rep: 生成的表達式倉庫
* output: 0表示輸出到控制檯,1表示輸出到文件,2表示輸出到打印機
* isResult: true表示計算出結果,false表示不計算出結果
* isBracket:ture表示有括號,false表示沒有括號
**/
void getResult(Repertory* rep, bool isResult, bool isBracket)
{
    int n = 0; //記錄左括號數
    bool turn = false; //標誌,當操做符後有括號時爲true,避免出現一個操做數被一對括號套住
    for(int i = 0;i<(rep->row);i++)
    {
        char equation[100]={'0'};
        string str="";
        for(int j = 0;j<(rep->col-1);j++)
        {
            if(j%2==0)
            {
                if(j==0&&isBracket&&random(2))//第一位操做數是否生成左括號
                {
                    str+="(";
                    cout << "(";
                    n++;
                }
                str+= to_string(rep->array[i][j]); //將操做數放到字符串str中

                if(j!=0&&isBracket&&random(2)&&n!=0&&!turn)//是否須要生成右括號
                {
                    n--;
                    str+=")";
                    cout<<rep->array[i][j]<<")";
                }
                else
                {
                    turn = false;
                    cout<<rep->array[i][j];            //打印單個操做數
                }
            }
            else
            {
                str+=configure[(int)rep->array[i][j]-1];//將操做符放到字符串str中
                if(isBracket&&random(2)&&((rep->col-j)/2>n+1))    //是否須要生成左括號
                {
                    turn = true;
                    n++;
                    str+="(";
                    cout<<configure[(int)(rep->array[i][j])-1]<<"(";
                }
                else
                {
                    cout<<configure[(int)(rep->array[i][j])-1];//打印單個操做符
                }
            }
        }
        while(n!=0)
        {
            str+=")";
            cout << ")";
            n--;
        }
        cout << "=";
        strcpy(equation, str.c_str()); //將字符串str轉換爲字符數組equation
        if(isResult)
        {
            calculate(equation);  //計算並打印出結果
        }
        cout <<endl;
    }
}

四.對夥伴的評價

小夥伴是一位特別有經驗的人,在拿到題目時,他首先提出了此次軟件的開發模式,並很快的進行模塊化的分解,這也是本次開發可以快速高效進行的一大重要因素;在開發過程當中,他思惟敏捷、清晰,老是能在關鍵的地方提出很是有建設性的建議,就像當咱們在肯定如何生成隨機運算符時,他給出了一個很高效的算法,把+,-,*,/用數字8,4,2,1表示,並用他們之間的代數和來表示總共有幾種操做符及都是哪些,這樣一來即解決了問題,也避免了去使用多重條件語句來判斷,提升了編程的效率;同時他也是一位穩重的人,在咱們進行討論的時候,我老是一有想法就立馬講出來,並無通過本身的仔細推敲,每每會形成打亂咱們的編程節奏,使之偏離方向,而夥伴則會通過仔細的推敲驗證後纔會把本身的想法提出來,而且可靠性很強;他仍是一位有耐心,有團隊精神的人,在出現我沒搞明白的地方時,他會耐心的和我講解,讓我很快的跟上節奏;他很注重細節,在給變量、函數等命名時,必定要找到一個能很容易看明白的詞,而且一開始就和我統一編碼風格和註釋風格,應爲這是結對編程,不是一我的進行開發,讓對方很快的明白代碼的意思很重要。編程

總之,他的這些優勢都是值得我去學習的,很開心能和他一塊兒合做開發。數組

五.總結

本次結對編程, 對我感觸最大的就是建一個適合該工程的好模型。可能對於一個小項目,拿來直接就上手,通常來講都沒太大問題,但只要稍微的加大點難度,再增長點功能,沒有一個好的模型支撐,寫到後面確定是要亂的,即便你寫下來了,可擴展性和可維護性是極差的。在此次編程中,我主要負責C,M層的底層開發,剛開始,都把每個功能儘量的模塊化,一切開發的都挺順利,但到了添加隨機括號時,由於當時就只差這個功能了,就只想着怎麼快速的實現,結果最後,雖然實現了,但致使代碼的複用性極低,甚至只要稍微該一點,就得對代碼進行重構,這一點很是不利於對項目的管理和維護。一樣,結對編程最大的特色就是須要兩人溝通,本身寫代碼可能不是問題,可怎麼把本身的idea有效的傳遞給對方,這就須要本身多加練習。dom

總的來講,此次結對編程讓我學習到了不少,不只在編程、設計方面,友誼方面也獲得了收獲,期待着下一次和他的合做。ide


相關文章
相關標籤/搜索