Macros 使用运算符参数定义修改宏
在中,Paul Graham写道,“不幸的是,我们不能用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) =>
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
的选择,缺少#
不再是似是而非的,而是必要的