gpt4 book ai didi

macros - 我如何实现推送宏?

转载 作者:行者123 更新时间:2023-12-02 05:34:58 26 4
gpt4 key购买 nike

谁能帮我理解 push 如何作为宏来实现?下面的原始版本对位置形式求值两次,并且在对元素形式求值之前这样做:

(defmacro my-push (element place)
`(setf ,place (cons ,element ,place)))

但是如果我尝试按下面的方式解决这个问题,那么我的setf-ing 错误的地方:

(defmacro my-push (element place)
(let ((el-sym (gensym))
(place-sym (gensym)))
`(let ((,el-sym ,element)
(,place-sym ,place))
(setf ,place-sym (cons ,el-sym ,place-sym)))))

CL-USER> (defparameter *list* '(0 1 2 3))
*LIST*
CL-USER> (my-push 'hi *list*)
(HI 0 1 2 3)
CL-USER> *list*
(0 1 2 3)

如何在不计算两次的情况下setf正确的位置?

最佳答案

正确执行此操作似乎有点复杂。例如,SBCL 1.0.58 中push 的代码是:

(defmacro-mundanely push (obj place &environment env)
#!+sb-doc
"Takes an object and a location holding a list. Conses the object onto
the list, returning the modified list. OBJ is evaluated before PLACE."
(multiple-value-bind (dummies vals newval setter getter)
(sb!xc:get-setf-expansion place env)
(let ((g (gensym)))
`(let* ((,g ,obj)
,@(mapcar #'list dummies vals)
(,(car newval) (cons ,g ,getter))
,@(cdr newval))
,setter))))

所以阅读 get-setf-expansion 上的文档好像有用。

郑重声明,生成的代码看起来相当不错:

插入一个符号:

(push 1 symbol)

扩展成

(LET* ((#:G906 1) (#:NEW905 (CONS #:G906 SYMBOL)))
(SETQ SYMBOL #:NEW905))

插入一个支持 SETF 的函数(假设 symbol 指向一个列表列表):

(push 1 (first symbol))

扩展成

(LET* ((#:G909 1)
(#:SYMBOL908 SYMBOL)
(#:NEW907 (CONS #:G909 (FIRST #:SYMBOL908))))
(SB-KERNEL:%RPLACA #:SYMBOL908 #:NEW907))

所以除非你花一些时间研究setfsetf expansions和公司,这看起来很神秘(即使在研究它们之后看起来仍然如此)。 OnLisp 中的“广义变量”一章也可能有用。

提示:如果您编译自己的 SBCL(并不难),请将 --fancy 参数传递给 make.sh。这样您就可以快速查看 SBCL 中函数/宏的定义(例如,在 Emacs+SLIME 中使用 M-.)。显然,不要删除这些源(您可以在 install.sh 之后运行 clean.sh,以节省 90% 的空间)。

关于macros - 我如何实现推送宏?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11854357/

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