1. 程式人生 > 其它 >2022 OO 第一單元個人總結

2022 OO 第一單元個人總結

2022 OO 第一單元個人總結

一、前言

對於本單元的專案設計,我認為以下三點是值得深思與提升的:

  1. 選取合適的資料結構儲存資訊,資料結構的選取合適程度,與表示式化簡的難度以及由程式碼優化產生的bug數量息息相關;

  2. 採用遞迴下降的方法解析expression;

  3. 通過表示式預處理,更優美地書寫程式碼;

但我們不得不承認,預處理化簡等不論是優化程式碼本身、抑或結果輸出的方式,都極大的增加了bug產生的可能性。因為優化的同時,就意味著我們考慮更少的可能性,也就存在更多的非法情況。對於 優化bug 之間的矛盾,是需要我們權衡的。

對於三次作業的迭代,筆者認為對於專案的思考,主要聚焦於資料結構選取

是否進行表示式的預處理、以及化簡的程度這三點。

對於遞迴下降分析法的應用,在第一次作業中也許是一件有挑戰的事,但在第二、三次作業中,也並不顯得很困難。

故而本部落格將僅對於第三次作業進行全面分享,並輔以 與第一次作業的資料結構與第二次作業的程式碼優化、表示式預處理 進行對比,闡述自己的思考。

二、專案迭代的最終版本

1. 形式化表述

首先,為方便觀看與回憶,附上第三次作業的形式化表述(本部落格不再討論關於課程限定等細枝末節的內容,而是聚焦於答題思路,課程限定不過是為了方便我們完成專案)。

2. 資料結構與程式架構

程式 = 資料結構 + 演算法

資料結構

在本程式中,資料結構的選擇需要面對儲存

化簡兩個問題。

Factor -> Term -> Expression

對於儲存的需求,我們自然而然地建立了NumPowerSinCos四個類來儲存最基本的資訊。並通過Factor這個抽象類來實現對基本類的統一。對於 SinCos如何儲存資訊(或者說其內部變數的資料結構)暫且不論,至少,我們擁有了這四個最基本的類,構成了可以完成本專案的最基本的資料結構。

在構造完成上一段的基本類後,事實上我們完成了最基本的Factor需求,接下來便是思考如何完成將Factor連乘為Term的資料結構構造。

近乎自然的發散思維,可以讓我們聯想到將基本類統一為一個NormalOfFactor

