Не знаю, как надо решать такую вот задачу.
Есть дерево, которое представляет из себя <Expression>. <Expression> состоит из набора <Value> и <Expression> в произвольном порядке (но должно быть как минимум два дочерних элемента). <Value> — примитивное значение. <Expression> имеет признак {Operation}. Результатом применения {Operation} над <Expression> является, по сути, <Value>. Требуется вычислить <Value> для некоего <Expression>.
Пример
[+ // Начало <Expression> с {Operation} '+'
1, // Вот это и есть <Value>
[*
2,
3,
[+
4,
5,
],
6,
],
7,
]
Требуется обойти дерево, выполнить операции и сосчитать результат. Начинается обход с корневого <Expression>.
У меня получился такой способ:
Имеется стек, элементом которого может быть либо <Value>, либо <Null>.
Идём по элементам:
Если <Value>, вставляем его в стек.
Если <Expression>, вставляем в стек <Null> и начинаем обход дочерних элементов этого <Expression>.
По достижении конца <Expression>, поднимаемся по стеку до ближайшего <Null>, вычисляя новое <Value>, соответствующее данному <Expression>. Вычислив, убираем из стека <Null>, поставленный для данного <Expression>, и вставляем вычисленный <Value>. Переходим к следующему элементу.
То есть, применительно к примеру:
Push <Null> // Корневое <Expression> с операцией '+'
Push '1'
Push <Null> // <Expression> с операцией '*'
Push '2'
Push '3'
Push <Null> // <Expression> с операцией '+'
Push '4'
Push '5'
Pop '5' // <Value> в последнем <Expression> закончились, вычисляем его…
Pop '4'
Pop <Null> // Заменяем маркер <Null> на значение <Expression>
Push '9' (4 + 5)
Push '6'
Pop '6'
Pop '9'
Pop '3'
Pop '4'
Pop <Null>
Push '324' (2 * 3 * 9 * 6)
Push '7'
Pop '7'
Pop '324'
Pop '1'
Pop <Null>
Push '332' (1 + 324 + 7)
В итоге в стеке только один элемент с результатом.
Насколько это нормальный "по науке" способ? Как надо по-правильному? Есть ли более "быстрые" или более "экономные" подходы?
У меня способ вызывает подозрение тем, что
необходимо знать, что "<Expression> начался" и "<Expression> закончился". Обход сделан через Visitor и его интерфейс получается не классический:
interface IVisitor
{
void VisitValue(Value value);
void VisitExpression(Expression expression);
}
а специфический:
interface IVisitor
{
void VisitValue(Value value);
void VisitBeginExpression(Expression expression);
void VisitEndExpression(Expression expression);
}
что, конечно, ни суть, ни механизм паттерна не меняет, но именно что "вызывает подозрение"
приходится использовать специальные значения (<Null>) в стеке.
Можно ли (алгоритмически) избавиться от указанных недостатков?