Моя цель - создать макрос, который автоматически создает функции со списком аргументов, созданным в другом месте. Я хочу, чтобы макрос возвращал список, состоящий из функции и списка аргументов, который она использовала (список символов). Я использую SBCL.
Допустим, список аргументов генерируется:
(defun input-syms ()
(list 'in1 'in2 'in3))
;;=> (IN1 IN2 IN3)
Следуя полезному ответу в Вложенный `defun` выдает повторяющееся предупреждение в Allegro Common Lisp, я использовал labels следующим образом (добавляя элементы списка просто для примера):
(defmacro create-funtest ()
(let ((input-list (input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
#'fun-created)))
(funcall (create-funtest) 2 2 3) ;=> 7
Кажется, это работает, хотя я думаю, что может быть более простой способ сделать это. (list ,@input-list) кажется ненужным, но заменить его только на ,input-list не получится.
Здесь я в недоумении, и, кажется, это связано с вопросом о том, что именно означает ,input-list. Я думаю, это как-то связано с тем, что мы манипулируем символами, поэтому я попытался добавить туда symbol-value, но безуспешно.
Я бы хотел получить, выраженный в коде не работает:
(defmacro create-funtest2 ()
(let ((input-list (input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
(list #'fun-created ,input-list))))
Что должно вернуть: (#<FUNCTION ...> (IN1 IN2 IN3)).
Но вызов create-funtest2 дает ошибку компиляции The variable IN2 is unbound.. Я думаю, он пытается оценить символ вместо того, чтобы дать мне символ как есть.
Мне нужно иметь возможность получить символы, которые использовались для построения функции, я использую их при вызове функции впоследствии, чтобы узнать, какой ввод есть что. Этот список символов также можно изменить с помощью макроса create-funtest, поэтому мне действительно нужно получить его изнутри макроса.
Спасибо Райнеру Йосвигу за ответ. То, что меня беспокоит, на самом деле возвращает список символов в виде символов. Я предполагаю, что расширенный код для create-funtest2 (из приведенного вами расширения) должен выглядеть так:
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED (LIST 'IN1 'IN2 'IN3)))
Так что на выходе макрос будет (#<FUNCTION ...> (IN1 IN2 IN3)).
Проблема в том, что я хочу оценить input-list, но сохраняю его элементы как символы (не уверен, что правильно формулирую, извините).
Спасибо, coredump. Рабочая версия create-funtest2:
(defmacro create-funtest2 ()
(let ((input-list (test-input-syms)))
`(labels ((fun-created ,input-list
(reduce #'+ (list ,@input-list))))
(list #'fun-created (quote ,input-list)))))
Что дает расширение (спасибо, Райнер за фрагмент кода):
(let ((*print-circle* t)
(*PRINT-RIGHT-MARGIN* 50))
(pprint (copy-tree (macroexpand-1 '(create-funtest2)))))
=>
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED '(IN1 IN2 IN3)))
И называется так:
(defparameter *fun-created2* (create-funtest2))
(funcall (car *fun-created2*) 1 2 3) ; => 6, OK
(second *fun-created2*) ; => (IN1 IN2 IN3), OK
Действительно, я понял это, посмотрев на дополнения.
Некоторые из моих самых больших открытий были связаны с простой печатью значений в коде расширения (чтобы я мог видеть их результат при вызове макрорасширения).





Список ввода - (IN1 IN2 IN3).
Это работает:
(reduce #'+ (list IN1 IN2 IN3))
Это не работает:
(reduce #'+ (IN1 IN2 IN3))
Причина: Нет функции IN1.
Macroexpand - ваш друг:
CL-USER 58 > (let ((*print-circle* t)
(*PRINT-RIGHT-MARGIN* 50))
(pprint (copy-tree (macroexpand-1 '(create-funtest2)))))
(LABELS ((FUN-CREATED (IN1 IN2 IN3)
(REDUCE #'+ (LIST IN1 IN2 IN3))))
(LIST #'FUN-CREATED (IN1 IN2 IN3)))
У расширенного кода есть две проблемы:
IN1 в последней строке не существуетIN2 и IN3 в последней строке не существуютВозможно, macroexpand поможет вам решить вашу проблему.
Для получения дополнительной помощи вам нужно будет придумать немного лучшее объяснение того, что вы хотите сделать.
Причина, по которой вы можете захотеть использовать reduce при наличии произвольного длинного списка входов, заключается в том, что вызовы функций ограничены CALL-ARGUMENT-LIMIT. Однако здесь вы используете свой список символов в качестве параметров функции, который ограничен LAMBDA-PARAMETERS-LIMIT. Также второй должен быть больше или равен первому. Таким образом, если список символов достаточно короткий, чтобы служить списком параметров, он также достаточно короткий, чтобы использовать его в качестве аргументов при вызове +.
(defun input-symbols ()
'(in1 in2 in3))
(defmacro create-funtest ()
(let ((args (input-symbols)))
`(lambda ,args (+ ,@args))))
Здесь выше я также использовал анонимную функцию, но это не важно.
Ваша вторая версия, переписанная с использованием того же подхода, что и выше, выглядит следующим образом:
(defmacro bad-create-funtest2 ()
(let ((args (input-symbols)))
`(list (lambda ,args (+ ,@args))
,args)))
Что по этому поводу говорит macroexpand?
(macroexpand '(bad-create-funtest2))
=> (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
(IN1 IN2 IN3))
Здесь вы можете видеть, что вы пытаетесь вызвать in1 с аргументами in2 и in3.
Вы не хотите оценивать список символов, просто передайте его без оценки.
(defmacro create-funtest2 ()
(let ((args (input-symbols)))
`(list (lambda ,args (+ ,@args))
(quote ,args))))
Цитируя значение, вы можете гарантировать, что значение не будет оцениваться.
(macroexpand '(create-funtest2))
=> (LIST (LAMBDA (IN1 IN2 IN3) (+ IN1 IN2 IN3))
'(IN1 IN2 IN3))
Это оно ! Точка зрения, связанная с reduce, была сделана только для примера. Немного глупо, но все, что мне нужно, это (цитата, аргументы). Отредактирую свой вопрос рабочей версией create-funtest2.
Я получил удовольствие от «(list, @ input-list) кажется ненужным, но замена его просто, input-list не работает». Я обнаруживаю товарища-ныряльщика в глубоком конце. :) И я хорошо помню эту кривую обучения, так что было бы хорошо получить хорошее представление о том, что происходит, потому что один важный навык, необходимый для написания макросов, заключается в сохранении прямой разницы между расширением кода и его расширением при кодировании первого . Короче говоря, просто с помощью «(reduce '+, inputs)» вы генерируете «(reduce' + (in1 in2 in3))», а вовсе не то, что вы намеревались. Мета-совет: macroexpand-1