類,正如數學模型C*x^p*∏(Sin()^q)*∏(Cos()^r這樣的形式(就像物理一直追求四個基本力大一統一樣)。該類通過如下 資料結構儲存資訊方法完成Term連乘功能

    private String op;
private BigInteger coe;
private BigInteger pow;
private ArrayList<Sin> sinFactors = new ArrayList<>();
private ArrayList<Cos> cosFactors = new ArrayList<>();

public void addFactorOfNum(Num num) //事實上,可以將四個方法再次包裝成一個方法:
   public void addFactorOfPower(Power power) //addFactor(Factor factor);
   public void addFactorOfSinArray(Sin sin)
   public void addFactorOfCosArray(Cos cos)

接下來,我們建立Term類,用來實現Term的功能,分析Factor與完成計算。

    private final ArrayList<NormalOfFactor> normalOfFactors = new ArrayList<>();

public void addFactor(Factor factor); //將Factor對與normalOfFactors中每個元素乘

根據形式化表述,我們可以將Factor分為兩類,基本類與表示式,基本類的連乘方法已在NormalOfFactor中實現,而考慮表示式的作用,表示式的連乘功能將在Expr類中實現。

另外,normalOfFactors的存在,是為了滿足表示式因子的計算,如(x+1)(x+2)。

通過上面的構建,我們已經擁有了很多個Term,現在的目的是將terms,計算出expression

expression具有兩個內容,一是將terms加在一起,二是將其計算冪次

不考慮化簡的情況下,功能一僅需要將每個termnormalOfFactors加在一起,所以在Expr,我們仍定義normalOfFactors而非terms

    public void addTerm(Term term) {
       for (NormalOfFactor normalOfFactor : term.getNormalOfFactors()) {
           addOneOfTerm(normalOfFactor); //在新增時化簡
      }
  }

而對於功能二,在NormalOfFactor類的方法下,就是相當於一個三重迴圈。

public void calForPow();
public void multiTwoNormalOfFactors(ArrayList<NormalOfFactor> normalOfFactors1,
           ArrayList<NormalOfFactor> normalOfFactors2,ArrayList<NormalOfFactor> newNormalOfFactors);
private void multiNormalOfFactorToNormalOfFactors(NormalOfFactor normalOfFactor,
           ArrayList<NormalOfFactor> normalOfFactors,ArrayList<NormalOfFactor> newNormalOfFactors);
private void multiTwoNormalOfFactor(NormalOfFactor normalOfFactor1,
           NormalOfFactor normalOfFactor2,ArrayList<NormalOfFactor> newNormalOfFactors);

我們可以看出,此前挖的坑(Term如何處理Expr因子),只需要呼叫expr.multiTwoNormalOfFactors()即可。

於是,拋去一些細枝末節的東西,資料結構的儲存便如此完成了。


現在我們討論對化簡的需求。

注意:此時未根據三角函式的性質化簡(奇偶性、三角恆等式)

化簡的思路只有一個:給定標準型,然後遞迴下降

我們自頂而下看:

  1. 如何化簡兩個項相加

    • addTerm時,對於相同的normalOfFactor完成合並(即兩個coe相加);

    • 如何識別normalOfFactor可以合併?

      • 規範normalOfFactor.toStringExcCoe;

      • normalOfFactor.toStringExcCoe相等,則認為可以合併;

    • 最終:保證ExprnormalOfFactors不存在可化簡情況

  2. 如何化簡兩個因子相乘

    • coe直接相乘

    • pow直接相加

    • sin(expr)^p二重迴圈

      • 如果expr.toString相同,則兩個p相加;

        • expr通過Collections.sort(normalOfFactorsToString)提供排序規範;

    • cos(expr)^p二重迴圈

      • 如果expr相同,則兩個p相加;

        • expr通過Collections.sort(normalOfFactorsToString)提供排序規範;

    • 最終:保證TermnormalOfFactors不存在可化簡情況

  3. 如何確定normalOfFactorsToString相等?

    • coe + * + normalOfFactor.toStringExcCoe

      • 規範Num.toString;

      • 規範normalOfFactor.toStringExcCoe

        • x^p相等;

          • 規範Power.toString;

        • 通過Collections.sort(sinStrings)規範∏(Sin()^q)的String輸出順序;

          • 規範Sin.toString;

        • 通過Collections.sort(cosStrings)規範∏(Cos()^q)的String輸出順序;

          • 規範Cos.toString;

上文中省略瞭如sin(0), coe == 0, pow == 1等情況的討論。

如上述討論,我們自頂而下分析,通過Collections.sort(Strings)這個java自帶的函式,通過重寫toString函式,完成了排序與優化。


演算法

預處理

對於輸入的expr,對其進行五方面的化簡:

  • 消除" "與"\t";

  • 將"**"替換為"^";

  • 使expr僅連續有一個正負號;

  • 使數字沒有前導零;

  • 消除一次冪;

自定義函式

首先儲存函式,並進行預處理,後在使用時對引數進行替換。注意,對於次冪要加入()防止出現表示式無括號的情況

求和函式

對於求和函式,先將i進行替換,後進行預處理。注意,對於次冪要加入()防止出現表示式無括號的情況

3. 程式碼度量

UML圖

OO度量

LOC:Lines of Code: 類/方法程式碼規模

NAAC:Number of Attributes: 類屬性個數

NOAC:Number of Methods:類方法個數

CONTROL:Number of Control statements:控制分支個數

  • 程式碼統計

   

  • 類度量

  

  • 類複雜度度量

  

  • 方法複雜度度量

  

  

  

  

  

 

  • 類耦合度量

     

    複雜度較低,耦合度較低,內聚情況良好

4. 對比

與第一次作業對比,增添了預處理,極大的減少了Parser類中toParsertoTermtoFactor的複雜度,但會由此產生更多的bug

與第二次作業相比,程式碼優化與資料結構的構造,讓程式碼變得優雅而輸出複雜度低;但同樣,存在一定的bug,並且資料結構複雜。

三、bug分析

一個最重要的bug,就是對於深淺拷貝理解不充分導致的。

對於大多數情況,如項的加減,淺拷貝即可。但對於表示式的冪次,必須要深拷貝,因為要多次運用其原表示式。

其他大多bug一個出現於化簡程式碼,另外均出現於sum類。由於在sum類代換後,未進行表示式化簡。

事實上,許多bug的產生是由於測試不充分導致的,應當進行更多、更充分的測試。

四、架構體會與感想

本單元的練習,我對遞迴下降的思想有了更深刻的理解與應用。

同時,對於OO的思想,高內聚低耦合的設計思路,有了自身的理解。

希望在後續學習中,安排更多的時間來早完成、多測試。