具有编译功能支持无限大数计算器的实现

本篇是MathAssist的第三篇,将在上篇所实现的BigNumber基础上完成具有编译功能支持无限大数的计算器SuperCalculator。

要想从形如 "(1.23435+sin(0.5*180/PI))*2468.2345" 字符串格式的表达式中求值,需要使用编译原理的知识,不过在一般的《数据结构》课程中都会讲解基础的表达式求值问题,而本篇也是在数据结构课程的基础上稍加拓展而实现。

多叉树的节点类型

node继承体系

表达式的值,一般将其转化成二叉树结构,根节点表示操作符,子节点表示操作符所使用到的分量。
比如上面的表达式表示成二叉树如图所示:

如图所示,加乘除的操作数都是两个,而sin只需要一个操作数,所以其子节点只有一个。而如果要支持不限参数个数的函数,就必须有两个以上的节点,所以SuperCalculator中所使用的是多叉数。
先看所有节点类型的根类型Node

  • Format表示此节点的字符串表示,如果是sin节点此值即为"sin",乘法节点此值即为"*"
  • Index 表示此节点的首字符在字符串表达式中的索引,主要是用在错误定位上。
  • MinParameterCount 因为现在操作符子节点的个数不定,所以要定义一个最小所需的操作个数,如果小于这个数后就是不合法的表达式
  • Nexts 此节点所有的子节点。这个类型是List<Node>就是用于存储不定个数的操作数。
  • Priority 表示节点在优先级,主要用在构建多叉树时,优先级越高的节点这个值越大。比如数字节点是6,函数节点是5,乘除是4,加减是2
  • Value 计算此节点时最后的值。

下面再看Node及其子类的继承体系。

直接从Node继承的类有三个:NumberNode(纯数字节点), ExpandNode(可拓展节点), CompartNode(间隔节点)
其中CompartNode,表示括号之类节点,只在词法分析中用到,不会出现在树形中

ExpandNode的直接子类有三个:FunctionNode(函数节点),ConstantNode(常量节点), OperateNode(操作符节点)

  • FunctionNode 所有的函数节点都以此类为父节点,比如sin, max, exp等
  • ConstantNode PI, e等常量节点的父节点
  • OperateNode 加减乘除等节点的父节点

其中节点中所有的数字类型都是用BigNumber来表示。

用反射机制来查找所有可用的拓展节点

如下所示的代码,将项目中所有从ConstantNode,FunctionNode,OperateNode中继承而来的子类,先实例化后再存储到对应的List<T>中,这样在构建多叉树时用这些List<T>中的对象的Format进行字符串查找即可判断对应的类型。

        internal static void Find(List<ConstantNode> constants, List<FunctionNode> functions, List<OperateNode> operates) {
            Assembly ass = Assembly.GetExecutingAssembly();
            Module[] modes = ass.GetModules();
            Type[] typs;

            foreach (Module m in modes) {
                typs = m.GetTypes();

                foreach (Type typ in typs) {

                    if (typ.IsSubclassOf(typeof(ConstantNode))) {
                        constants.Add(ass.CreateInstance(typ.FullName) as ConstantNode);
                    } else if (typ.IsSubclassOf(typeof(FunctionNode))) {
                        functions.Add(ass.CreateInstance(typ.FullName) as FunctionNode);
                    } else if (typ.IsSubclassOf(typeof(OperateNode))) {
                        operates.Add(ass.CreateInstance(typ.FullName) as OperateNode);
                    }
                }
            }
        }

ReflectWord.FInd

使用反射机制后就非常方便添加新的函数或操作符了。比如现在项目中只实现了Max函数,如果要实现Min函数,只需要添加一个MinNode类,重写Format属性返回"min",重写Value属性求出List<Node>中的最小值即可。将这个类实现后,反射机制将自动添加min函数了。

语法分析、构建多叉树

Expression表达式类

Exression 用于接受一个字符串类型的表达式,并计算出值。其结构如图所示:


从图中可以看出词法分析在Lexcial类中,语法分析在Syntax.cs类中。
其中Lexical.Analyse()函数负责将string value格式的字符串表达式转化成List<Node>格式的表示式。然后Syntax.Analyse(List<Node> nodes)负责将中序表达式转化成多叉树,最后只返回一个根节点Node

词法分析

