【问题标题】:How to build a parse tree of a mathematical expression?如何构建数学表达式的解析树?
【发布时间】:2014-07-01 23:45:38
【问题描述】:

我正在学习如何编写标记器、解析器,作为练习,我正在用 JavaScript 编写计算器。

我正在使用 prase tree 方法(我希望这个术语是正确的)来构建我的计算器。我正在根据运算符优先级构建一个令牌树。

例如,给定一个表达式a*b+c*(d*(g-f)),正确的树应该是:

         +
        / \
       *   \
      / \   \
     a   b   \
              *
             / \
            c   *
               / \
              d   -
                 / \
                g   f

一旦我有了这棵树,我就可以向下遍历它并递归地在左右节点上的每个根节点上应用操作以找到表达式的值。

然而,最大的问题实际上是构建这棵树。我只是不知道如何正确地做到这一点。由于优先级,我不能只拆分运算符+-/* 并从左右部分创建树。

到目前为止,我所做的是对表达式进行标记。所以给定a*b+c*(d*(g-f)),我最终得到了一个令牌数组:

[a, Operator*, b, Operator+, c, Operator*, OpenParen, d, Operator*, OpenParen, g, Operator-, f, CloseParen, CloseParen]

但是我不知道下一步如何从这个令牌数组转到我可以遍历并找出值的树。任何人都可以帮我解决如何做到这一点的想法吗?

【问题讨论】:

  • 看看调车场算法
  • 或解析器生成器。
  • @bmargulies 你能解释一下要寻找什么吗?我不明白什么是解析器生成器……关于这个术语有很多不同的页面。
  • 查找 yacc 或 antlr。这些是使用语言语法并生成代码的工具;您填写代码以创建各个节点。

标签: parsing tokenize evaluation


【解决方案1】:

对的朋友,我不知道如何让这个看起来漂亮

我用 C 编写了一个类似的程序,但我的树是颠倒的,这意味着新的运算符成为根。

C code, but read the readme 中的计算器解析树

例如:输入 2 + 3 - 4

从空节点开始

{}

规则1:读取数字时,在当前节点的左边或右边追加一个子节点,以空者为准

      {}
     /
   {2}

规则2:那么你读取一个操作符,你必须从当前节点{2}开始爬到一个空节点,当你找到一个时,将其值更改为+,如果没有空节点,你必须创建然后将其作为树的根

      {+}
     /
   {2}

你遇到另一个数字,转到规则 1,我们目前在 {+} 找到一个空的边(这次是对的)

      {+}
     /   \
   {2}   {3}

现在我们有了新的操作数 '-' 但因为 {3} 的父节点已满,您必须创建新节点并使其成为所有内容的根节点

        {-}
        /
      {+}
     /   \
   {2}   {3}

哦,看那个,另一个数字,因为我们当前指向根,让我们找到一个 {-} 的子节点,它是空的,(提示右侧)

         {-}
        /   \
      {+}   {4}
     /   \
   {2}   {3}

一棵树建成后,看看函数 getresult() 来计算一切

您可能想知道括号是如何工作的。我是这样做的:

每次遇到“(”时,我都会创建全新的树,并将其余输入构建到该树中。如果我读取另一个“(”,我会创建另一个并继续使用新树构建。

一旦读取输入,我将所有树的根连接在一起,以形成最后一棵树。查看代码和自述文件,我必须绘制来解释一切。

希望这对未来的读者也有帮助

【讨论】:

【解决方案2】:

我经常看到这个问题,所以我就把它写下来了。这绝对应该将您推向正确的方向,但要小心,这是一个自上而下的解析器,因此它可能不会以您期望的优先级解释表达式。

class Program
{
    static void Main()
    {
        Console.WriteLine(SimpleExpressionParser.Parse("(10+30*2)/20").ToString());
        Console.ReadLine();
        //
        // ouput: ((10+30)*2)/20
    }

    public static class SimpleExpressionParser
    {
        public static SimpleExpression Parse(string str)
        {
            if (str == null)
            {
                throw new ArgumentNullException("str");
            }

            int index = 0;

            return InternalParse(str, ref index, 0);
        }

        private static SimpleExpression InternalParse(string str, ref int index, int level)
        {
            State state = State.ExpectLeft;
            SimpleExpression _expression = new SimpleExpression();

            int startIndex = index;
            int length = str.Length;

            while (index < length)
            {
                char chr = str[index];

                if (chr == ')' && level != 0)
                {
                    break;
                }

                switch (state)
                {
                    case State.ExpectLeft:
                    case State.ExpectRight:
                        {
                            SimpleExpression expression = null;

                            if (Char.IsDigit(chr))
                            {
                                int findRep = FindRep(Char.IsDigit, str, index + 1, length - 1);

                                expression = new SimpleExpression(int.Parse(str.Substring(index, findRep + 1)));
                                index += findRep;
                            }
                            else if (chr == '(')
                            {
                                index++;
                                expression = InternalParse(str, ref index, level + 1);
                            }

                            if (expression == null)
                            {
                                throw new Exception(String.Format("Expression expected at index {0}", index));
                            }

                            if (state == State.ExpectLeft)
                            {
                                _expression.Left = expression;
                                state = State.ExpectOperator;
                            }
                            else
                            {
                                _expression.Right = expression;
                                state = State.ExpectFarOperator;
                            }
                        }
                        break;

                    case State.ExpectOperator:
                    case State.ExpectFarOperator:
                        {
                            SimpleExpressionOperator op;

                            switch (chr)
                            {
                                case '+': op = SimpleExpressionOperator.Add; break;
                                case '-': op = SimpleExpressionOperator.Subtract; break;
                                case '*': op = SimpleExpressionOperator.Multiply; break;
                                case '/': op = SimpleExpressionOperator.Divide; break;

                                default:
                                    throw new Exception(String.Format("Invalid operator encountered at index {0}", index));
                            }

                            if (state == State.ExpectOperator)
                            {
                                _expression.Operator = op;
                                state = State.ExpectRight;
                            }
                            else
                            {
                                index++;

                                return new SimpleExpression(op, _expression, InternalParse(str, ref index, level));
                            }
                        }
                        break;
                }

                index++;
            }

            if (state == State.ExpectLeft || state == State.ExpectRight)
            {
                throw new Exception("Could not complete expression");
            }

            return _expression;
        }

        private static int FindRep(Func<char, bool> validator, string str, int beginPos, int endPos)
        {
            int pos;

            for (pos = beginPos; pos <= endPos; pos++)
            {
                if (!validator.Invoke(str[pos]))
                {
                    break;
                }
            }

            return pos - beginPos;
        }

        private enum State
        {
            ExpectLeft,
            ExpectRight,
            ExpectOperator,
            ExpectFarOperator
        }
    }

    public enum SimpleExpressionOperator
    {
        Add,
        Subtract,
        Multiply,
        Divide
    }

    public class SimpleExpression
    {
        private static Dictionary<SimpleExpressionOperator, char> opEquivs = new Dictionary<SimpleExpressionOperator, char>()
        {
            { SimpleExpressionOperator.Add, '+' },
            { SimpleExpressionOperator.Subtract, '-' },
            { SimpleExpressionOperator.Multiply, '*' },
            { SimpleExpressionOperator.Divide, '/' }
        };

        public SimpleExpression() { }

        public SimpleExpression(int literal)
        {
            Literal = literal;
            IsLiteral = true;
        }

        public SimpleExpression(SimpleExpressionOperator op, SimpleExpression left, SimpleExpression right)
        {
            Operator = op;
            Left = left;
            Right = right;
        }

        public bool IsLiteral
        {
            get;
            set;
        }

        public int Literal
        {
            get;
            set;
        }

        public SimpleExpressionOperator Operator
        {
            get;
            set;
        }

        public SimpleExpression Left
        {
            get;
            set;
        }

        public SimpleExpression Right
        {
            get;
            set;
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            AppendExpression(sb, this, 0);

            return sb.ToString();
        }

        private static void AppendExpression(StringBuilder sb, SimpleExpression expression, int level)
        {
            bool enclose = (level != 0 && !expression.IsLiteral && expression.Right != null);

            if (enclose)
            {
                sb.Append('(');
            }

            if (expression.IsLiteral)
            {
                sb.Append(expression.Literal);
            }
            else
            {
                if (expression.Left == null)
                {
                    throw new Exception("Invalid expression encountered");
                }

                AppendExpression(sb, expression.Left, level + 1);

                if (expression.Right != null)
                {
                    sb.Append(opEquivs[expression.Operator]);
                    AppendExpression(sb, expression.Right, level + 1);
                }
            }

            if (enclose)
            {
                sb.Append(')');
            }
        }
    }
}

【讨论】:

  • 问题是关于 javascript,而不是 c#
【解决方案3】:

https://github.com/carlos-chaguendo/arboles-binarios 该算法将代数表达式转换为二叉树。将表达式转换为后缀,然后计算并绘制树


测试:2+3*4+2^3

输出:

    22 =
        └── +
            ├── +
            │   ├── 2
            │   └── *
            │       ├── 3
            │       └── 4
            └── ^
                ├── 2
                └── 3

【讨论】:

    【解决方案4】:

    您可能对 math.js 感兴趣,这是一个包含高级表达式解析器的 JavaScript 数学库(请参阅 docsexamples。您可以简单地解析如下表达式:

    var node = math.parse('a*b+c*(d*(g-f))');
    

    它返回一个节点树(可以编译和评估)。

    你可以在这里学习解析器代码:https://github.com/josdejong/mathjs/blob/master/src/expression/parse.js

    您可以在此处找到一些更简单的表达式解析器示例(用于 C++ 和 Java):http://www.speqmath.com/tutorials/,这对于研究表达式解析器的基础知识很有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多