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">

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值