計算器 abacus 技術文檔之二----初步設計

======================================= 數據結構

計算器 abacus 的下載地址:http://www.oschina.net/code/snippet_736932_13725 dom

      若是你有關於 abacus 的問題或者建議,請發郵件至 zhoucosin@163.com。謝謝。 函數

=======================================   ui

本節介紹一些問題以及如何設計計算器以解決這些問題。 .net

程序的目標: 命令行

  • 支持四則混合運算 ok.
  • 支持數學函數,如三角函數、指對函數、組合數等 ok.
  • 支持符號常量,如圓周率、天然對數的底數等  ok.
  • 支持變量運算(並不是符號計算) doing...
  • 支持表達式函數(即含有變量的表達式做爲函數) doing...
  • 支持有控制流程的函數 wait for doing.
     首先肯定程序的使用方式,目前只打算以命令行的方式運行程序,暫不考慮界面的問題,程序啓動後,用戶逐條輸入表達式以計算其值,每計算完一個表達式並顯示以後,程序將等待用戶輸入下一個表達式,直到用戶輸入"quit" 退出程序。

      表達式在本質上就是一個由運算符、運算數、標點符號這些表達式元素組成的序列,因此問題的關鍵在於解釋這些序列的數學意義。 設計

      首先須要從字符串形式的表達式中提取各個表達式元素(運算符、運算數、標點符號:主要是括號和逗號),並將這些表達式元素依次保存到一個線性的數據結構中去,這稱爲詞法分析。在詞法分析以前,應該進行一些預處理,好比檢查括號是否匹配。而在詞法分析以後也應有一些後處理,好比對標識符做進一步處理,詞法分析器只能識別出標識符,它並不知道一個標識符究竟是一個函數的名字仍是一個符號常量,須要作相應的更換。 3d

      通過詞法分析以後,咱們就獲得了一個由表達式元素所組成的序列了,而表達式元素都是有相應的數學意義的,好比運算符有優先級,須要的運算數個數,以及最重要的計算函數指針這些屬性,而運算數最主要的屬性就是它所對應的數值了。好比表達式 1-2*(sin(pi/3))^2 在通過詞法分析之後將被分割成以下的表達式元素序列: 指針

                                            1  -  2  *  (  sin  (  pi  /  3  )  )  ^  2 code

     其中的各個部件都已經有了數學意義(這須要相應的表達式部件類的實現),好比運算符 * 具備屬性: 字符串體:「*」,優先級:2, 所需運算數個數:2, 計算函數指針:(一個完成乘法運算的函數)。而最後的運算數2具備屬性:字符串體:「2」,數值:2.

      這裏有一個很是重要的設計,就是把數學函數當成運算符處理。這樣作的理由是,函數運算符跟普通的四則混合運算符都能對給定的若干個實數,計算出一個結果來,這說明它們在本質上是一致的,你能夠把加法 2+3 寫成函數調用的形式 +(2,3),也能夠把函數調用 pow(2,3) 按照二元運算符的慣例放在兩個運算數之間: 2 pow 3,想必人們對於 2^3 這樣的指數寫法習覺得常,那麼這個 2 pow 3 不也是一脈相承的麼?而對於一元函數也是同樣,一元運算符負號放在運算符以前,那麼 sin(3) 也能夠寫成 sin 3 的形式。總之,把函數運算符跟普通運算符統一處理,這是 abacus 在設計上的一大特點。

      接下來就要調整表達式的結構,傳統的數學表達式實際上是很不規範的,歷史上加減乘除這些二元運算符最先發明,很天然的就把它們放在兩個運算符之間了,但一樣的形式對於非二元運算符就不適用了。又如,做爲一元運算符的負號是放在運算數以前的,而階乘符號倒是放在運算數以後的,而函數運算符卻採用了另一種寫法: opt(a1,a2,.....)。因而可知傳統的數學表達式雖然對人來講是易於理解的,但卻不利於計算機進行處理,在此咱們須要用一種統一的觀點來看待全部的運算符,即運算符的本質是一個映射,能對給定的若個數(輸入)產生一個肯定的結果(輸出),有了這一點認識,咱們就但願用一種統一的形式來刻畫表達式。

      咱們採用「運算符前置」的形式來規範數學表達式的結構,它把任何一個表達式放在一對小括號內部,而小括號內的第一個對象就是運算符,剩餘的對象均是參與運算的運算數,例如 2+3 的規範形式爲 (+ 2 3),而 sin(pi) 的規範式爲 (sin pi),而且表達式能夠任意嵌套,好比 5*(2-3) 的規範形式爲 (* 5 (- 2 3)),做爲一個更復雜的例子,一元二次方程x^2-3x+2=0的求根公式(正根)將表示爲 (/ (+ (- (-3)) (sqrt (- ((^ (- 3) 2)) (* (* 4 1) 2)))) (* 2 1)) 。雖然這樣的表達式對於人來說是一種痛苦,但因爲其格式的規範性,計算機是樂於處理這樣的結構的。

      由傳統形式向規範式轉換的過程咱們就稱之爲語法分析,若是有語法錯誤,在這個過程當中將會被發現。在語法分析以前也應該會有一些預處理,例如在詞法分析階段並不能區分負號與減號,這須要在語法分析以前進行一點「有語法傾向」的檢查糾正,而後根據括號的層次提高運算符的優先級。

     表達式在通過語法分析以後,就已經被轉換爲規範式了,這個時候進行計算就至關簡單了,只要採用遞歸的方式,在表達式中查找主運算符(即優先級最低的運算符),而後檢查剩餘的各個運算對象,若是某個運算對象自己也是一個表達式,即含有子表達式,則先計算子表達式的值,最後計算整個表達式的值。 1-2*(sin(pi/3))^2 的計算過程以下:

轉換爲規範式:  (- 1 (* 2 (^ (sin (/ pi 3)) 2)))

計算過程:

      Step 1:     (- 1 (* 2 (^ (sin (/ pi 3)) 2)))

      Step2:     (- 1 (* 2 (^ (sin 1.047197551) 2)))

      Step3:     (- 1 (* 2 (^ 0.8660256282 2)))

      Step4:     (- 1 (* 2 0.7500010327))

      Step5:     (- 1 1.500002065)

      Step6:     -0.500002065

這樣就完成了計算過程。在此處能夠看到這個「規範式」帶來的一個最大的好處:把語法分析跟遞歸計算分離開來了,若是不轉換成這種規範式而使用傳統的數學表達式,那麼因爲表達式的各類形式(分一元普通運算符,二元普通運算符,函數運算符.....),就只能在語法分析的過程當中根據相應的運算符選擇合適的形式來進行計算了。而如今引入了這種規範式,在計算過程當中根本不用去關心這個運算符是什麼類型的運算符。這說明,運算符前置表達式的引進,是 abacus 的另外一個很是好的設計。

      至此大致設計基本上就完成了。這裏有一個用腦圖描述的計算器 abacus 的實現,一看就懂的:

http://www.mindomo.com/mindmap/abacus-5b6804e0f3fa466f96bc198c9b5d63d3

zhcosin

2012-10-29

相關文章
相關標籤/搜索