Проблемы построения дерева выражений из постфиксной записи

В настоящее время я пишу импретер для простых математических выражений (константы и простая арифметика).

У меня возникла проблема с построением дерева выражения из выражения в формате постфикса. То, что я сделал, отлично работает в большинстве сценариев, но не в этом примере из Википедии.

Если я оцениваю выражение 3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3, я получаю результат 3,0001220703125, хотя результат должен быть 3,001953125. Причина этого, по-видимому, в том, что дерево выражений выглядит как 3+((4*2)/((1-5)^(2^3))) вместо (3+((4*2)/(((1-5)^2)^3))).

Постфиксная запись исходного выражения выглядит как 3 4 2 * 1 5 − 2 3 ^ ^ / +

Есть предложения, как получить дерево выражений таким, каким я хочу его видеть?
Ниже приведен постфикс кода дерева выражений и некоторые тесты, написанные на C#, но не требующие пояснений.

public MathExpression Parse()
{
    var tokens = this.ToPostFix(_tokens);
    var stack = new Stack<MathExpression>();
    foreach(token in tokens)
    {
        if(token.IsOperand())
        {
            // Push the operand on the stack.
            stack.Push(new ConstantExpression(token.Value));
        }
        else
        {
            Debug.Assert(token.Type == TokenType.Operator, "Expected operator.");
            var op = (Operator)token.Value;
            var right = stack.Pop();
            var left = stack.Pop();
            var expression = new ArithmeticExpression(op, left, right);
            stack.Push(expression);
        }
    }
    Debug.Assert(stack.Count == 1, "More than one expression on stack.");
    return stack.Pop();
}

И немного тестов:

[Test]
public void Wikipedia_Example_Can_Be_Evaluated()
{
    var expected = 3+4*2/(1-5)^2^3; // 3,001953125
    var actual = MathExpression.Parse("3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3")
                               .Evaluate(); // 3,0001220703125
    Assert.AreEqual(expected, actual); // Not equal :(
}

[Test]
public void Can_Convert_To_Prefix()
{
    string expected = "3 4 2 * 1 5 − 2 3 ^ ^ / +"
    string actual = MathExpression.ToPostFix("3+4*2/(1-5)^2^3")
    Assert.AreEqual(expected, actual); // Works as expected
}

person Patrik Svensson    schedule 04.07.2012    source источник
comment
Да, я написал пример как инфикс, чтобы пояснить, как я хочу, чтобы дерево бинарных выражений выглядело. Код показывает, как я строю дерево из постфиксной записи.   -  person Patrik Svensson    schedule 04.07.2012
comment
Тогда кажется, что вы пропустили код, вызвавший ошибку. Можете ли вы добавить постфикс для образца?   -  person Henk Holterman    schedule 04.07.2012
comment
Я добавил постфикс для выражения. То же, что и в примере с Википедии, поэтому я думаю, что могу исключить ошибки.   -  person Patrik Svensson    schedule 04.07.2012
comment
Я удалил этот комментарий, я предполагал слева направо.   -  person Henk Holterman    schedule 04.07.2012
comment
Нет. Просто пытаюсь немного освежить память. Давно я про все это читал...   -  person Patrik Svensson    schedule 04.07.2012


Ответы (1)


Страница WikiPedia содержит это замечание:

^ оценивается справа налево

Я не вижу, чтобы ваш код учитывал это, ^ обрабатывается так же, как и другие операторы, как левоассоциативные.

Это означает, что ваша интерпретация может быть неправильной:

x ^ 2 ^ 3 => x^(2^3) => x 2 3 ^ ^

и ваш код и исходный ответ (30001220703125) верны.

person Henk Holterman    schedule 04.07.2012
comment
Вопрос в том, как я это учитываю. - person Patrik Svensson; 04.07.2012
comment
Я понимаю. Если я объявлю переменную в C# как double value = 3+4*2/(1-5)^2^3 (см. первый модульный тест), я получу 3001953125 вместо 30001220703125. Помню, разные калькуляторы в школе по-разному интерпретировали выражения. Может и в этом дело? - person Patrik Svensson; 05.07.2012
comment
Относительно модульного теста: 3+4*2/(1-5)^2^3 не выполняет возведение в степень. ^ - это исключающее ИЛИ. На самом деле expected будет int. Старайтесь избегать var и введите 2.0 для удвоения констант. - person Henk Holterman; 05.07.2012
comment
Я пробовал с double expected = 3.0D+4.0D*2.0D/(1.0D-5.0D)^2.0D^3.0D, но результат тот же. Или я вас неправильно понял? - person Patrik Svensson; 05.07.2012
comment
^3.0D не должен даже компилироваться. В C# нет оператора Exp/Power, поэтому его будет сложно сравнить с синтаксисом C#. - person Henk Holterman; 05.07.2012
comment
Да, изначально я написал этот код на VB.NET и перенес его на C# для ответа на вопрос (что в ретроспективе могло показаться плохой идеей), так как вероятность того, что кто-то на него ответит, была выше. Но все же интересно, почему VB.NET так оценивает выражение. - person Patrik Svensson; 06.07.2012
comment
Согласно MSDN операторы называются левоассоциативный в Visual Basic. Я понимаю, что это означает всех операторов, включая ^. - person Henk Holterman; 06.07.2012
comment
В порядке. Это действительно объясняет это. Большое спасибо за ваше терпение со мной. - person Patrik Svensson; 06.07.2012