Создание динамических предложений COND в Common Lisp
Интересно, возможно ли динамически создавать предложения COND из цикла типа (псевдокод):
(defvar current-state 1)
(defmacro mymacro ()
(cond
`(loop (state . callback) in possible-states
do ((eq current-state ,state)
(funcall ,callback)))))
LOOP будет строить предложения из списка и генерировать что-то вроде:
(cond
((eq current-state 1)
(funcall func-1))
((eq current-state 2)
(funcall func-2))
((eq current-state 3)
(funcall func-3)))
1 ответ
Макросы расширяются во время компиляции, поэтому ваш possible-states
переменная должна быть константой времени компиляции. Если это не так (или если вы не совсем понимаете, что я имею в виду выше), вам не следует использовать макрос здесь.
Вместо этого используйте функцию:
(funcall (cdr (find current-state possible-states :key #'car :test #'eq)))
или же
(funcall (cdr (assoc current-state possible-states :test #'eq)))
или, еще лучше, сделать свой possible-states
хеш-таблица, а не список ассоциаций:
(funcall (gethash current-state possible-states))
Однако, если ваш possible-states
является постоянной времени компиляции, вы можете использовать макрос, за исключением того, что вы, вероятно, хотите использовать case
вместо cond
:
(defmacro state-dispatch (state)
`(case ,state
,@(mapcar (lambda (cell)
`((,(car cell)) (,(cdr cell))))
possible-states)))
(defparameter possible-states '((1 . foo) (2 . bar)))
(macroexpand-1 '(state-dispatch mystate))
==> (CASE MYSTATE ((1) (FOO)) ((2) (BAR))) ; T
Обратите внимание, что с точки зрения скорости, gethash
версия, вероятно, идентична версии макроса (по крайней мере, она не медленнее).