Согласно и Lispworks's Hyperspec и CLtL2 (ищет define-modify-macro
), предполагается, что функция быть символом (к функции или макро). Насколько я знаю, следующее определение не может быть в соответствии спецификации:
(define-modify-macro _f (op operand)
(lambda (x op operand)
(funcall op x operand)))
Но, конечно же, вполне возможно, что реализация позволяет. Чтобы убедиться, что вы в соответствии со стандартом, вы можете определить свою собственную функцию, или даже макрос:
(defmacro funcall-1 (val fun &rest args)
`(funcall ,fun ,val ,@args))
(define-modify-macro _ff (&rest args) funcall-1)
(let ((x (list 1 2 3 4)))
(_ff (third x) #'+ 10)
x)
Если вы хотите иметь функцию в качестве второго аргумента, можно определить другой макрос:
(defmacro ff (fun-form place &rest args)
`(_ff ,place ,fun-form ,@args))
В принципе, ваш подход заключается в обертывании funcall
в define-modify-macro
и дает желаемую функцию в качестве аргумента этой функции. На первый взгляд, это выглядит как взломать, но, как мы видим ниже, это дает тот же макроэкранный код, что и в На Lisp, предполагая, что мы немного модифицируем его.
macroexpansion вышеперечисленное:
(LET ((X (LIST 1 2 3 4)))
(LET* ((#:G1164 X) (#:G1165 (FUNCALL #'+ (THIRD #:G1164) 10)))
(SB-KERNEL:%RPLACA (CDDR #:G1164) #:G1165))
X)
Версия в На Лисп ведет себя следующим образом:
(defmacro _f (op place &rest args)
(multiple-value-bind (vars forms var set access)
(get-setf-expansion
place)
`(let* (,@(mapcar #'list vars forms)
(, (car var) (,op ,access ,@args)))
,set)))
(let ((x (list 1 2 3 4)))
(_f * (third x) 10)
x)
Macroexpansion:
(LET ((X (LIST 1 2 3 4)))
(LET* ((#:G1174 X) (#:G1175 (* (THIRD #:G1174) 10)))
(SB-KERNEL:%RPLACA (CDDR #:G1174) #:G1175))
X)
Здесь *
находится вводится непосредственно макросом pansion, что означает, что полученный код не имеет возможности для выполнения во время выполнения (хотя компиляторы, вероятно, будут обрабатывать ваш (funcall #'+ ...)
одинаково хорошо). Если вы передадите #'+
макросу, он не будет макроэкспозироваться. Это основное отличие вашего подхода, но не большое ограничение.Для того, чтобы позволить На Лисп версии принять #'*
или даже (create-closure)
как оператор, он должен быть изменен следующим образом:
(defmacro _f (op place &rest args)
(multiple-value-bind (vars forms var set access)
(get-setf-expansion
place)
`(let* (,@(mapcar #'list vars forms)
(, (car var) (funcall ,op ,access ,@args)))
,set)))
(см вызов funcall
)
Предыдущий пример затем расширяются следующим образом, для #'*
:
(LET ((X (LIST 1 2 3 4)))
(LET* ((#:G1180 X) (#:G1181 (FUNCALL #'* (THIRD #:G1180) 10)))
(SB-KERNEL:%RPLACA (CDDR #:G1180) #:G1181))
X)
Теперь, это именно так, как ваша версия. На Lisp использует _f
, чтобы продемонстрировать, как использовать get-setf-expansion
, и _f
- хороший пример для этого. Но, с другой стороны, ваша реализация кажется одинаково хорошей.