List 为什么可以在准整数列表末尾的非列表上使用无引号拼接?

List 为什么可以在准整数列表末尾的非列表上使用无引号拼接?,list,scheme,language-lawyer,common-lisp,quasiquotes,List,Scheme,Language Lawyer,Common Lisp,Quasiquotes,由于2不是列表,因此准链接列表`(1,@2 3)无效。但是,`(12,@3)是有效的,将返回一个虚线列表:(12.3)。我在CommonLisp和Scheme中观察到了这个结果。为什么可以在准索引列表的末尾对非列表使用非引号拼接?为什么结果是虚线列表?”(1 2 3)实际上是(cons 1(cons 2(cons 3 nil)) 因此,(1 2 3)中的最后一个元素不是一个“非列表”,而是一个(cons 3 nil),即(列表3),因此可以拼接。要构造一个包含3个准量化变量的列表,可以编写 `(

由于2不是列表,因此准链接列表
`(1,@2 3)
无效。但是,
`(12,@3)
是有效的,将返回一个虚线列表:
(12.3)
。我在CommonLisp和Scheme中观察到了这个结果。为什么可以在准索引列表的末尾对非列表使用非引号拼接?为什么结果是虚线列表?

”(1 2 3)
实际上是
(cons 1(cons 2(cons 3 nil))


因此,
(1 2 3)
中的最后一个元素不是一个“非列表”,而是一个
(cons 3 nil)
,即
(列表3)
,因此可以拼接。

要构造一个包含3个准量化变量的列表,可以编写

`(,x ,y ,z)
这可以去除糖渍

(cons x (cons y (cons z nil)))
如果
z
是一个列表,我们希望将其内容拼接到结果列表中,该怎么办?然后,我们不再为它创建新的缺点,而是简单地将它放在尾部。当我们写作时

`(,x ,y ,@z)
这一点很重要

(cons x (cons y z))
如果
z
恰好不是一个列表,那么结果就是一个合法值,尽管不是一个正确的列表。例如,正如您所观察到的,
(12.3)
。如果您显式地编写被删除的表单,您将看到相同的结果,如
(cons1(cons2 3))
。如果这也让您感到困扰,您可能需要研究一般不正确列表或点对表示法的概念。

表达式
`(12,@3)
在Scheme或Common Lisp中都无效


计划 在R6RS方案中(与R5RS类似),该行为未指定用于在具有
无引号拼接的非列表上操作。R6RS方案标准要求():

如果(取消报价拼接…)表单出现在中,则s必须计算为列表


公共口齿不清 常见的Lisp HyperSpec首先说():

如果逗号后面紧跟着at符号,则 在at符号之后进行求值以生成对象列表。 然后将这些对象“拼接”到模板中的适当位置

在子表达式
,@3
中,
3
不会计算为列表。这似乎是一个相当有力的论点,认为该表达式无效。即使在拼接之前将
3
神奇地放置在列表中,也不会产生虚线列表。HyperSpec继续提供反引号语法的正式摘要。利益部分是:

  • `(x1 x2 x3…xn.原子)可解释为

    (附加[x1][x2][x3]…[xn](引用原子))

    其中,括号用于表示xj的转换,如下所示:

    --[form]被解释为(list`form),其中包含一个后引表单,必须进一步解释

    --[,表单]被解释为(列表表单)

    --[,@form]被解释为form

因此,在Common Lisp中,原始表达式(相当于
`(12,@3.nil)
)可以解释为:

(append (list `1) (list `2) 3 (quote nil))
但是,这不是对
append
的有效调用,它需要为除最后一个参数之外的所有参数提供正确的列表。因此,似乎没有人支持原始表达式有效的观点

它在Scheme和Common Lisp中对OP都有效的事实可能归结为不同实现中对backquote宏的类似定义。这些定义似乎都期望
,@
后面的表单将计算为一个列表;如果不是这样,根据标准,观察到的行为(虚线列表的生成)就不能依赖。也就是说,我测试了Chez方案、Guile方案、MIT方案、SBCL、CCL和CLisp:它们都表现出OP报告的相同行为


一个有趣的案例 我也测试过了。这个案例更有趣。CLTL2中的反引号实现旨在探索反引号表达式的行为,并具有可选的代码简化阶段。这里,
$
对应于反引号,
%@
对应于
,@
。如果不进行代码简化,扩展原始表达式的结果是:

CL-USER> (setf *bq-simplify* nil)
NIL
CL-USER> (try '("$(1 2 %@3)"))
`(1 2 ,@3) = (APPEND (LIST '1) (LIST '2) 3 'NIL)
这对应于上面阅读HyperSpec中的描述时所期望的表达式。但请注意,此表达式不会编译:

CL-USER> (append (list 1) (list 2) 3 nil)

The value
  3
is not of type
  LIST
[Condition of type TYPE-ERROR]
但是,当启用代码简化时:

CL-USER> (setf *bq-simplify* t)
T
CL-USER> (try '("$(1 2 %@3)"))
`(1 2 ,@3) = (LIST* '1 '2 3)
此“简化”表达式有效,计算结果为虚线列表:

CL-USER> (list* 1 2 3)
(1 2 . 3)
我的结论是,
,@
后面的表达式必须是符合公共Lisp标准的列表,但是一些常见的实现要么进行某种形式的代码简化,类似于CLTL2中所示,要么以某种方式扩展反引号形式,使非列表形式可以跟随
,@
。不要依赖它,因为很难说它什么时候不起作用。

正确的列表 其他答案已经详细说明了这一部分,让我快速地重新表述:列表要么是正确的列表,要么是不正确的列表;不正确的列表是圆形列表或虚线列表

特别地,虚线列表是其最后一个cons单元具有非列表cdr槽的非循环列表

有些函数只有在给定适当的列表时才能工作,而另一些函数则没有这样的限制,这通常是因为检查此属性并不是没有成本的。 写
(cons12)
会创建这样一个列表。现在,如果拼接语法扩展为
(cons x y)
,其中
y
是一个非列表,那么也会有一个虚线列表

切片 第二章(重点):

如果逗号后面紧跟着at符号,则对at符号后面的表单进行计算,以生成对象列表

我看不到任何其他注释表明拼接非列表值可能是列表的一部分,即使实际上在列表末尾发生拼接时会导致不正确的列表

该节还指出:

将被解释
`((,a b) ,c ,@d)
(append (list (append (list a) (list 'b) 'nil)) (list c) d 'nil)
(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))
(list* (cons a '(b)) c (copy-list d))
`(1 2 ,@3)