Краткий ответ: Если вы хотите использовать if
, обратите внимание, что вам понадобится progn
, чтобы иметь более одной формы в последующих и альтернативных случаях.
Longответ - также объясняет, как пройти через накопление посещенных узлов в списке:
Я предполагаю, что это домашняя работа, поэтому я не дам вам полного решения, но ваш вопрос показывает, что у вас в основном правильная идея,так что я покажу вам простой идиоматический способ сделать это.
Во-первых, вы правы: машина без кавычек должна быть функцией, поэтому, в принципе, все что угодно, как (foo ...)
, где foo
не является функцией (или макросом, специальной формой ...), и все это должно быть оценено, будет ошибкой.Обратите внимание, что это не распространяется на специальные формы и макросы (например, cond
).Они могут изменить правила оценки, и не все, что выглядит как (foo bar)
, должно быть формой, которая должна оцениваться по нормальным правилам оценки.Самым простым примером будет quote
, который просто возвращает свой аргумент без оценки, поэтому (quote (foo bar))
будет не ошибкой.
Теперь о вашей проблеме:
Простым решением было бы иметь аккумулятор и рекурсивную вспомогательную функцию, которая пересекает дерево и помещает значения в аккумулятор.Примерно так:
(defun pre (node)
(let ((result (list)))
(labels ((rec (node)
(cond (...
...
...))))
(rec node)
(nreverse result))))
labels
просто вводит локальную вспомогательную функцию, которая будет выполнять рекурсию, а внешний let
дает вам аккумулятор для сбора значений узлов.Это решение вернет результат в виде списка.Если вы просто хотите напечатать значение каждого узла, вам не нужен аккумулятор или вспомогательная функция.Просто напечатайте вместо нажатия и сделайте помощника вашей функцией верхнего уровня.
Помните, что вам понадобится базовый случай, когда рекурсия останавливается.Вы должны проверить это в cond
.Затем вам понадобятся рекурсивные шаги для каждого поддерева, и вам нужно будет подтолкнуть значение узла к результатам.Порядок, в котором вы выполняете эти шаги, определяет, будете ли вы выполнять обход до, во время или после заказа.Ваш код показывает, что вы уже понимаете этот принцип, поэтому вам просто нужно заставить его работать в Лисп-коде.Вы можете использовать push
, чтобы переместить значения в result
, и consp
, чтобы проверить, является ли узел непустым списком.Поскольку для пустых списков ничего не нужно делать, вам, в основном, понадобится только один тест в cond
, но вы также можете явно проверить, является ли узел null
, как вы делали в своем коде.