Анализатор выражений Lisp в Java (используя только один стек)

Я пытаюсь реализовать простой оценщик выражений Lisp с использованием Java. На самом деле существует множество информации по этому вопросу, но, похоже, все они используют два отдельных стека для получения результата. Мне интересно, возможно ли реализовать такую ​​программу, используя только один стек и как может выглядеть какой-то псевдокод для этого.

Благодарю.

Для получения дополнительной информации о чем я говорю, обратитесь к

2 ответа

Оба интерпретатора, с которыми вы связались, делают очень важное предположение: что +, -, * и т. Д. Являются двоичными функциями, то есть они всегда принимают ровно два аргумента. В реальном Лиспе можно сказать (+ 1 2 3 4 5) суммировать кучу чисел. Но, если вы готовы принять упрощающее предположение, что арность каждого оператора известна, то вы определенно можете сделать это только с одним стеком. Ключ: переверните стопку вверх дном.

У переводчиков, с которыми вы связаны, есть стеки:

дно -> ( + ( - 2 1 ) 4 ) <- top (мы нажимаем и высовываемся отсюда)

Но вы также можете прочитать выражение назад и справа и построить стек следующим образом:

верх -> ( + ( - 2 1 ) 4 ) <- низ

Тогда вы по сути получите RPN, обратная польская запись. RPN очень хорошо играет со стеками.

Алгоритм такой:

  • Если вы видите круглые скобки, игнорируйте их
  • Если вы видите операнд, поместите его в стек
  • Если вы видите оператора, вызовите его. Оператор выскакивает столько значений, сколько ему нужно, а затем помещает свой результат в стек

Взять, к примеру, ( + ( - 2 1 ) 4 ), Вот как алгоритм будет работать:

Стек: пусто Чтение: ) Действие: скобки игнорируются. Оставил: ( + ( - 2 1 ) 4

Стек: пусто Чтение: 4 Действие: операнд помещен в стек. Оставил: ( + ( - 2 1 )

Стек: 4 Чтение: ) Действие: скобки игнорируются. Оставил: ( + ( - 2 1

Стек: 4 Чтение: 1 Действие: операнд помещен в стек. Оставил: ( + ( - 2

Стек: 1 4 Чтение: 2 Действие: операнд помещен в стек. Оставил: ( + ( -

Стек: 2 1 4 Чтение: -Действие: оператор вызван. Это вытолкнет 2 и 1 из стека, а затем нажмите 2-1=1 на него. Оставил: ( + (

Стек: 1 4 Чтение: ( Действие: скобки игнорируются. Оставил: ( +

Стек: 1 4 Чтение: + Действие: оператор вызван. Это вытолкнет 1 и 4 из стека, а затем нажмите 1+4=5 на него. Оставил: (

Стек: 5 Чтение: ( Действие: скобки игнорируются. Слева: ничего

Готово! Результат 5, как и ожидалось.

PS про игнорирование скобок. Это будет работать идеально, если введенное вами выражение правильно сформировано, но оно может принимать некорректно сформированные выражения и придавать им бессмысленные значения. например (+ - + 1 2 3 4) интерпретируется как (+ (- (+ 1 2) 3) 4), Если вы хотите предотвратить такое поведение, просто поместите закрывающие скобки в стек. Когда приходит время вызывать оператор, извлекайте аргументы и добавьте один дополнительный токен. Убедитесь, что токен близкий друг, иначе выведите ошибку. Как только у вас будет результат, поместите его в стек. Убедитесь, что следующий токен, который вы прочитали, является открытым пареном, а затем отбросьте его.

PPS это все становится намного сложнее, если, как в реальном Лиспе, аргументы функций сами могут быть функциями. Тогда у вас нет удобного различия между оператором / операндом, на который опирается RPN, и вам нужно уделять больше внимания скобкам как элементам группировки. Я не уверен, что вы могли бы сделать полноценный оценщик выражений Lisp с переменной arity и functions-as-operands только с одним стеком.

Надеюсь, что это поможет вам калькулятор в стиле LISP без стека

Другие вопросы по тегам