計算器 abacus 技術文檔之三----自定義函數

計算器 abacus 是一個小巧卻功能齊備的計算器,支持四則混合運算(包括邏輯運算),支持大量的數學函數,支持變量參與運算,支持自定義函數以擴充功能。目前版本是 2,地址:http://www.oschina.net/code/snippet_736932_13725。本文就自定義函數做一介紹。 函數

用戶能夠將含有參數的表達式定義爲一個新函數,以實現含參表達式的複用,對於一元二次方程求根,能夠定義函數 spa

                SolveEqution1x2p(a, b, c) = (- b + sqrt(b ^ 2 - 4 * a * c)) / (2 * a) .net

那麼沒有參數的表達式就不能定義成函數嗎?照樣能夠,只要你喜歡,假使你不喜歡使用符號常量,你仍然能夠經過定義函數來使用圓周率:Pi() = 3.141593,而後在須要圓周率的地方調用它就好了。進一步,能夠在已定義函數的基礎上定義新的函數,好比你定義了圓的面積函數(下式中pi 是符號常量,圓周率):  code

                AreaCircle(r) = pi * r * r 遞歸

就能夠繼續定義圓環的面積 ip

                AreaRing(r1, r2) = AreaCircle(r1)- AreaCircle(r2) get

如何,很刺激吧?咱們來看一個更有趣的例子,先介紹一下程序內置的 if 條件函數 數學

                   if(x, a, b) io

這個函數有三個參數,當第一個參數 x 不爲零時函數返回第二個參數 a,當第一個參數爲零時返回第三個參數 b,從而實現選擇的功能,實際上因爲程序提供了關係運算符,咱們徹底能夠本身來定義這個函數(下式中雙等號 == 表示相等,單等號是用做賦值符號的) 基礎

                 if(x, a, b) = a + (b - a) * (x == 0)

有了這個函數,咱們甚至能夠寫出這樣的階乘函數

                factorial(n) = if(n == 0, 1, n * factorial(n - 1))

意思是

                factorial(0) = 1, factorial(n) = n * factorial(n - 1)

這個例子是如此的特殊,在函數的定義體中竟然出現了它本身,這叫作遞歸,實現起來並無技術障礙。一樣的方式,咱們能夠這樣計算餘弦

                cos(x) = if(abs(x) < 0.000001, 1, 2 * cos(x / 2) ^ 2 - 1) 

這個計算是近似的,當 x 充分接近 0 的時候,取 1 爲它的餘弦值,不然先計算它的一半的餘弦,再按倍角公式計算它本身的餘弦。這技術的確很誘人,可是這會致使一個問題,就是函數之間的依賴關係,這在刪除函數的時候會致使問題,若是我把圓的面積函數給刪除了,那前面那個圓環的面積函數還可使用嗎?有一種思路,就是在處理用戶自定義函數時採用一個原則:若是在定義函數時使用了已經定義的其餘函數,內部最終必須轉化爲只依賴於內置函數,這樣,這個圓環的面積函數在通過處理後,內部事實上是

                pi * r1 * r1 - pi * r2 * r2

不過這個解決方法也是有缺陷的,首先是不支持遞歸,好比上面的階乘函數將變得不可能,由於會陷入無窮替換,更嚴重的問題是它會致使錯誤信息變得不可理解,好比咱們不喜歡符號常量,咱們本身寫一個函數來計算圓周率:Pi(),並且這個函數是有可能會計算失敗的,而後定義圓的面積爲

                AreaCircle(r) = Pi() * r * r

再定義圓環的面積爲

                AreaRing(r1, r2)= AreaCircle(r1) - AreaCircle(r2)

那麼按照前面所述的原則,在定義完成後計算器內部實際保存的是 Pi() * r1 * r1 - Pi() * r2 * r2,而後咱們調用這個圓環的面積函數,可能會得出一個對函數 Pi() 的調用失敗的提示,咋一看,不對呀,咱們這裏並無調用函數 Pi() 呀,因而就丈二和尚摸不着頭腦了。鑑於這兩個緣由,程序中將不採用上面這種原則,而採用構造依賴關係表來解決這個問題,每一個函數在定義時將會構造一個它所依賴的函數列表(若是有遞歸,則對本身的依賴不會記入此列表),在刪除函數的時候,會先刪除依賴它的函數,包括間接依賴它的函數,而後再刪除它本身。
相關文章
相關標籤/搜索