Macros 使用运算符参数定义修改宏

Macros 使用运算符参数定义修改宏,macros,common-lisp,setf,Macros,Common Lisp,Setf,在中,Paul Graham写道,“不幸的是,我们不能用define modify macro定义正确的\u f,因为要应用于广义变量的运算符是作为参数给出的。” 但是像这样的东西有什么问题吗 (define-modify-macro _f (op operand) (lambda (x op operand) (funcall op x operand))) (let ((lst '(1 2 3))) (_f (second lst) #'* 6) lst) =>

在中,Paul Graham写道,“不幸的是,我们不能用
define modify macro
定义正确的
\u f
,因为要应用于广义变量的运算符是作为参数给出的。”

但是像这样的东西有什么问题吗

(define-modify-macro _f (op operand)
  (lambda (x op operand)
    (funcall op x operand)))

(let ((lst '(1 2 3)))
  (_f (second lst) #'* 6)
  lst)

=> (1 12 3)
在ANSI Common Lisp中定义modify宏时是否有更改,但在编写Lisp时该更改无效?或者除了说明的原因之外,是否还有其他原因不在此处使用
define modify macro

格雷厄姆似乎希望能够打一个电话,比如

(_f * (second lst) 6)
而不是

(_f #'* (second lst) 6)
但这肯定与Lisp2(如Common Lisp)不符?

根据和(查找
定义修改宏)
,函数被假定为符号(函数或宏)。据我所知,以下定义可能不符合规范:

(定义修改宏(操作数)
(λ(x操作数)
(所有操作数(x操作数)))
但当然,有可能实现允许它。 为确保符合标准,您可以定义自己的函数,甚至宏:

(defmacro funcall-1(val fun和rest参数)
`(funcall,fun,val,@args))
(定义修改宏\u ff(&rest参数)funcall-1)
(让((x(清单1、2、3、4)))
(_ff(第三个x)#'+10)
十)
如果希望将函数作为第二个参数,可以定义另一个宏:

(defmacro ff (fun-form place &rest args)
  `(_ff ,place ,fun-form ,@args))
基本上,您的方法包括将
funcall
包装在
define modify macro
中,并将所需函数作为该函数的参数。乍一看,它看起来像一个黑客,但正如我们在下面所看到的,这给出了与Lisp中相同的macroexanded代码,假设我们对后者稍加修改

上述的宏观扩展是:

(LET((X(列表1 2 3 4)))
(设*(#:G1164 X)(#:G1165(FUNCALL#'+(THIRD#:G1164)10)))
(SB-KERNEL:%RPLACA(CDDR#:G1164)#:G1165))
十)
Lisp上的版本的行为如下所示:

(defmacro\u f(操作位置和休息参数)
(多值绑定(vars表单var集访问)
(获取setf扩展)
(地点)
`(让*(,@(mapcar#列出变量表)
(,(汽车变量)(,op,access,@args)))
,集)))
(让((x(清单1、2、3、4)))
(_f*(第三个x)10)
十)
宏观扩张:

(LET ((X (LIST 1 2 3 4)))
  (LET* ((#:G1174 X) (#:G1175 (* (THIRD #:G1174) 10)))
    (SB-KERNEL:%RPLACA (CDDR #:G1174) #:G1175))
  X)
在这里,
*
是由宏扩展直接注入的,这意味着生成的代码没有可能的运行时开销(尽管编译器可能会同样好地处理
(funcall#'+…)
)。如果将
#'+
传递给宏,则宏扩展失败。这是您的方法的主要区别,但不是一个很大的限制。为了允许On Lisp版本接受
#'*
,或者甚至接受
(创建闭包)
作为运算符,应该对其进行如下修改:

(defmacro\u f(操作位置和休息参数)
(多值绑定(vars表单var集访问)
(获取setf扩展)
(地点)
`(让*(,@(mapcar#列出变量表)
(,(汽车变量)(funcall,op,access,@args)))
,集)))
(请参见调用
funcall

然后,对前面的示例展开如下:
#'*

(LET((X(列表1 2 3 4)))
(设*(#:G1180 X)(#:G1181(FUNCALL#'*(THIRD#:G1180)10)))
(SB-KERNEL:%RPLACA(CDDR#:G1180)#:G1181))
十)

现在,它与您的版本完全相同。在Lisp上,使用
\u f
演示如何使用
get setf expansion
,而
\u f
就是一个很好的例子。但另一方面,您的实现似乎也很好。

关于是否希望传递
*
*
,我们还可以注意到
定义修改宏
版本的
\u f
和@coredump的修改版本(带有
funcall
)两者都接受带有或不带有
#'
的op位置的lambda表单,例如
(lambda(xy)(*xy))
#'(lambda(xy)(*xy))
,而Graham的原始版本只接受前者

有趣的是,道格·霍伊特(Doug Hoyte)在他的书中提请注意格雷厄姆(Graham)在他的书中的一句话,即在lambda形式之前省略
#
“最多提供了一种似是而非的优雅形式”,然后才愿意省略它

我并没有采取任何立场,只是指出,鉴于格雷厄姆对
\u f
的选择,缺少
#
不再是似是而非的,而是必要的