Я попытался написать макрос defparameter для произвольного количества переменных, начиная с рабочего кода для одной переменной:
(defmacro defpar (name initial-value )
`(progn (declaim (special ,name))
(setf (symbol-value ',name) ,initial-value)
',name)
Вот модификация
(defmacro defpar (&rest rest)
(let ((i (gensym)))
`(let (,i)
;; (declaim (special ,rest))
(dotimes (,i (/ (length ',rest) 2))
(progn
(print (list ,i ',rest))
(setf (symbol-value ',(pop rest)) ',(pop rest))
)
))))
Сейчас:
(defpar S1 1)
(print `("S1 = " ,S1))
дает
(0 (S1 1))
("S1 = " 1)
Все в порядке: S1 определен и равен 1.
и для двух аргументов
(defpar S1 1 S2 2)
(print `("S1 = " ,S1 " S2 = " ,S2 ))
дает
(0 (*S1* 1 *S2* 2))
(1 (*S1* 1 *S2* 2))
.... The variable *S2* is unbound.
Как я проверил, список отдыха на новой итерации цикла тот же, но мне не удалось его обновить. И я пропустил специальное заявление. (declaim (special, rest)) возникает ошибка в SBCL. Дайте мне знать, если это важно.
Пожалуйста, помогите мне. Может быть, это уже сделано, как в setf?
Я бы назвал это defparameters





Не хорошо! Смотрите комментарии.
Это работает для меня:
(defmacro defpar-multiple (&rest varname-val-list)
`(loop for (varname val) on ',varname-val-list by #'cddr
collect varname
do
;;(format t "~a = ~a~%" varname val)
(setf (symbol-value varname) val)))
В ответе:
CL-USER> (defpar-multiple aaa 2 bbb 3)
Это расширяется до:
(loop for (varname val) on '(aaa 2 bbb 3) by #'cddr
collect varname
do (setf (symbol-value varname) val))
И оцениваю:
CL-USER> aaa
; Debugger entered on #<UNBOUND-VARIABLE AAA {700756DAD3}>
[1] CL-USER> bbb
; Debugger entered on #<UNBOUND-VARIABLE BBB {70079BB643}>
CL-USER> (defpar-multiple aaa 2 bbb 3)
(AAA BBB)
CL-USER> aaa
2 (2 bits, #x2, #o2, #b10)
CL-USER> bbb
3 (2 bits, #x3, #o3, #b11)
CL-USER>
Я не уверен, почему я должен писать:
(setf (symbol-value varname) val)
вместо:
(setf varname val)
но последний не сохраняется во время компиляции.
Отредактированный код:
(defmacro defpar-multiple (&rest varname-val-list)
`(loop for (varname val) on ',varname-val-list by #'cddr
for evaled-val = (eval val)
collect varname
do ;;(format t "~a = ~a~%" varname evaled-val)
(setf (symbol-value varname) evaled-val)))
Затем
CL-USER> (defpar-multiple att 0 gtt (+ 1 4))
расширяется до:
(loop for (varname val) on '(att 0 gtt (+ 1 4)) by #'cddr
for evaled-val = (eval val)
collect varname
do (setf (symbol-value varname) evaled-val))
Оценка:
CL-USER> att
; Debugger entered on #<UNBOUND-VARIABLE ATT {7007340F13}>
CL-USER> gtt
; Debugger entered on #<UNBOUND-VARIABLE GTT {700779AAF3}>
CL-USER> (defpar-multiple att 0 gtt (+ 1 4))
(ATT GTT)
CL-USER> att
0 (0 bits, #x0, #o0, #b0)
CL-USER> gtt
5 (3 bits, #x5, #o5, #b101)
CL-USER>
(defpar-multiple a (+ 1 2)) -> А = (+ 1 2)(setf varname val) присваивает переменной varname значение переменной var. Вы хотите, чтобы?
@RainerJoswig, нет! Редактирую свой ответ, чтобы оператор мог изменить галочку на вашем ответе.
@RainerJoswig, Могу ли я просто добавить (eval val), чтобы вместо установки переменной (+ 1 4) было установлено значение 5? Я добавил код с оценкой внизу моего ответа.
Расширение до цикла — не такое уж хорошее решение. Это могло бы привести к уменьшению кода, если бы были сотни переменных? Может быть. Ответ Райнера Йосвига очевиден: просто создайте программу с формами defparameter. eval плохой. Можно сделать лучше. Мы можем разделить символы и переменные: например. (a 1 b 2 c 3) можно превратить в (quote (a b c)) и (list 1 2 3). Мы позволяем этим выражениям вычисляться естественным образом, вставляя их в шаблон кода, а затем обрабатывая их параллельно for sym in '(a b c) and for val in (list 1 2 3).
Основная идея заключается в том, что у вас большой набор параметров, и вы, конечно же, хотите минимизировать код, исключить лишние определяемые параметры и т. д. Я думаю, что самое важное здесь — это читаемость кода. Тогда пары Имя-Значение с комментарием — лучший выбор, по крайней мере, для некоторых проблем)
@7stud: вопрос EVAL всегда заключается в следующем: в какой среде я оцениваю (интерпретирую или компилирую?) форму? Один пример: например, (let ((a 10)) (defpar-multiple b a)) не будет работать для локальной переменной a.
Просто создать несколько форм defparameter?
CL-USER 8 > (defmacro defpar (&rest var-and-value-list)
`(progn ,@(loop for (var value) on var-and-value-list by #'cddr
collect `(defparameter ,var ,value))))
DEFPAR
CL-USER 9 > (pprint (macroexpand '(defpar a 1 b 2 c 3)))
(PROGN (DEFPARAMETER A 1)
(DEFPARAMETER B 2)
(DEFPARAMETER C 3))
Детали расширения DEFPARAMETER различаются в зависимости от реализации CL.
Имейте в виду, что макрорасширение DEFPARAMETER не стандартизировано в Common Lisp. Различные реализации расширяют это в разные формы. Многие из них также зависят от реализации. Кроме того, расширение может предоставлять функции среды разработки, такие как запись исходного кода или запись местоположения исходного кода. Поэтому лучше не изобретать defparameter для нескольких форм, а использовать его повторно. Это гарантирует, что особенности реализации по-прежнему поддерживаются.
Спасибо! Замечательно, можно даже вставлять комментарии для отдельных переменных при вызове макроса! Очень полезно, когда у вас есть страницы переменных)
@Maxim Вам нужны страницы переменных? мне сейчас интересно
почему бы и нет?) это обычная ситуация в физических моделях.. поэтому я и задал вопрос..
насколько я понимаю, здесь также можно использовать документацию, реализованную в defparameter при необходимости.
Странно, что замена параметра defparameter на оператор defvar в макросе не меняет поведения: мы по-прежнему можем изменить переменную в любое время, используя setf. Конечно, мы можем проверить, используя переменную погодыboundp, которая уже определена, но я ожидаю, что это должен сделать defvar.
@Maxim DEFPARAMETER или DEFVAR не имеют ограничивающего влияния на обновления через SETF.
Мой ответ не работает, если макрос вызывается следующим образом:
(defpar-multiple x (+ 1 2))потому что мой ответ не оценивает(+ 1 2), тогда как если вы вызовете(defparameter x (+1 2)), тоxбудет установлено в3.