Лексические замыкания над макролетом?
Есть ли способ сделать что-то вроде лексического замыкания, используя macrolet? Что я хочу сделать, так это сделать следующий макрос локальным рекурсивным помощником, который вызывает функцию для каждой комбинации вместо генерации списка, как это теперь происходит при вызове макроса в repl:
CL-USER> (combinations nil '(1 2 3) '(4 5 6))
((1 4) (1 5) (1 6) (2 4) (2 5) (2 6) (3 4) (3 5) (3 6))
То, что я хотел бы, это макрос, который принимает функцию и любое количество списков и приводит к вложенным циклам, которые вызывают функцию для каждой комбинации. Я довольно новичок в lisp, это первый макрос, который я написал за пределами клонов 'nif' и т.п., поэтому любые предложения приветствуются.
Я пытался превратить макрос в макрос в макросе, который принимает функцию, и строка '(nreverse (list,item,@vars))' заменяется на '(func (nreverse (list,item,@vars)))) 'но я получаю ошибки, утверждая, что func - неопределенная переменная или функция.
Это оригинальная функция:
(defmacro combinations (vars &rest lsts)
(with-gensyms (item)
`(loop for ,item in ,(car lsts) ,(if (null (cdr lsts)) 'collecting 'nconcing)
,(if (null (cdr lsts))
`(nreverse (list ,item ,@vars))
`(combinations (,item ,@vars) ,@(cdr lsts))))))
Это то, что я пробовал с макросом и получаю неопределенные ошибки функции 'func'.
(defmacro for-all-combonations (func &rest lst)
(macrolet ((for-all (vars &rest lsts)
(with-gensyms (item)
`(loop for ,item in ,(car lsts) ,(if (null (cdr lsts))
'collecting 'nconcing)
,(if (null (cdr lsts))
`(func (nreverse (list ,item ,@vars)))
`(for-all (,item ,@vars) ,@(cdr lsts)))))))
(for-all nil lst)))
1 ответ
Макросы не являются первоклассными объектами в Common Lisp, поэтому вы не можете иметь эквивалент лексического замыкания в качестве макроса. Вы можете получить аналогичный эффект, создав функцию, которая генерирует список, который является допустимой программой на Лиспе, а затем проверяет его.
Это, вероятно, не очень хорошее решение вашей проблемы, хотя. Как сказал Райнер Йосвиг, макросы предназначены для манипулирования исходным кодом. Используйте их, когда вы хотите новую синтаксическую форму, которая не встроена в язык. Не используйте их там, где вы можете написать то, что вы хотите с помощью обычных функций.