gpt4 book ai didi

macros - 带有运算符参数的定义-修改-宏

转载 作者:行者123 更新时间:2023-12-02 03:08:10 25 4
gpt4 key购买 nike

Section 12.4 of On Lisp ,Paul Graham 写道,“不幸的是,我们无法使用 define-modify-macro 定义正确的 _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 中的定义修改宏进行了更改,而该更改在编写 On Lisp 时无效?或者除了此处所述不使用 define-modify-macro 之外还有其他原因吗?

格雷厄姆似乎希望我们能够调用诸如

(_f * (second lst) 6)

而不是

(_f #'* (second lst) 6)

但这肯定不符合 Lisp2(例如 Common Lisp)吗?

最佳答案

根据 Lispworks's HyperspecCLtL2 (查找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 中,并将所需的函数作为该函数的参数。乍一看,它看起来像是一个 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)

这里,*是由宏扩展直接注入(inject)的,这意味着生成的代码没有可能的运行时开销(尽管编译器可能会处理您的(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 就是一个很好的例子那。但另一方面,您的实现似乎同样好。

关于macros - 带有运算符参数的定义-修改-宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31587429/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com