У меня проблемы с макросом чтения обратных кавычек Lisp. Всякий раз, когда я пытаюсь написать макрос, который, кажется, требует использования встроенных обратных кавычек (например, ``(w ,x ,,y)
из ANSI Common Lisp Пола Грэма, стр. 399), я не могу понять, как написать свой код таким образом что компилируется. Обычно мой код получает целую цепочку ошибок, которой предшествует фраза «Запятая не внутри обратной кавычки». Может ли кто-нибудь дать некоторые рекомендации о том, как я могу написать код, который будет правильно оценивать?
В качестве примера мне сейчас нужен макрос, который принимает форму, описывающую правило в форме '(function-name column-index value)
и генерирующую лямбда-тело предиката, чтобы определить, удовлетворяет ли элемент, проиндексированный column-index
для конкретной строки, правилу. Если бы я вызвал этот макрос с правилом '(< 1 2)
, я бы хотел, чтобы тело лямбда выглядело следующим образом:
(lambda (row)
(< (svref row 1) 2))
Лучший удар, который я могу нанести по этому поводу, следующий:
(defmacro row-satisfies-rule (rule)
(let ((x (gensym)))
`(let ((,x ,rule))
(lambda (row)
(`,(car ,x) (svref row `,(cadr ,x)) `,(caddr ,x))))))
После оценки SBCL выдает следующий отчет об ошибке:
; in: ROW-SATISFIES-RULE '(< 1 2)
; ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121))
;
; caught ERROR:
; illegal function call
; (LAMBDA (ROW) ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121)))
; ==>
; #'(LAMBDA (ROW) ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121)))
;
; caught STYLE-WARNING:
; The variable ROW is defined but never used.
; (LET ((#:G1121 '(< 1 2)))
; (LAMBDA (ROW) ((CAR #:G1121) (SVREF ROW (CADR #:G1121)) (CADDR #:G1121))))
;
; caught STYLE-WARNING:
; The variable #:G1121 is defined but never used.
;
; compilation unit finished
; caught 1 ERROR condition
; caught 2 STYLE-WARNING conditions
#<FUNCTION (LAMBDA (ROW)) {2497F245}>
Как я могу написать макросы для генерации нужного мне кода и, в частности, как реализовать row-satisfies-rule
?
Используя идеи Ivijay и schemeulus, я модифицировал макрос так, чтобы он компилировался и работал, даже позволяя передавать формы в качестве аргументов. Он работает немного иначе, чем мой изначально запланированный макрос, поскольку я определил, что включение row
в качестве аргумента для более плавного кода. Однако это уродливо, как грех. Кто-нибудь знает, как его очистить, чтобы он работал так же без вызова eval
?
(defmacro row-satisfies-rule-p (row rule)
(let ((x (gensym))
(y (gensym)))
`(let ((,x ,row)
(,y ,rule))
(destructuring-bind (a b c) ,y
(eval `(,a (svref ,,x ,b) ,c))))))
Кроме того, будет очень признательно объяснение чистых, Lispy способов получения макросов для генерации кода для правильной оценки аргументов во время выполнения.
gensym
s необходимы? Вы не вводите никаких новых переменных в расширение, и имена переменных, которые вы используете для генерации расширения, не вызовут утечек. - person smackcrane   schedule 25.06.2011gensym
s, уберитеeval
, а затем это три линии, выглядит красиво и работает правильно. Вы даже можете убратьdestructuring-bind
и сделать его двумя строчками. - person smackcrane   schedule 25.06.2011