【问题标题】:Math Parser - Adding logical, comparison, and conditional operators数学解析器 - 添加逻辑、比较和条件运算符
【发布时间】:2014-12-11 03:01:02
【问题描述】:

我找到了一个简单的数学解析器类,但它不支持逻辑运算,也不支持如下表达式:

(a > b) ? (x^2) : (a / 3.56)

这是解析器:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Calcultra
{
    class Parser
    {
        // The available operators for use in mathematical expressions.
        private string[] _operators = { "-", "+", "/", "*", "^" };

        // The mathematical operations performed by the operators.
        private Func<double, double, double>[] _operations = {
            (a1, a2) => a1 - a2,
            (a1, a2) => a1 + a2,
            (a1, a2) => a1 / a2,
            (a1, a2) => a1 * a2,
            (a1, a2) => Math.Pow(a1, a2)
        };

        /**
         * Parses and evaluates a mathematical expression and returns the result.
         */
        public double Eval(string expression)
        {
            List<string> tokens = getTokens(expression);
            Stack<double> operandStack = new Stack<double>();
            Stack<string> operatorStack = new Stack<string>();
            int tokenIndex = 0;

            while (tokenIndex < tokens.Count)
            {
                string token = tokens[tokenIndex];

                if (token == "(")
                {
                    string subExpr = getSubExpression(tokens, ref tokenIndex);
                    operandStack.Push(Eval(subExpr));
                    continue;
                }

                if (token == ")")
                {
                    throw new ArgumentException("Mis-matched parentheses in expression");
                }

                // If this is an operator.
                if (Array.IndexOf(_operators, token) >= 0)
                {
                    while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek()))
                    {
                        string op = operatorStack.Pop();
                        double arg2 = operandStack.Pop();
                        double arg1 = operandStack.Pop();
                        operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
                    }

                    operatorStack.Push(token);
                }
                else
                {
                    operandStack.Push(double.Parse(token));
                }

                tokenIndex += 1;
            }

            while (operatorStack.Count > 0)
            {
                string op = operatorStack.Pop();
                double arg2 = operandStack.Pop();
                double arg1 = operandStack.Pop();
                operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
            }

            return operandStack.Pop();
        }

        /**
         * Why even write a description for this function.
         */
        private string getSubExpression(List<string> tokens, ref int index)
        {
            StringBuilder subExpr = new StringBuilder();
            int parenlevels = 1;
            index += 1;

            while (index < tokens.Count && parenlevels > 0)
            {
                string token = tokens[index];

                if (tokens[index] == "(")
                {
                    parenlevels += 1;
                }

                if (tokens[index] == ")")
                {
                    parenlevels -= 1;
                }

                if (parenlevels > 0)
                {
                    subExpr.Append(token);
                }

                index += 1;
            }

            if ((parenlevels > 0))
            {
                throw new ArgumentException("Mis-matched parentheses in expression");
            }

            return subExpr.ToString();
        }

        /**
         * Tokenizes the given mathematical expression.
         */
        private List<string> getTokens(string expression)
        {
            string operators = "()^*/+-";
            List<string> tokens = new List<string>();
            StringBuilder sb = new StringBuilder();

            foreach (char c in expression.Replace(" ", string.Empty))
            {
                if (operators.IndexOf(c) >= 0)
                {
                    if ((sb.Length > 0))
                    {
                        tokens.Add(sb.ToString());
                        sb.Length = 0;
                    }

                    tokens.Add(c.ToString());
                }
                else
                {
                    sb.Append(c);
                }
            }

            if ((sb.Length > 0))
            {
                tokens.Add(sb.ToString());
            }

            return tokens;
        }
    }
}

在问这个问题的过程中,我尝试了更多的代码来理解代码,我真正掌握的主要部分是:

// If this is an operator.
if (Array.IndexOf(_operators, token) >= 0)
{
    while (operatorStack.Count > 0 && Array.IndexOf(_operators, token) < Array.IndexOf(_operators, operatorStack.Peek()))
    {
        string op = operatorStack.Pop();
        double arg2 = operandStack.Pop();
        double arg1 = operandStack.Pop();
        operandStack.Push(_operations[Array.IndexOf(_operators, op)](arg1, arg2));
    }

    operatorStack.Push(token);
}
else
{
    operandStack.Push(double.Parse(token));
}

我看到它正在检查运算符,并将参数之间的运算推入操作数堆栈。我认为这是我检查条件语句的地方,我需要检查 ?: 符号,获取所有三个子表达式,评估条件表达式,然后选择最后两个子表达式之一进行推送操作数栈。至少我是这么理解的。不过,我不太确定如何处理这些比较。

我知道有一个类似的问题:Adding Conditionals & Functions to a Math Parser

但我使用的是 C#.NET,但我不知道如何将那里所做的事情适应我的代码。我不是要求您为我编写代码(不是说我会抱怨很多 xP),但我确实需要一个起点。我需要采取哪些步骤才能完成这项工作。

【问题讨论】:

  • 您可以使用内置编译器:CodeDOM

标签: c# parsing math conditional-statements conditional-operator


【解决方案1】:

我没有尝试过,但是代码看起来并不太复杂,您应该自己尝试一下。尝试在调试器中单步执行Parse

您必须做的第一件事是将"&lt;""?"":" 等新字符串添加到_operators,然后在getTokens 中查找它们。还将Funcs 添加到执行操作的_operations

我预见到的一个问题是它处理的唯一数据类型是doubles,而像&lt; 这样的关系运算符返回bools。您可以使用旧的 C 约定,其中零为假,非零为真,但请注意,这会使您的解析器非类型安全。

您将面临的另一个障碍是三元运算符? 的参数是表达式,而不是标记,并且您的解析器只查看标记。您可以使用 Pascal 中的快捷方式来完全评估每个表达式。您将不得不修改Eval 以处理? 具有最低优先级但在您的表达式中位于: 之前的事实,并且看起来Eval 是严格从左到右的。 IIRC Pascal 使用了另一个快捷方式,它会在表达式内部添加括号来处理优先级,例如,1&gt;0 ? 1+1 : 1-1 变为 (1&gt;0)?((1+1):(1-1)) 变为1?(2:0) 变为 2

更进一步,您将不得不产生Abstract Syntax Trees,而不是令牌。

【讨论】:

    猜你喜欢
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多