Expanding math expression (expanding parentheses)

时间:2019-01-15 18:11:44

标签: java .net math abstract-syntax-tree symbolic-math

I have a expression like this a*(b+c) and I have successfully parsed into an AST so it finally becomes:

enter image description here

I'm trying to expand the expression it finally becomes a*b + a*c, but with no luck.

I would like to know an algorithm to expand the expression, or maybe a library to do it, preferably for .NET.

5 个答案:

答案 0 :(得分:1)

如果您使用shunting-yard algorithm来构造AST,则每当在乘法运算符之后将左括号弹出到运算符堆栈上时-这就是分配和扩展表达式的信号。

对于所提供的图像,加法运算符移动到根节点,并且其子节点是当前树的副本,但加法节点被其自己的子节点代替。

希望这有助于创建解决方案。

AST before and after distributive property

答案 1 :(得分:1)

Symja中,您可以使用Distribute()Expand()函数来解决问题:

package org.matheclipse.core.examples;

import org.matheclipse.core.eval.ExprEvaluator;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.parser.client.SyntaxError;
import org.matheclipse.parser.client.math.MathException;

public class ExpandSO54204637 {

    public static void main(String[] args) {
        try {
            ExprEvaluator util = new ExprEvaluator();
            IExpr expr = util.eval("a*(b+c)");

            IExpr result = util.eval(F.Distribute(expr));
            // print: a*b+a*c
            System.out.println(result.toString());

            result = util.eval(F.Expand(expr));
            // print: a*b+a*c
            System.out.println(result.toString());

        } catch (SyntaxError e) {
            // catch Symja parser errors here
            System.out.println(e.getMessage());
        } catch (MathException me) {
            // catch Symja math errors here
            System.out.println(me.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        } catch (final StackOverflowError soe) {
            System.out.println(soe.getMessage());
        } catch (final OutOfMemoryError oome) {
            System.out.println(oome.getMessage());
        }
    }
}

答案 2 :(得分:1)

我发现Math.NET具有执行此操作的功能:

SymbolicExpression.Parse("a*(b+c)").Expand();

答案 3 :(得分:1)

您可以通过编写过程代码来执行此操作,也可以使用源代码来获取转换程序转换系统(PTS)。

编写过程代码仅包含调用支持功能以在树中上下导航,删除节点之间的链接,删除节点,创建节点和链接节点。任何AST库都具有(应该具有!)这样的功能。

因此,“ a *(b + c)”的过程解决方案重写为“ a b + a c”是这样的:

 Find tree root of "a*(b+c)".  That's the "*" node.
 Unlink the "a" child and the "b+c" child.
 Unlink the "b" and "c" children from the "+" node.
 Create a second "*" node.
 Link "a" and "b" nodes under the original "*" node, producing subtree "a*b*
 Copy the "a" node.
 Link the copyied-"a" and "c" under the second "*" node, producing subtree "a*c"
 Link the subtrees under the "+" node.
 Return "+" as the root of the tree.

这没什么难的,只是改组链接。

但是编写起来很烦人,没有帮助您从可能要操作的语言(“ C#”?)中解析表达式,没有轻易进行复杂的转换,也没有帮助您找到这种类型的您可能要修改的更大的AST中的子树。

这就是为什么要使用PTS的原因。一个好的PTS提供了解析机制,可以构造复杂语言(例如Java,COBOL,C ++或C#)的解析器。 它还提供了一种编写模式导向的重写的方法。如果碰巧已经对您要操作的语言进行了认真验证的解析器,则它会得到布朗尼点(因为否则,您还会在编写树操作问题的同时也编写解析器)。

作为示例,使用我们的DMS软件重新设计工具包,您可以利用上述语言的经过充分验证的解析器。假设您要操纵C#, 然后,您可以编写此DMS脚本以在任意大的C#AST上完成示例:

domain CSharp~CSharp7_5; -- establishes which parser to use to read source code

rule distribute_times_over_plus(a:multiplicative_expression,
                                b:additive_expression,
                                c:multiplicative_expression)
   :multiplicative_expression->multiplicative_expression
   = "\a*(\b+\c)"  -> "(\a*\b+\a*\c)";

您可以将此脚本交给DMS,它将解析C#源文件,并将此转换应用于找到模式的所有位置。 (如果要对应用此应用的位置/时间有更多控制,则可以编写一个额外的元编程脚本来定义该脚本,而不是依靠内置的“应用于所有位置”操作)。

应该清楚,这很容易编写;不太清楚,但是最大的好处是DMS对它进行了检查。您不能编写破坏语言语法的规则。 (如果编写过程代码,则可以以任何荒谬的方式链接节点,然后进行调试)。如果您想编写许多规则,这将提供巨大的帮助:您无法犯一整类错误。最后,这些规则比您可能编写的程序代码更具可读性。使其更易于阅读,理解和修改。

有关可以在规则中编写的内容的更多详细信息,请访问DMS Rewrite Rules

如果您想通过定义一种语言(“大学演算”)并将规则应用于该语言(“如何区分公式”)来详细了解此示例,可以在Algebra as a DMS Domain

另一个(巨大的)细节:如果普通AST表示编程语言,则对它们的重写不是很有效,因为您不能忽略标识符的含义和范围。参见Life After Parsing"进行深入讨论。

但是最重要的是,您的重写规则通常需要以要操作的编程语言的语义属性为条件。 DMS规则通过允许附加的 if条件子句来处理该问题,该子句可以为DMS调用为该语言定义的语义谓词。您可以在“代数”示例中看到一些简单的示例。

答案 4 :(得分:1)

这是prolog中的单行程序。 作为奖励,它可以双向工作。 也就是说,您将其设计为“展开”,即可免费获得“展开”。这是一个使用yap prolog的交互式REPL的示例。都是大写字母的标识符是变量。

$ yap
YAP 6.2.2 (x86_64-linux): Sat Sep 17 13:59:03 UTC 2016

?- [user].

/* consulting user_input... */

rewrite(A * (B + C), (A * B + A * C)) .

end_of_file .

/* example usage from the REPL */

?- rewrite(3 * (4 + 5), REWRITTEN) .

REWRITTEN = 3*4+3*5

?- rewrite(a * (b + c), REWRITTEN) .

REWRITTEN = a*b+a*c

/* example usage showing it work the opposite way */

?- rewrite(REWRITABLE,(3*4+3*5)) .

REWRITABLE = 3*(4+5)