集大軟件工程15級結對編程week1

集大軟件工程15級結對編程week1

0. 團隊成員

姓名 學號 博客園首頁 碼雲主頁
孫志威 20152112307 Agt Eurekaaa
孫慧君 201521123098 野原澤君 野原澤君

1. 需求分析:針對現有代碼的改進分析,新開發功能的分析。

1. 題目需求:

  • 原題要求:
    • 寫一個能自動生成小學四則運算題目的命令行 「軟件」:
      • 除了整數之外,還要支持真分數的四則運算,真分數的運算,例如:1/6 + 1/8 = 7/24;
      • 運算符爲 +, −, ×, ÷;
      • 而且要求能處理用戶的輸入,並判斷對錯,打分統計正確率
      • 要求能處理用戶輸入的真分數, 如 1/2, 5/12 等;
      • 使用 -n 參數控制生成題目的個數,例如執行下面命令將生成10個題目 :Myapp.exe -n 10。
    • 把這個程序作成GUI:
      • 記錄用戶的對錯總數,程序退出再啓動的時候,能把之前的對錯數量保存並在此基礎上增量計算;
      • 有計時功能,能顯示用戶開始答題後的消耗時間;
      • 界面支持中文簡體/中文繁體/英語,用戶能夠選擇一種。
    • 兩個任務:
      • 把計算模塊提取出來,單首創建一個類。
      • 針對提取出來的計算類的接口函數作單元測試。
  • 功能改進與擴展:
    • 增長括號操做符;
    • 減小重複題目;
    • 增長一個運算符(乘方):用符號 ^ 表示乘方,例如:4 ^ 2=16;
    • 迴歸測試:在開發新功能時避免損壞舊的功能,以確保新的功能不與原有功能衝突,在確認修改的功能正確以後再嵌入代碼;
    • 效能分析。

2. 代碼分析

  • 獲取代碼
    • 引用老師提供的代碼爲: 碼雲地址
    • Clone該項目
  • 分析結構
    • 類之間關係
      比較碼雲的連接中給出的代碼只有兩個類:
      TrivialCalculatorTrivialCalculatorTest
    • 類圖
    • 邏輯分析:該項目一共只有4個函數,分別爲
      • gcd 求出最大公約數
      • cal 實現兩個數字的加減乘除計算
      • zfcal 實現兩個分數的加減乘除
      • calinput 處理輸入的字符串
        基本上都是邏輯清晰的分支結構,因此沒有「邏輯泥球」
    • 測試用例
      html

      測試用例完整覆蓋了全部函數的各個分支前端

    • 分析現有代碼須要改進的地方:
      1. 沒有實現真分數和整數的同時存在:使用隨機實現真分數和整數的可能同時出現。
      2. 沒有實現表達式中數字個數的隨機:利用宏定義統一設置表達式數字個數的最多個數,從2到設置的最多位數隨機取一個數,決定表達式的數字長度。
        【表達式的生成利用循環「隨機符號+隨機數字」,最終去除第一位符號來獲得】
      3. 碼雲裏的代碼不能運行;
      4. GUI界面有待改善;
      5. 代碼可讀性不強,缺乏相應的註釋。
      • 新開功能的分析:
      1. 添加括號:
        • 表達式中數字個數大於2時,可隨機添加括號;
        • 左括號的添加位置能夠爲表達式前,或各個運算符的後面(除了最後一個運算符),右括號的添加位置能夠爲表達式的最後,或與左括號相隔一個符號開始到表達式最後的每一個運算符的左邊。
      2. 減小重複題目:
        • 將表達式分解爲多個「符號+數字」組合的數組,第一位數字前符號爲「+」;
        • 對組合數組進行排序,並放入二叉樹,左子節點爲該父節點組合在數組中的下一位,右子節點爲在數組中擁有相同上幾位組合的同位的不一樣組合;
        • 每生成一個表達式就v在該二叉樹內檢索插入,已存在則剔除從新生成。(直說好難理解,下面會用圖解釋)
      3. 添加乘方:
        • 爲了減少使用者和計算機內部的計算量,將最多隻添加一個二次冪或三次冪;
        • 遍歷表達式字符串,獲得運算符的位置,並在這些位置和表達式的末尾中隨機抽取一個位置,在其右邊添加"^2"或 "^3"。

