由於前兩次做業設計複雜度差異不大,於是放在這裏統一總結。正則表達式
前兩次做業確實存在缺少可拓展設計的構想,基本仍是面向過程的思惟方式。「一類到底,一main到底」,由於有代碼風格的要求被迫將代碼模塊化(捂臉)。算法
初次接觸正則表達式,第一次設計正則表達式的時候並不知道正則的內部實現,出現了「一個大正則」,後來瞭解到許多正則匹配模式(貪婪,懶惰,獨佔)。兩次做業都改爲了小正則匹配同時捕獲,這樣能夠有效避免正則爆棧的問題。shell
String expon = "[\\t ]*(\\^[\\t ]*[+-]{0,1}\\d{1,}){0,1}[\\t ]*";//指數 String subterm = "([\\t ]*(\\*[\\t ]*" + //後續表達式 "(((cos[\\t ]*\\([\\t ]*x[\\t ]*\\)[\\t ]*)|" + "(sin[\\t ]*\\([\\t ]*x[\\t ]*\\)[\\t ]*)|x)" + expon + "|([+-]{0,1}\\d{1,}[\\t ]*))))*"; String regax1 = "([\\t ]*\\d{1,}[\\t ]*" + subterm + "|([\\t ]*((cos[\\t ]*\\([\\t ]*x[\\t ]*\\)[\\t ]*)|" + "(sin[\\t ]*\\([\\t ]*x[\\t ]*\\)[\\t ]*)|x)" + "(" + expon + subterm + "|" + subterm + ")" + ")" + "|([\\t ]*[+-]\\d{1,}" + subterm + ")" + ")"; String regex = "([\\t ]*[+-][\\t ]*" + //表達式開頭 "((((cos[\\t ]*\\([\\t ]*x[\\t ]*\\)[\\t ]*)|(sin[\\t ]*\\([\\t ]*x[\\t ]*\\)[\\t ]*)|x)" + expon + subterm + ")" + "|([+-]" + regax1 + ")" + "|(\\d{1,}[\\t ]*" + subterm + ")" + ")[\\t ]*)";
設計正則表達式時,我運用了簡單的樹結構,由於每一個項的第一個因子較爲特殊,因此單獨設計,每一個項的後續因子具備重複性,因此統一設計。(其實就是暴力列舉全部狀況)模塊化
求導處理方面,由於沒有很好考慮到可拓展性,簡單的暴力求導,公式以下:優化
存儲方面,利用Arraylist存儲每個項,每一個項內存儲各個因子的指數。由於後來才解了Hashmap,發現對於前兩次做業,Hashmap比ArrayList合併時有更大優點。spa
bug主要存在於輸出方面的,由於優化時想去除常數因子爲0的項,由於考慮不周,因此出現+0無輸出的bug。操作系統
一、聚焦於WF檢測,根據本身設計的正則反向構造許多反例,可是發現你們WF寫的都很好,確實難頂。設計
二、設計一些邊緣數據,好比0*sin(x)^0。(在我那個組基本沒用,你們都統一設計,未發現刁鑽數據一刀hack1人以上)code
三、借別人的數據來測·····難頂對象
四、寫了個shell腳本,能用操做系統的知識解決一次測一組人的實際需求(太頂了)
此次做業簡直就是地獄,週五髮指導書,原本還想沿用前兩次的正則構造思想,大量查閱資料,發現要遞歸定義正則(本身定義本身),實在是寫不出來,放棄了這個選擇。週六周天毫無頭緒,週一瞭解到了一個遞歸降低的算法,看懂代碼以後原本想拿來主義變成本身的,可是無奈本身沒法復現如此精妙的遞歸降低,週二凌晨3點重構,從頭開始。
本次做業主要分紅兩個工做:
一、WF判斷,這一部分我並不想前兩次,在處理以前就判斷,而是考慮到括號的遞歸存在,因此在處理時若是不符合簡單小正則規範,則輸出WF(在這以前有「錯誤符號檢測」「空格及製表符模式檢測」「+-符號個數檢測」)。
二、求導,針對此次做業十分複雜的特性,使用遞歸求導:
1)、Poly求導結果爲Poly中的Term求導相加
2)、Term求導爲Term中每一個Factor求導後與剩餘全部Factor相乘;
3)、Factor求導分爲5大類:x、sin(x)、cos(x)、constant、Polyfactor,其中Polyfactor求導遵循1)規則
表達式處理是我認爲本次做業最難的部分,我採用的(借鑑的)辦法是,用+將Poly分紅Term(在這以前用棧的方式,標記處於每一個Term外的+,將Term內的+換成別的符號),而後用*將Term分紅Factor(分紅Factor過程分爲5類,同時進行簡單小正則WF判斷),進而求導。(這裏注意,在求導前,全部構造已經完成,即已經將全部表達式因子(Polyfactor)拆分紅新的Term)
(Poly和Term之間不存在繼承關係,Factor與其餘5個因子均爲繼承關係)
以上即爲個人方法(借鑑吸取別人的方法),下面我想重點介紹我理解的(僅僅我的理解,歡迎大牛指導批評),僅針對本題的遞歸降低算法:
遞歸降低算法在本題主要針對於表達式處理,由於代碼版權屬於別人(我真菜),就不貼代碼了,簡單介紹一下:
1)、各個類的構造辦法比較平凡,和個人辦法中類的構造大同小異。
2)、在斷定表達式WF中(前提是已經進行了「無效字符檢查」「空格格式檢查」),採用的方式是將問題下放,在最終的各個因子中進行「小正則」的簡單檢查。
eg:sin((x)
總體方法:調用toPoly()方法,toPoly()方法中調用toTerm()方法,toTerm()方法中調用toFactor()方法,toFactor()方法先檢測到這屬於sin類,判斷「sin(」 是否存在且合法,而後跳過,接着對於中間的部分進行toFactor()方法繼續構造(根據定義,sin括號內必須爲一個5種因子(constant,sin,cos,x,Polyfactor)中的一種),接着回到toFactor()方法,檢測後面的「)」,即完成了Sin(Factor)檢查,(至於Factor是否合法,那是Factor的事)。
追蹤本eg:toFactor()方法,判斷「sin(」 存在且合法後,跳過「sin(」,接着對於中間的部分使用toFactor()方法繼續構造,在toFactor中發現了一個‘(’,所以判斷它爲一個Polyfactor類型的Factor,繼續調用toPolyFactor()方法,toPolyFactor()方法構造完畢(結果爲取出了「(x)」)後返回至最初的toFactor()方法檢查最後的「)」,發現沒有這個「)」,由於已經將「)」匹配給了裏面的「(x)」,進而拋出異常,輸出WF。
3)、這種方法我讀懂以後讓我驚歎大天然的鬼斧神工(還有本身真菜)。我的認爲它的精妙之處在於,他不關注頂層Poly如何構造,只須要知道Poly是一個一個Term構成的;同時也不須要知道Term怎麼構造,由於它是一個一個Factor構成的。全部全部的問題只存在於,如何寫出正確的Factor構造(這相對簡單不少啊)。全部表達式提取問題迎刃而解。