公式翻译Ⅱ

那是2006年12月份的一个非常寒冷的夜晚,我写下了《公式翻译》这篇文章,并贴在自己在csdn.net的技术博客上,在当时,那文章对我而言是非常有成就感的,曾经以前自己遇到的一个难题,就这样被自己用一些“无聊”的时间给攻克了。

光阴飞逝,N年过去,我没想到当初写的这么篇文章居然要在最近的一个项目里派上用场了,这种感觉就像达文西对卖猪肉阿柒说:“国家有任务要交给你了!”于是我花时间整理了一下。功能嘛,和以前差不多,就是根据一个公式字符串和提供的参数,计算出其数值。而这次不是用C++,而是更简单的C#,跟以前用C++写的相比,占用内存空间会多一些,但运算速度应该会有些提升,因为这次我把翻译和计算分开了,不用每次计算的时候都得先翻译,另外还可以使用不定参数的函数,提供了较为友好的出错提示,代码嘛,也非常OOP。原理图如下:

 

 从图中可以看出,一条表达式(#n表示第n个参数,n从0开始),可以拆分成一棵树,树的叶子都是能够直接提供值的标量或变量,而树的其它节点,则表示一种运算(四则运算或函数)。计算的过程是非常典型的后序遍历过程,先准备参数,后执行运算,图中红色的数字标明了它们计算的次序。执行效果:

谁能用计算器验算下?

当你拿到一条公式之后,如何让程序一步步去执行翻译呢?规则很简单:先拆加减,再拆乘除,(跳过括号)最后拆括号(函数也算作括号),拆到最后应该只剩下变量和标量,就可以计算了。

如上图的例子,“#0*pow(2,sin(#1)+1)/(30+6/#2)”,先拆加减,有没有加减?——没有,那么乘除呢?有啊,于是拆成了“#0”、“pow(2,sin(#1)+1)”和“(30+6/#2)”三项,“#0”是第一项,标记为“first”,第二项“pow(2,sin(#1)+1)”和前一项的关系是“multiply”,第三项和前一项关系是“divide”。OK,项拆分好之后再次把各个项当作是单独的表达式,执行前面提到的那个规则,如“#0”这项,先拆加减,没有,再拆乘除,也没有,括号和函数?没有too,最后发现它是个变量,标记它的index为0,结束,别的项也是一样的动作,依此类推。

C#和C++相比,多了一个非常非常逆天的功能——反射!这也是我认为的C#中最cool的功能,有了这个功能,我就能在不改变现有公式翻译代码的基础上,轻易地添加新的函数,因为我可以根据函数名称字符串,找到要调用的函数,生成其delegate,这次的公式翻译Ⅱ就是这么干的。

另外,这次还加入了比较友好的出错提示,告诉用户哪里错了,是括号缺失还是表达式本身就不对。

完整代码(Visual Studio 2010)

原文地址:https://www.cnblogs.com/guogangj/p/3397377.html