2. 程序設計:針對新開發功能作設計,建議使用思惟導圖。

  • 程序整體設計
    node

  • 所使用的二叉樹算法介紹
    python

  • 程序主要部分介紹(C++,C++/QT,Python,C#)
    • 生成表達式(C++):採用隨機產生字符拼接成字符串的方式生成
    • 加工表達式(C++)
      • 對生成的表達式進行合法性檢查
      • 在合理位置添加括號、 冪運算等操做
      • 將表達式分割爲以「符號+數字」的更小元素
      • 將表達式以「元素」爲單元進行統一排序
    • 避免表達式廣義重複(C++)
      • 構建了一個二叉樹結構用來解析排序後的表達式是否重複
      • 二叉樹將添加並維護存在過的全部表達式
    • 解析表達式(python)
      • 使用C++調用python接口
      • 使用python的eval函數動態解析表達式
        因此就不用正常的中綴-後綴 + 棧解析的作法了(py大法好)
    • 前端顯示(C++/QT)
      • 用QT簡單的作了個顯示錶達式、能檢測輸入框答案是否正確的GroupBox
      • 而後運行時在MainWindows裏new幾個Box就醬
    • 單元測試部分(C#)
      • 單元測試用的是MS測試框架
      • 測試項目不須要與正常代碼放在同一項目下,‘引用’功能和測試資源管理器都十分好用
  • 各新增功能演示
    • 支持括號
      c++

    • 表達式不重複
      表達式重複的概率爲0,具體算法見二叉樹算法處
    • 乘方運算
      git

  • 測試分析結果
    • 迴歸測試:
      測試方法大綱

      部分測試用例
      算法

    • 代碼覆蓋率測試
      express

    • 效能分析
    • 消耗最大模塊
      • 發現佔用絕大部分時間的都不是用戶的函數
        基本都是系統調用
      • 可是在用戶代碼中有一個 Evaluator::initPython方法被調用了124次,實際上這裏是能夠優化的
      • 優化建議:將Evaluator的一些須要全局常常調用的方法設置爲static的類方法,一旦init後就不須要再調用了,能夠大大減小該函數的調用次數

3. 代碼展現:展現每一個功能的核心代碼。

  • 字符串生成類編程

    class ExpGenerator
    {
    public:
        ExpGenerator();
        ~ExpGenerator();
        int getGcd(int x, int y);
        string generateRawExp();
        vector<int> findOperator(string exp);
        string addBracket(string exp);
        string addPower(string exp);
        vector<string> apartExp(string exp);
        vector<string> sortExp(vector<string> items);
        vector<string> generate(int amount,  
                     vector<double>&results);
    };
    • 生成字符串數組

      string ExpGenerator::generateRawExp()
      {
          //產生初始表達式
          string exp = "";
          char ch[5] = { '+','-','*','~','^' };
          int maxNum;
      
          if (NUMBER < 2)
          {
              //表達式位數小於2則報錯
              exp = "ERROR!";
              return exp;
          }
          else if (NUMBER == 2)
              maxNum = 2;
          else
              maxNum = rand() % NUMBER + 2;
      
          for (int i = 0; i < maxNum; i++)
          {
              //以(符號+數字)*maxNum造成第一位爲符號的「僞表達式」
      
              //隨機生成符號
              char c = ch[rand() % 4];
              exp.push_back(c);
      
              //隨機生成數字
              stringstream ss;
              string s;
              int flag = rand() % 10;
              if (flag != 0)
                  //生成整數(90%)
                  ss << rand() % MAX + 1;
              else
              {
                  //生成分數(10%)
                  int mumNum = rand() % MAX + 2;
                  int sonNum = rand() % (mumNum - 1) + 1;
                  sonNum /= getGcd(mumNum, sonNum);
                  mumNum /= getGcd(mumNum, sonNum);
                  ss << sonNum << "/" << mumNum;
              }
              ss >> s;
              exp += s;
          }
      
          //截去第一位符號,生成初始表達式
          exp = exp.substr(1, exp.length());
          ////cout << exp << endl;
          return exp;
      }
    • 拆分字符串並排序
      ```C++
      vector ExpGenerator::apartExp(string exp)
      {
      //遍歷初始表達式,以「符號+數字」的結構分割返回
      vector items;
      int begin = 0;
      int end;
      unsigned int i;
      string str;
      exp = "+" + exp;
      char c;
      for (i = 0; i < exp.length(); i++)
      {
      c = exp.at(i);
      if ((c == '+' || c == '-' || c == '*' || c == '~')
      && (i != 0))
      {
      end = i;
      str = exp.substr(begin, end - begin);
      items.push_back(str);
      begin = i;
      }
      }
      str = exp.substr(begin, exp.length() - begin);
      items.push_back(str);

      return items;

      }

      vector ExpGenerator::sortExp(vector items)
      {
      //將分割後的表達式項進行排序,用於二叉樹的造成
      sort(items.begin(), items.end());
      return items;
      }
      ```
  • 二叉樹相關
    ```C++
    class UniqueBinaryTree
    {
    public:
    UniqueBinaryTree();
    ~UniqueBinaryTree();

    // check if the tree contains the expression
      // if contains return true
      // else return false 
      // and add the unique new nodes to the tree
      bool add(vector<string>expressionVec, double answer);
      // clear the tree  but not destoryed it 
      void clear();
      // get amount of the nodes
      int nodeAmount();
      // get amount of all the resultsNodes
      int resultsAmount();
      // amount of both result node and the mormal node
      int amount();
      // Debug: show contents to the console
      void showLayer();

    private:
    // add the result node to the last node (levelNode)
    // return true if new result added
    // which represent that the new expression
    // has the same expression(without brackets) but
    // different answers
    bool mountAnswerToNode(PNode levelNode, double answer);
    int countAnswerNode(PNode levelNode);
    void clearAnswerNode(PNode levelNode);

    Node * head = NULL;

    };
    - add函數C++
    bool UniqueBinaryTree::add(vector expressionVec , double answer)
    {
    // treat empty string as true/contains and ignore it
    if (expressionVec.empty())
    return true;

    int length = expressionVec.size();
          PNode curNode = this->head->right; // compare each node from the first
          PNode fatherNode = this->head; 
          string currentStr;
    
          int index = 0; // the current index of stringVector
          bool isContained = false;
          while (curNode != NULL)
          {
    
              currentStr = expressionVec.at(index);
              // record the father node to support the insertion later
              fatherNode = curNode; 
              if (curNode->data == currentStr)
              {       
                  curNode = curNode->left;
                  // jump to the next string
                  index++;
                  // break when the whole expression finish
                  if (index >= length)
                      break;
              }
              else
              {
                  curNode = curNode->right;
              }
          }
          //if not then it's an new expression
          // mount it to the tree  
          // begin from the last part they're same
          // the current node is the last node matches part of the new expression
          bool first = true;
          // if it reached the end of the expression in the last loop
          // will skip it
          while (index < length)
          {
              string curStr = expressionVec.at(index);
    
              PNode newNode = new Node();
              if (first)
              {
                  // mount the first newNode as the rightChild
                  fatherNode->right = newNode;
                  first = false;
              }
              else
              {
                  fatherNode->left = newNode;
              }
    
              newNode->data = curStr;
              newNode->left = NULL;
              newNode->right = NULL;
              newNode->answer = NULL;
              fatherNode = newNode;
              index++;
          }
          // now the new expression has been added to the tree
    
          // add the answer of the expression as well
          // and check if the answer is inside the tree 
          // if it's inside it   return false as well
          PNode lastNode = fatherNode;
          bool newAnswerAdded = this->mountAnswerToNode(lastNode,answer);
          // new answer node is unique , and been added to the tree
          return !newAnswerAdded;
      }
      ```
  • 具體代碼見碼雲Agt-TrivialCalculator


4. 程序運行:程序運行及每一個功能的使用截圖。

  • 如下圖片分別演示了
    1. 隨機生成字符串
    2. 表達式的加減乘除
    3. 表達式的括號運算符以及冪運算
    4. 用戶的輸入和結果正確反饋
    5. 表達式的不重複
    6. 表達式的正確解析
    7. etc...

5. 結對編程記錄

  • 碼雲記錄

  • 編碼規範文檔

  • PSP表格
  • PSP2.1 我的開發流程 預估耗費時間(分鐘) 實際耗費時間(分鐘)
    Planning 計劃 20 30
    Estimate 明確需求和其餘相關因素,估計每一個階段的時間成本 10 30
    Development 開發 413 940
    Analysis 需求分析 (包括學習新技術) 20 60
    Design Spec 生成設計文檔 60 60
    Design Review 設計複審 60 30
    Coding Standard 代碼規範 3 10
    Design 具體設計 30 60
    Coding 具體編碼 120 400
    Code Review 代碼複審 60 200
    Test 測試(自我測試,修改代碼,提交修改 60 120
    Reporting 報告 60 120
    · 測試報告 10 20
    · 計算工做量 30 30
    · 並提出過程改進計劃 60 60

6. 小結感覺:結對編程真的可以帶來1+1>2的效果嗎?經過此次結對編程,請談談你的感覺和體會。

我認爲1+2是大於2的,結對編程仍是有不少收穫的。
例如在考慮問題的解決方案的時候,一我的本身思考的時候很容易被本身過去的經驗所影響,容易被本身的思惟定勢束縛住。而在結對編程的時候,因爲一邊寫一邊審,互相之間會交流爲何用這種算法,算法有什麼優缺點等,能更好地找到更佳的解決方案,因此結對編程也仍是有好處的。

隊友的感想見博客野原澤君

相關文章
相關標籤/搜索