2013-05-09 2 views
2

Я пишу рекурсивный макрос Лиспа с номером n и оценивая тело n раз (упражнение от ANSI Lisp). Я пробовал два способа сделать это - сам макро-вызов в своем расширении и макрос расширяется в локальную рекурсивную функцию. Я не работаю так, как хочу.Lisp рекурсивные макро проблемы

Вот первый - у него переполнение стека, но когда я смотрю его расширение с помощью macroexpand-1, это кажется мне прекрасным.

(defmacro n-times (n &rest body) 
    (let ((i (gensym))) 
    `(let ((,i ,n)) 
    (if (zerop ,i) nil 
     (progn 
      ,@body 
      (n-times (- ,i 1) ,@body)))))) 

Вот второй один - он делает ошибку, «неопределенная функция #xxxx вызвана с аргументами (г)» где #xxxx это имя GENSYM и г составляет 1 меньше числа я называю его с. Я думаю, что есть проблема с тем, как я использую gensyms и flet вместе, но я не уверен, как это сделать правильно.

(defmacro n-times (n &rest body) 
    (let ((g (gensym))) 
    `(flet ((,g (x) 
       (if (zerop x) nil 
        (progn 
        ,@body 
        (,g (- x 1)))))) 
     (,g ,n)))) 

ответ

9

Чтобы ответить на ваш первый вопрос, у вас есть рекурсивное расширение макросов, которое никогда не перестает рекурсивно. Наличие if не останавливает рекурсивное расширение макросов, так как макрораспределение происходит во время компиляции, и ваш if происходит во время выполнения.

Чтобы ответить на второй вопрос, вы не можете использовать flet для указания рекурсивных функций, вместо этого вы должны использовать labels.

+0

Спасибо, что заставило его сработать! Почему вы не можете использовать flet для указания рекурсивных функций? – lightlike

+2

'flet' делает привязки видимыми только внутри тела. 'labels 'делает привязки видимыми внутри определений привязки. –

+1

Получил это. Будет ли первый метод в порядке, если я возьму 'if' из backquote для проверки' (zerop n) 'во время компиляции? – lightlike

7

Поскольку расширение макросов в Common Lisp происходит до начала выполнения, это немного сложно.

Помните, что макрос видит исходный код. Это означает:

  • Число n должно передаваться как число, а не переменная, когда вы используете макрос. Таким образом, при времени макроразложения число известно. Для такого макроса я бы проверял это в макросе - в противном случае у вас всегда возникает соблазн написать что-то вроде (let ((n 10)) (n-times n ...)) - это не сработает.

  • макрос должен вычислить рекурсивную итерацию. Таким образом, логика находится в макросе, а не в сгенерированном коде. Каждый макрос должен генерировать код, что на один шаг проще при увеличении макроса - пока не будет достигнут основной случай.

+0

OK, спасибо. Почему не стоит называть макрос, который должен принимать число с переменной, содержащей номер? Кроме того, это первый метод (имеющий сам макросообщение в своем расширении) «неправильный» по сравнению со вторым методом (с макрообъемением в рекурсивную функцию)? – lightlike

+1

В «Let Over Lambda» есть хороший раздел, который охватывает очки Райнера в этом посте более подробно: http://letoverlambda.com/index.cl/guest/chap5.html#sec_5 –

Смежные вопросы