本篇是MathAssist的第三篇,將在上篇所實現的BigNumber基礎上完成具備編譯功能支持無限大數的計算器SuperCalculator。html
要想從形如 "(1.23435+sin(0.5*180/PI))*2468.2345" 字符串格式的表達式中求值,須要使用編譯原理的知識,不過在通常的《數據結構》課程中都會講解基礎的表達式求值問題,而本篇也是在數據結構課程的基礎上稍加拓展而實現。node
表達式的值,通常將其轉化成二叉樹結構,根節點表示操做符,子節點表示操做符所使用到的份量。
好比上面的表達式表示成二叉樹如圖所示:
算法
如圖所示,加乘除的操做數都是兩個,而sin只須要一個操做數,因此其子節點只有一個。而若是要支持不限參數個數的函數,就必須有兩個以上的節點,因此SuperCalculator中所使用的是多叉數。
先看全部節點類型的根類型Node數據結構
下面再看Node及其子類的繼承體系。
ide
直接從Node繼承的類有三個:NumberNode(純數字節點), ExpandNode(可拓展節點), CompartNode(間隔節點)
其中CompartNode,表示括號之類節點,只在詞法分析中用到,不會出如今樹形中函數
ExpandNode的直接子類有三個:FunctionNode(函數節點),ConstantNode(常量節點), OperateNode(操做符節點)spa
其中節點中全部的數字類型都是用BigNumber來表示。3d
以下所示的代碼,將項目中全部從ConstantNode,FunctionNode,OperateNode中繼承而來的子類,先實例化後再存儲到對應的List<T>中,這樣在構建多叉樹時用這些List<T>中的對象的Format進行字符串查找便可判斷對應的類型。code
internal static void Find(List<ConstantNode> constants, List<FunctionNode> functions, List<OperateNode> operates) { Assembly ass = Assembly.GetExecutingAssembly(); Module[] modes = ass.GetModules(); Type[] typs; foreach (Module m in modes) { typs = m.GetTypes(); foreach (Type typ in typs) { if (typ.IsSubclassOf(typeof(ConstantNode))) { constants.Add(ass.CreateInstance(typ.FullName) as ConstantNode); } else if (typ.IsSubclassOf(typeof(FunctionNode))) { functions.Add(ass.CreateInstance(typ.FullName) as FunctionNode); } else if (typ.IsSubclassOf(typeof(OperateNode))) { operates.Add(ass.CreateInstance(typ.FullName) as OperateNode); } } } }
使用反射機制後就很是方便添加新的函數或操做符了。好比如今項目中只實現了Max函數,若是要實現Min函數,只須要添加一個MinNode類,重寫Format屬性返回"min",重寫Value屬性求出List<Node>中的最小值便可。將這個類實現後,反射機制將自動添加min函數了。orm
Exression 用於接受一個字符串類型的表達式,並計算出值。其結構如圖所示:
從圖中能夠看出詞法分析在Lexcial類中,語法分析在Syntax.cs類中。
其中Lexical.Analyse()函數負責將string value格式的字符串表達式轉化成List<Node>格式的表示式。而後Syntax.Analyse(List<Node> nodes)負責將中序表達式轉化成多叉樹,最後只返回一個根節點Node
詞法分析的過程,就是字符串的各類比較。
具體細節見代碼吧
從中序從構建多叉樹的經典算法「數據結構」中都應該有講到,即先將中序轉化爲後序或前序(本文轉化成後序),而後再將後序轉化成多叉樹。
Syntax.cs中有四個靜態函數:
下面詳細介紹 CreateSyntaxTree(List<Node> nodes, int first, int end)——
這個函數中遍歷nodes,對每一個節點先判斷
上面這個過程也是數據結構中的經典算法。
獲得正確的樹型後,就只須要簡單地調用Head.Value便可引爆多叉樹的求值過程(四年前本人是使用屬性,其實如今看來用函數更合理一些)。
最後提供程序的exe和所有源碼。
SuperCalculator_exe http://files.cnblogs.com/files/xiangism/SuperCalculator_exe.rar
SuperCalculator http://files.cnblogs.com/files/xiangism/SuperCalculator.rar
exe中在控制檯直接輸入想要計算的表達式,而後回車便可看到結果。
補充:sin,pow之類的函數由於精度的須要,因此在正常函數的基礎上添加了一個表示精度的可選參數。
sin(PI) = 0.0000000000000032 sin(PI, 30) = 0.0000000000000032384627081457275276040217744770360060341499422643 44376687740626754440803176096879519813709774691978013205 pow(2.1, 2.1) = 4.74963807 pow(2.1, 2.1, 30) = 4.7496380917422417156885305942099853613159436030728012667686 29382182863206610681766137388241594625579055187057232218446673