軟工網絡結對編程做業

軟工網絡結對編程做業

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 處理輸入的字符串
        基本上都是邏輯清晰的分支結構,因此沒有「邏輯泥球」
    • 測試用例
      前端

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

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

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

  1. 程序整體設計
    python

  2. 所使用的二叉樹算法介紹
    c++

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

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

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

      部分測試用例
      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+1>2的效果嗎?

    關於結對編程的1+1是否大於2,我還不是很肯定,緣由以下。
     1. 在結對編程的過程當中,同一臺電腦,一我的敲代碼,另外一個負責複審,無可厚非會浪費掉一我的敲代碼的時間,但與此同時,又節省了複審時間,這二者之間孰輕孰重我還沒法準確比較;
     2.  結對編程的兩我的是不可能實力至關的,因而就會有一部分時間是「隊友教我作做業」的狀態OvO,在這裏對我(沒錯,我弱)的編程能力的提升是有所幫助的。這個時候相比於讓隊友直接上手,咱們更加樂意於「隊友教我作做業」,可是這個時候「教學時間」會花不少時間=-=;
     3. 我在編程時,偶爾會邏輯混亂,腦子漿糊,我能夠冷靜下來和個人隊友分析一波,理清思路再繼續編寫,由此完成咱們的項目,我以爲在這裏結對編程相比於一我的完成代碼是有所優點的;
     4. 最後一點給結對編程的優勢,在編程時,兩人能夠發揮各自的優點,促進項目的完成,就好比:數據結構的規劃是我想出來的,但我實際構建數據結構會弱一點,那麼個人隊友就負責完成這一個部分,我負責其餘部分。
  2. 經過此次結對編程,請談談你的感覺和體會。

    至於感覺:
     1. 學好英語很重要=-=。英語學很差,命名只能用拼音全拼,很是醜!
     2. 一個項目的好的設計可能不是在開始敲代碼以前就完成的,多是編寫時忽然的一個靈感來完善的。在以前,我還沒注意到老師說能夠用二叉樹,數據結構徹底是咱們本身分析出來的。起初決定用單純的樹,當時以爲「嗯,這個結構好像不錯」,後來發現多個度的樹是很難用代碼實現的(OK FINE);接着聯想到了樹轉化爲二叉樹,用左孩子右兄弟的方式存儲,「妙哇,節省空間又很快」;可是發覺答案的存儲又成了一個問題,再次改進運用了指針。
     3. 其實,不少時候項目自己不難,若是仔細分析一波,分紅一個個小塊去實現,編寫好項目是很容易的,只是你不肯意去認真而已。
     4. 作項目的時候合理控制一下時間。一方面,屁股都坐不住,就不要妄想作好一個項目;另外一方面,沉迷代碼,面對顯示屏過久人也會受不了的,同時也會影響工做效率。

以上就是該博客的所有內容,喜歡請關注+轉發+收藏+硬幣,咱們下次再見。 ↑皮這一下很開心hhhhh。

相關文章
相關標籤/搜索