Схема инфикса в постфикс - PullRequest
       13

Схема инфикса в постфикс

1 голос
/ 10 апреля 2010

Позвольте мне установить, что это часть задания класса, поэтому я определенно не ищу полный кодовый ответ. По сути, нам нужно написать конвертер в Scheme, который берет список, представляющий математическое уравнение в инфиксном формате, а затем выводит список с уравнением в постфиксном формате.

Нам был предоставлен алгоритм для этого, достаточно простой. Проблема заключается в том, что существует ограничение на использование любых доступных императивных языковых функций. Я не могу понять, как сделать это чисто функциональным образом. Это наше первое введение в функциональное программирование в моей программе.

Я знаю, что собираюсь использовать рекурсию, чтобы перебрать список элементов в выражении инфикса, подобном таковому.

(define (itp ifExpr)
  (
    ; do some processing using cond statement
    (itp (cdr ifExpr))
  ))

У меня реализована вся обработка (по крайней мере, насколько я могу, не зная, как делать остальное), но алгоритм, который я использую для реализации этого, требует, чтобы операторы помещались в стек и использовались позже. У меня вопрос, как мне реализовать в этой функции стек, который также доступен для всех рекурсивных вызовов?

Ответы [ 2 ]

4 голосов
/ 10 апреля 2010

(Обновлено в ответ на комментарий ОП; см. Новый раздел ниже исходного ответа.)


Используйте список для стека и сделайте его одной из переменных цикла. Э.Г.

(let loop ((stack (list))
           ... ; other loop variables here,
               ; like e.g. what remains of the infix expression
            )
  ... ; loop body
  )

Затем, когда вы захотите изменить то, что находится в стеке на следующей итерации, ну, в общем, просто сделайте это.

(loop (cons 'foo stack) ...)

Также обратите внимание, что если вам нужно выполнить несколько «последовательных» обновлений, вы часто можете смоделировать это с помощью формы let*. На самом деле это не работает с векторами в Scheme (хотя оно работает с постоянными векторами Clojure, если вы хотите посмотреть на них), но оно работает со скалярными значениями и списками, а также с потоками SRFI 40/41.


В ответ на ваш комментарий о том, что петли исключены как «императивная» функция:

(let loop ((foo foo-val)
           (bar bar-val))
  (do-stuff))

является синтаксическим сахаром для

(letrec ((loop (lambda (foo bar) (do-stuff))))
  (loop foo-val bar-val))

letrec затем расширяется до формы let, которая, вероятно, будет использовать что-то эквивалентное set! или локальному define внутри, но считается совершенно функциональным. Кстати, вы можете использовать другой символ вместо loop. Кроме того, этот тип let называется «именованным let» (или иногда «помеченным»).

Вы, вероятно, помните, что базовая форма let:

(let ((foo foo-val)
      (bar bar-val))
  (do-stuff))

также является синтаксическим сахаром при умном использовании lambda:

((lambda (foo bar) (do-stuff)) foo-val bar-val)

так что все сводится к применению процедуры, как обычно на схеме.

Именованные let делает самовосстановление красивее, вот и все; и, как я уверен, вы уже знаете, (само) рекурсия с хвостовыми вызовами - это путь, который нужно использовать при функциональном моделировании итерационных вычислительных процессов .

Понятно, что эта конкретная "зацикленная" конструкция прекрасно подходит и для императивного программирования - просто используйте set! или мутаторы структуры данных в теле цикла, если это то, что вы хотите сделать, - но если вы держитесь подальше от деструктивной функции в вызовах, нет ничего обязательного в циклической рекурсии или в теге let. Фактически, циклическая рекурсия является одним из самых основных приемов в функциональном программировании, и весь смысл этого вида домашней работы должен был бы научить именно этому ...: -)

Если вы действительно не уверены в том, можно ли его использовать (или будет достаточно ясно, что вы понимаете шаблон, если вы просто используете имя let), то вы можете просто удалить его, как описано выше ( возможно, используя локальный define вместо letrec).

0 голосов
/ 18 мая 2010

Я не уверен, что все правильно понимаю, но что не так с этим более простым решением:

Во-первых:

Вы проверяете, является ли ваш аргумент действительно списком:

Если да: добавьте MAP функции через хвост (постфиксатор карты (cdr lst)) к списку, содержащему только заголовок. Карта просто применяет постфикс снова к каждому последовательному элементу хвоста.

Если нет, просто верните аргумент без изменений.

Три строки Scheme в моей реализации переводятся:

(postfixer '(= 7 (/ (+ 10 4) 2)))

Кому:

(7 ((10 4 +) 2 /) =)

Рекурсия по карте не требует зацикливания, даже зацикливания на хвосте, без мутации и показывает функциональный стиль с применением карты. Если я не совсем понимаю вашу точку зрения, я не вижу необходимости во всей этой сложности выше.

Редактировать: О, теперь я читаю, инфикс, а не префикс, чтобы постфикс. Что ж, применима та же общая идея, за исключением того, что мы берем второй элемент, а не первый.

...