根据Lispworks 的 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
in define-modify-macro
,并将所需的函数作为该函数的参数。乍一看,它看起来像一个 hack,但正如我们在下面看到的,这给出了与On Lisp中的相同的宏扩展代码,假设我们对后者稍作修改。
上面的宏展开是:
(LET ((X (LIST 1 2 3 4)))
(LET* ((#:G1164 X) (#:G1165 (FUNCALL #'+ (THIRD #:G1164) 10)))
(SB-KERNEL:%RPLACA (CDDR #:G1164) #:G1165))
X)
On Lisp中的版本行为如下:
(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)
宏展开:
(LET ((X (LIST 1 2 3 4)))
(LET* ((#:G1174 X) (#:G1175 (* (THIRD #:G1174) 10)))
(SB-KERNEL:%RPLACA (CDDR #:G1174) #:G1175))
X)
在这里,*
由宏扩展直接注入,这意味着生成的代码没有可能的运行时开销(尽管编译器可能(funcall #'+ ...)
同样可以处理您的问题)。如果您传递#'+
给宏,它无法进行宏扩展。这是与您的方法的主要区别,但不是很大的限制。为了让On Lisp版本能够接受#'*
,甚至(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)
现在,它与您的版本完全相同。On Lisp使用_f
来演示如何使用get-setf-expansion
,并且_f
是一个很好的例子。但另一方面,您的实现似乎同样出色。