Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > LISP > Реализация макроса once-only |
Автор: GreenTea22 20.9.2009, 20:40 |
В книге Practical Common Lisp в конце главы про макросы (http://gigamonkeys.com/book/macros-defining-your-own.html) есть пример макроса, который обеспечивает расчет аргументов макроса 1 раз в заданном порядке " Another classic macro-writing macro is once-only, which is used to generate code that evaluates certain macro arguments once only and in a particular order. Using once-only, you could write do-primes almost as simply as the original leaky version, like this: (defmacro do-primes ((var start end) &body body) (once-only (start end) `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))) ((> ,var ,end)) ,@body))) However, the implementation of once-only is a bit too involved for a blow-by-blow explanation, as it relies on multiple levels of backquoting and unquoting. If you really want to sharpen your macro chops, you can try to figure out how it works. It looks like this: (defmacro once-only ((&rest names) &body body) (let ((gensyms (loop for n in names collect (gensym)))) `(let (,@(loop for g in gensyms collect `(,g (gensym)))) `(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n))) ,(let (,@(loop for n in names for g in gensyms collect `(,n ,g))) ,@body))))) " Я только начинаю изучать лисп и такой страшный макрос просто шокировал. Потом все таки с горем пополам разобрался как он работает. А теперь ВОПРОС к уважаемым знатокам лиспа. В чем недостаток моей реализации этого макроса? (defmacro my-once-only ((&rest names) &body body) `(let (,@(loop for n in names collect `(,n (eval ,n)))) ,@body)) |
Автор: adejneka 21.9.2009, 05:56 | ||
DO-PRIMES с Вашей версией ONCE-ONLY производит вычисление аргументов START и END на этапе компиляции кода, использующего DO-PRIMES. Это работает только в том случае, когда аргументы - константные выражения без побочных эффектов, например, (DO-PRIMES (PRIME 11 99) ...). Если же значения этих аргументов должны определяться на этапе исполнения, особенно, если они имеют побочные эффекты, например, (DO-PRIMES (PRIME (AREF LOWER I) (PRINT (AREF UPPER (INCF I))) ...), то Ваш макрос не сработает:
|