求教: SBCL 的bug??

我正在写上次分享过的 => 宏,要加强,可以提供多个占位符(上一个表达式提供多个 value),也可以在=>里嵌套 => 无数层次,也可以同时使用反方向和别名 <-, ->
但发现一个问题百思不得其解,感觉是 SBCL的bug的问题:

(vprint (list (second next) '~) `(,(second next) ~))
讲道理这两个应该是一模一样的吧? 但是我在执行的时候只有第一次(1+ ~) 是一致的,后面几次居然都是不一样的??!
一开始我是用 `(,(second next) ~) 在某些情况不能够正常运行,改为(list (second next) '~) 就正常了
谁能够解释吗? 我想是不是SBCL 的bug?


我把
(setf next `(,(second next) ~)))
改为
(setf next (list (second next) '~))
就跟预料的那样运行了

image
这个也一样改了后才正常运行(我定义了 (eq ~ '~) => t )

因为这是 ANSI CL 允许的 ub

http://clhs.lisp.se/Body/02_df.htm

注意加粗的地方

An implementation is free to interpret a backquoted form F1 as any form F2 that, when evaluated, will produce a result that is the same under equal as the result implied by the above definition, provided that the side-effect behavior of the substitute form F2 is also consistent with the description given above. The constructed copy of the template might or might not share list structure with the template itself. As an example, the above definition implies that

`((,a b) ,c ,@d)

will be interpreted as if it were

(append (list (append (list a) (list 'b) ’ nil )) (list c) d ’ nil )

but it could also be legitimately interpreted to mean any of the following:

(append (list (append (list a) (list 'b))) (list c) d) (append (list (append (list a) '(b))) (list c) d) (list* (cons a '(b)) c d) (list* (cons a (list 'b)) c d) (append (list (cons a '(b))) (list c) d) (list* (cons a '(b)) c (copy-list d))

SBCL 定义了一个 compiler macro 把 backquote form 常量化了

* (describe 'SB-INT:QUASIQUOTE)
SB-INT:QUASIQUOTE
  [symbol]

QUASIQUOTE names a macro:
  Lambda-list: (SB-IMPL::THING)
  Source file: SYS:SRC;CODE;BACKQ.LISP

QUASIQUOTE has a compiler-macro:
  Source file: SYS:SRC;CODE;BACKQ.LISP
* (macroexpand '`(,(second next) ~) )
(LIST* (SECOND NEXT) '(~))
T
* (sb-impl::expand-quasiquote '`(,(second next) ~) t)
(SB-IMPL::|List| 'SB-INT:QUASIQUOTE
                 (SB-IMPL::|List*| (SB-INT:UNQUOTE '(SECOND NEXT)) '(~)))
* (setf *PRINT-PRETTY* nil)
NIL
* '`(,(second next) ~)
(SB-INT:QUASIQUOTE (#S(SB-IMPL::COMMA :EXPR (SECOND NEXT) :KIND 0) ~))

常量我也不在知道那个 Symbol ~ 怎么会绑定到另外一个变量里啊?


这个勉强理解,就是强调不同的CL实现可以有不同的宏解析结果?
加粗的那句话主要就是强调可有不同的实现吗?

您后面附加的代码也不理解主要是强调什么,可能是我知之甚少了,但是以前没有遇到过这个问题。。。

强调 backquote 的行为可能和 quote 类似,模版的部分会变成「常量」

类比

你有个 (setf (deep-nth (car (nth n safe~ ... 的会修改掉 next 的 subform。

对呀,我是要修改next啊,但是那个的结果好像是我定义的常量 ~ 被改了:


但是当我加了2个 ~ 的时候又跟期望的一样。。。 这是你意料之中的吗?

感觉关键不是那个变量 next,不知道是不是跟我定义的全局常量

(defconstant ~ '~)

有关~

完全无关。

* (defvar *a* 12)         
*A*
* (defun foo () `(,(+ *a* 2) ~))
FOO
* (foo)     
(14 ~)
* (setq *a* 15)           
15
* (foo)
(17 ~)
* (defparameter tmp (cons 11 (foo)))
(11 17 ~)
* tmp
(11 17 ~)
* (caddr tmp)
~
* (setf (caddr tmp) 12)
12
* (foo)
(17 12)

懂?

* (defun foo2 () (list (+ *a* 2) '~))
FOO2
* (foo2)
(17 ~)
* (setq tmp (cons 12 (foo2)))
(12 17 ~)
* (setf (caddr tmp) 1)
1
* tmp
(12 17 1)
* (foo2)
(17 ~)
1赞

好像有点懂了,就是 `(… ~) 的 ~ 是绑定了某个全局的 cons ,每次调用这个函数都是用那个(改了也会全局改),而 (list … '~) 就是每次新建的? 就像是 C语言的 地址咯?

建议少用 setf,多用无副作用的构建方法。反正因为是 macro 只会对编译期有影响。

原来你说的 subform 就是指这个 ~ ,我本是想替换这个值的,没想到是修改这个全局的值


我自己实现的获取/setf 深度元素 不知道是不是你说的副作用导致的后果

我记得 PCL 那本书又是建议用 SETF 啊,所以我已经基本没有用 setq了。。。
记得其它 CL 书籍也是说适当的时候用 setq更好,但是也没有强调说尽量用setq

类似于 C 的 static 声明的作用。

多用无副作用的构建方法

这个是不是也没有在标准中要求但是是允许的(提高性能)? 你说的 ub 我不知道是什么(Google也没查到~)

a behavior or aspect of Common Lisp which has been deliberately left unspecified

1赞

我现在算是理解这句话了! 以前没有考虑过这个问题!~

ub = Unspecified Behavior
(未声明的行为,意味着实现者可以按照自己的方式实现以达到更佳效果)

有副作用的场合最好不要用list,可以用vector或者hashmap。