词法分析的过程,就是字符串的各种比较。

  1. 先过滤过空格回车,
  2. 是否为字母,那么就有可能是常量节点或者函数节点,就在之前用反射获取的节点List<Node>中查找是否有对应的字符串
  3. 是否为数字
  4. 是否为左右括号
  5. 是否为"," ,函数中的多个参数是用逗号进行分隔。
  6. 是否为操作符节点
  7. ...

具体细节见代码吧

语法分析

从中序从构建多叉树的经典算法“数据结构”中都应该有讲到,即先将中序转化为后序或前序(本文转化成后序),然后再将后序转化成多叉树。
Syntax.cs中有四个静态函数:

  • Node Analyse(List<Node> nodes); 对外接口,将中序的参数转化成多叉树,返回根节点。
  • Node CreateSyntaxTree(List<Node> nodes, int first, int end); //nodes全部的中序结构,[first,end]表示要将中序结构中的哪一部分转化成树形。
  • Node CreateSyntaxTree(List<Node> after)  // 这个方法的参数已经是后序形式,没有括号,函数的参数也合并到函数的子节点后,所以这里就是“数据结构”课程中最经典的,使用一个栈将后序转化成树。
  • List<Node> FindParameters(List<Node> nodes, int start, ref int end) 在函数的括号中,找到参数,这些参数用逗号分开,返回所以的参数。start指向左括号的位置,end返回函数的右括号。这个函数中可能会递归调用到CreateSyntaxTree(),因为参数也有可能是复杂的表示式。

下面详细介绍 CreateSyntaxTree(List<Node> nodes, int first, int end)——

这个函数中遍历nodes,对每个节点先判断

  • 是否为数字节点或常量节点,如果是则添加到后序列表中。
  • 如果为左括号,则把左括号添加到栈
  • 如果为右括号,则一直弹出栈中元素,直到遇到栈中的左括号
  • 如果是函数节点,则使用下面的FindParameters函数将所有参数添加到这个节点的子节点中后,再将这个节点添加到后序
  • 如果是操作符节点,先看栈中有先元素,如果没有元素直接把操作符放到栈中,如果有元素,则比较栈最后一个节点和此节点的优先级,如果栈中优先级高则弹出栈节点放到后序,并把此节点放入栈,否则将此节点放入栈。

上面这个过程也是数据结构中的经典算法。

得到正确的树型后,就只需要简单地调用Head.Value即可引爆多叉树的求值过程(四年前本人是使用属性,其实现在看来用函数更合理一些)。

结束语

最后提供程序的exe和全部源码。

SuperCalculator_exe http://files.cnblogs.com/files/xiangism/SuperCalculator_exe.rar

SuperCalculator http://files.cnblogs.com/files/xiangism/SuperCalculator.rar

exe中在控制台直接输入想要计算的表达式,然后回车即可看到结果。

补充:sin,pow之类的函数因为精度的需要,所以在正常函数的基础上添加了一个表示精度的可选参数。

sin(PI) = 0.0000000000000032
sin(PI, 30) = 0.0000000000000032384627081457275276040217744770360060341499422643 44376687740626754440803176096879519813709774691978013205

pow(2.1, 2.1) = 4.74963807
pow(2.1, 2.1, 30) = 4.7496380917422417156885305942099853613159436030728012667686 29382182863206610681766137388241594625579055187057232218446673

时间: 2024-10-06 20:52:43

具有编译功能支持无限大数计算器的实现的相关文章

Android KitKat 4.4平台开发-添加USB ADB和MTP功能支持

ADB和MTP是Android基于USB实现的两个重要功能,极大地方便了用户在PC与Android设备之间的互操作,比如传输文件.安装应用.开发调试应用. 本文讲述如何在特定软硬件平台下支持Android ADB和MTP功能. Android版本: KitKat 4.4.2 Linux内核版本: 3.10 (Vendor Kernel) 硬件平台: Atmel SAMA5D3 SoC 针对Linux内核的更改 Merge Android Linux内核USB Gadget驱动到处理器厂商Linu

PHPWAMP内置IIS管理器一键搭建PHP网站,支持无限个不同PHP版本同时运行

PHPWAMP内置了功能强大的IIS站点管理,可同时运行多个PHP版本,站点管理可自定义添加PHP版本 使用方式:点击相关设置,直接打开IIS站点管理即可使用,如果你电脑没安装IIS,会自动快速安装 (点击图片可查看高清大图) IIS站点管理一打开就可以使用,按照你的需求进行站点添加,也分为域名模式和端口模式 区别:域名模式不需要将默认主界面设置成80端口(因为主界面是nginx和apache的端口与IIS无关) 下面我们来演示IIS站点管理的域名模式以及端口模式 IIS站点管理"端口模式&qu

