本篇是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

相关文章: