java实现表达式求值_如何编写一个高效的Java表达式求值程序
雖然,這個題目是有一點奪人眼球,但我真實這么做了(關是以否信任基準測試效果,這是其他一個話題)。
所以,上周我一貫在找一個小型、適用的競賽爭辯數學表達式的類庫。有功夫我在stackoverflow上看到了一個帖子,里面舉薦的庫(Expr)真實是很快而且根抵具有我需求的一切特色。但不幸的是,它不支持供應限制變量局限(在虛擬機里面,一切變量都位于一個全局命名空間)。
所以,我做了一件正常人不會做的責任:從新發明輪子,本人編寫一個解析器和實行器。那是一個下雨的周六,我想到了用一個小型遞歸向下的解析器,一個簡化了的、可以競賽爭辯表達式的籠統語法樹。籠統語法樹行使一個小型變量治理助手,看起來也沒什么除夜不了的。但它不是沒有用。我做出一個初步的完成而且實行速度特殊快。在中止了一些測試后,更讓我布滿決心,它實行的一切運算都準確無誤。我想與最初面提到的類庫比照,確認這個競賽爭辯器究竟有多快。在沒有對每一個內部輪回和其他的實行中止優化前,我不報太除夜的希冀,究竟有許多類庫是商業軟件。所以當我看到測試效果的時辰很驚異。上面的清單展現了一個小的基準測試,行使不合的類庫競賽爭辯不合個表達式。
parsii
是我編寫的庫,測試時用的是最終版本。這個版本做了一些簡化,好比事后競賽爭辯了常量表達式。然則沒有行使任何“黑魔法”,好比生成字節碼或其他相反的操作。
在功效評價中,一個用例是實行表達式”2 + (7 –
5) * 3.14159 * x^(12-10) +
sin(-3.141)”。個中X的取值局限為0到1000000。測試時先運轉10次,對JIT中止預熱。然后再運轉15次競賽爭辯平均時辰:
PARSII: 28.3 ms
EXPR: 37.2 ms
MathEval: 7748.5 ms
JEP: 647.0 ms
MESP: 220.8 ms
JFEP: 274.3 ms
現在我敢一定,每一個類庫都有本人的優勢,所以不能直接對它們中止比擬。雖然如斯,使人受驚的是一個龐雜完成的軌范可以具有這么好的顯示。
假定讀者對編譯器的事理不太意見的話,上面是一個關于編譯器運起色制的龐雜引見:
同其他的解析器或編譯器一樣,parsii行使了傳統的分詞器。它將字符流轉化成詞法單元流,所以”4 +
38″,也就是字符數組’4′,
‘ ‘,
‘+’, ‘
‘, ’3′ ,
‘ ‘,
‘‘,
’8′被轉化成:
4 (整數)
+ (符號)
3 (整數)
* (符號)
8 (整數)
分詞器取到一個字符,接著剖斷是一個什么類型的詞法單元,然后再讀入這個屬于詞法單元的一切字符。每一個詞法單元都有類型、文本內容而且曉得肇端位置(行號和字符)。網上有許多深切的教程,所以在這里就不具體講授了。你可以看一下源代碼,但正如我說的,它只是一個初步的完成。
解析器用來將傳入的詞法單元流翻譯成可以實行的AST(籠統語法樹),它是一個傳統的自上而下遞歸解析器。這是完成解析器最龐雜的體式格式,完整手寫,沒無益用對象生成。像這樣的解析器只具有一個包括一切語法劃定禮貌的方法。
一樣,關于這類類型的解析器也有許多的教程,然則若何適合地處置責罰缺陷卻窮困相關的示例。除意見析表達式的速度和準確性外,優秀的缺陷處置責罰機制是一個優秀解析器的最焦點成份之一。正如在源代碼里看到的那樣,完成起來并非太難題。因為解析器在解析表達式的進程傍邊歷來不會拋出異常,
一切的缺陷都被匯集起來,而且連續盡量中止解析。即便在第一個缺陷發生發火往后已不能勝利解析生成AST,主要的是要可以盡量的連續解析。因為在一次的實行中我們需求申報盡量多的缺陷。這樣的方法也一樣用在了分詞器申報上。好比申報造孽花招的詞法單元,例如帶有2個小數點的浮點數,放到一樣的缺陷列表中。
實行一個解析完成后的籠統語法樹異常龐雜。每一個籠統語法樹節點都包括一個競賽爭辯方法,從根節點最先到父節點會挪用這個它。這里的實行效果就是表達式的效果,一個龐雜的例子就是算數運算,包括了+、-、*等操作。
實行一個解析完成后的籠統語法樹異常龐雜。每一個籠統語法樹節點都包括一個競賽爭辯方法,它的父親從根節點最先挪用此方法。算數運算,代表了+、-、*等操作。
為了增加實行時辰,軌范里運用了3種優化裝備:首先,在完成解析AST后,在根節點上中止一個簡化的方法挪用,而且會散布到每一個子節點。每一個節點剖斷本人的子表達式中能否是有簡化的表達方法。例如:對算數運算,我們搜檢2個操作數能否是都是常量(數字)。假定是數字,我們將競賽爭辯表達式而且前往一個包括競賽爭辯效果的常量。對函數,假定一切的參數都是常量的話,也會中止此類優化。
在表達式中行使變量時會實行第二種優化。這里行使map用來在需求的時辰來對變量的值中止讀寫。這一定是有用的,而且會中止許多次的查找。所以我們有一個叫做Variable類,它包括了變量稱號和變量值。在中止表達式解析時,變量在浸染域局限內(僅是一個map)只被查找一次,之后就可以一貫行使。因為每次查找都前往溝通的實例,所以在競賽爭辯表達式值時變量的接見就像讀寫字段一樣廉價,因為我們剛剛獲得了Variable類的值。
第三個也是最初一個優化極可以不是經常起浸染。然則因為易于完成,照樣運用了這類尤華。它的功用根抵上和名字“延遲運算”一樣,次要用于函數挪用。函數不會自動競賽爭辯一切參數值,而且挪用函數。而“延遲運算”會搜檢一切的參數,自行決意哪些參數需求競賽爭辯。在if函數中可以看到它運用的實例。
parsii遵照MIT準許證受權。在GitHub上可以找到一切的源代碼,而且包括了預編譯的jar包。
style="display:inline-block;width:336px;height:280px"
data-ad-client="ca-pub-9611302475373562"
data-ad-slot="8266948139">
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java实现表达式求值_如何编写一个高效的Java表达式求值程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顺丰同城多少钱同城啊?
- 下一篇: java 1.6u29 下载_jdk1.