数据结构设计——大数计算器

  1 /*  2 大数计算器  3   4 实验目的:数据结构主要是研究计算机存储,组织数据,非数值计算程序设计问题中所 出现的计算机操作对象以及它们之间的关系和操作的学科.数据结构是介于数学.计算机软件和计算机硬件之间的一门计算机专业的核心课程,它是计算机程序设计.数据库.操作系统.编译原理及人工智能等的重要基础,广泛的应用于信息学.系统工程等各种领域.学习数据结构是为了将实际问题中涉及的对象在计算机中表示出来并对它们进行处理.通过课程设计可以提高学生的思维能力,促进学生的综合应用能力和专业

MySQL的预编译功能

MySQL的预编译功能 预编译的好处 大家平时都使用过JDBC中的PreparedStatement接口,它有预编译功能.什么是预编译功能呢?它有什么好处呢? 当客户发送一条SQL语句给服务器后,服务器总是需要校验SQL语句的语法格式是否正确,然后把SQL语句编译成可执行的函数,最后才是执行SQL语句.其中校验语法,和编译所花的时间可能比执行SQL语句花的时间还要多. 如果我们需要执行多次insert语句,但只是每次插入的值不同,MySQL服务器也是需要每次都去校验SQL语句的语法格式,以及编译

自定义 ViewGroup 支持无限循环翻页之三(响应回调事件)

大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢 转载请标明出处,再次感谢 ####################################################################### 自定义 ViewGroup 支持无限循环翻页系列 自定义 ViewGroup 支持无限循环翻页之一(重写 onLayout以及 dispatchDraw) 自定义 ViewGroup 支持无限循环翻页之二(处理触摸事件)

微信推广功能支持图片广告和投放外链广告

微信推广功能公测一段时间了,之前只支持图文消息底部文字广告,更直观的图片广告的推出是众望所归,这不,微信公众平台就发布“推广功能新增图片广告,并支持投放外链(公测)”的公告了 1. 推广功能新增图片广告规格,现在已有图片和文字链两种广告规格 2. 开通了微信支付的公众号,成为广告主后,可投放外链广告 微信推广功能一直处于内测阶段,是在大量的A/B测试,毕竟6亿多用户众口难调,必须多试验一些,提出一个折中方案,相信会越来越完善,传统的广告平台现在应该感到压力山大了吧? 微信推广功能支持图片广告和投

编译Nginx支持Tcp_warppers

Tcp wrappers : Transmission Control Protocol (TCP) Wrappers 为由 inetd 生成的服务提供了增强的安全性.TCP Wrappers 是一种对使用 /etc/inetd.sec 的替换方法.TCP Wrappers 提供防止主机名和主机地址欺骗的保护.欺骗是一种伪装成有效用户或主机以获得对系统进行未经授权的访问的方法. 1.重新编译Nginx [[email protected] nginx-1.6.1]# tar zxf ../ngx

WebAPI增加Area以支持无限层级同名Controller

原文:WebAPI增加Area以支持无限层级同名Controller 微软的WebAPI默认实现逻辑 默认实现中不支持同名Controller,否则在访问时会报HttpError,在网上找到了各种路由自实现,如 给ASP.net Web API的Controller分类 搭建MVC及WebAPI项目框架时碰到的问题集合 在上述地址的帮助下,根据需求,重新编写了AreaHttpControllerSelector,路由原理与上述地址大同小异,均是通过路由匹配拼接FullName,然后匹配最接近的A

xcode 编译打包 支持64位 设置

首先说两个时间 1.自2015年2月份开始,新上传到iTunes上面审核的app,必须支持64位,新上传是指第一次上传, 或者没有审核通过过,总之就是在AppStore上面没有上架的app,必须支持64位,包括工程里面的代码和用到的静态库文件 2.如果之前iTunes上面有审核通过的app,现在只是上传升级版本,更新版本去审核,这时也可以提交成功,iTunes上面验证二进制文件可以通过, 不必非得支持64位,但是不要高兴太早,这只是权宜之计,到2015年6月份上传的升级版本还是要支持64位,只是