有人可以帮助我了解如何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
不计算两次正确的地方吗?
这样做的权利似乎是一个更复杂些。 例如,对于代码push
在SBCL 1.0.58是:
(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膨胀似乎是有用的。
根据记录,生成的代码看起来相当不错:
推成一个符号:
(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))
所以,除非你需要一些时间来研究setf
, SETF扩张和公司,这看起来相当神秘的(但仍可能显得那么即使他们学习后)。 在“广义变量”一章OnLisp可能是有用的。
提示:如果您编译自己的SBCL(并不难),通过--fancy
参数make.sh
。 这样,您就可以快速地看到里面SBCL功能/宏的定义(例如,与M-。Emacs的+泥里面)。 显然,不删除这些源(可以运行clean.sh
后install.sh
,节省90%的空间)。
考虑看看如何在现有的(在SBCL,至少)做的事情,我看到:
* (macroexpand-1 '(push 1 *foo*))
(LET* ((#:G823 1) (#:NEW822 (CONS #:G823 *FOO*)))
(SETQ *FOO* #:NEW822))
T
所以,我想,在你的版本,这是什么产生,人们可能会做一个组合的混合:
(defmacro my-push (element place)
(let ((el-sym (gensym))
(new-sym (gensym "NEW")))
`(let* ((,el-sym ,element)
(,new-sym (cons ,el-sym ,place)))
(setq ,place ,new-sym)))))
一些意见:
这似乎与任何工作setq
或setf
。 根据什么问题,你实际上是试图解决(我相信重写push
是不实际的最终目标),你可能会偏向一方或另一方。
需要注意的是place
确实还得到两次评估...只有评估后尽管它至少这样做的element
。 是你真正需要避免双重评价的东西吗? (由于内置push
不,我在想,如果/你怎么能...虽然我花非常多的时间考虑这个问题。之前写这个了),考虑到它的东西需要评价为“ 到位 ”,也许这是正常的吗?
使用let*
而不是let
允许我们使用,el-sym
中的设置,new-sym
。 这使其中的cons
情况,例如,它在第一评价评估,place
,和后评价,element
。 或许,这可以让你你需要什么,相对于评估排序?
我认为你的第二个版本,最大的问题是,你的setf
确实需要传递的符号操作,而不是在一个gensym
符号。
希望这有助于...(我还是有点新的这一切我自己,所以我在这里做一些猜测。)