3

反引号的CLHS 声明“如果反引号语法是嵌套的,则应首先展开最里面的反引号形式。这意味着如果连续出现多个逗号,最左边的一个属于最里面的反引号。”

但是当我在 SBCL 中评估以下嵌套反引号表达式时

`(outer `(inner ,@(no ,@(list 'cat 'dog))))  ; expression P

我明白了

(OUTER `(INNER ,@(NO CAT DOG)))

似乎表达式 P 的外部反引号形式(而不是内部反引号形式)在以下意义上被扩展:

  • 外部反引号消失了,而内部反引号在评估后仍然存在
  • 只有对应于外部反引号级别的逗号完成了拼接工作(表达式 P 中的第二个逗号)

当 CHLS 说“应该首先扩展最里面的反引号形式”时,这是什么意思?

4

2 回答 2

2

Common Lisp 中 comma-comma-at的答案 帮助我弄清楚发生了什么。我只是要建立在这个答案的基础上并适用于这个问题。

我们在这里有两种推理嵌套反引号形式的方法。一种方法,我称之为替换解释,就是对于双重嵌套的反引号形式,对该形式的评估是

  • 删除最外面的反引号字符
  • 然后用它们的评估结果替换出现在与最外层反引号相同级别的逗号后面的形式(请参阅使用两个反引号和逗号,Common Lisp以获得相同级别的含义)(另外,删除那些逗号)
  • 然后通过将它们的评估结果拼接到它们的位置来替换出现在与最外层反引号相同级别的逗号形式(另外,删除那些逗号)

另一种方式,我将称之为等价解释,是我应该用等效的非反引号形式重新解释反引号形式,就像在 CLHS 链接中一样,对于双重嵌套的反引号形式,我应该在之前重新解释最里面的反引号形式重新解释最外层的反引号形式。

替换解释唯一明确指定的是如何评估反引号形式。反引号形式如何等同于非反引号形式只是间接指定为它们的评估如何工作的结果。

等效解释唯一明确指定的是反引号形式如何等效于非反引号形式。反引号表单如何被评估只是间接指定为它们等效的非反引号表单如何评估的结果。

SBCL 评估表达式 P 的方式很容易从替换解释中得到解释。但也可以从等价解释来解释。要了解原因,我们首先研究示例 A 和示例 B。

一些约定:我用 ~ 来表示“前一个表达式等同于下一个表达式”。我用 ~> 来表示“前一个表达式的计算结果等于或等于下一个表达式”。“等同于”我的意思是“可能被解释为”,就像在 CLHS 链接中一样。

示例 A(一个简单的反引号形式):

`(open ,@(list 'cat 'dog))
;; ~
(append (list `open)
        (list 'cat 'dog)
        nil)
;; ~
(append (list 'open)
        (list 'cat 'dog)
        nil)
;; ~>
(open cat dog)

示例 B(当 comma-at 位于更深的位置时):

`(beg (open ,@(list 'cat 'dog)))
;; ~
(append (list `beg)
        (list `(open ,@(list 'cat 'dog)))
        nil)
;; ~>
(beg (open cat dog))

示例 C(表达式 P)

`(outer
  `(inner ,@(no ,@(list 'cat 'dog))))
;; ~
`(outer
  '(append
    (list `inner)
    (no ,@(list 'cat 'dog))
    nil))
;; ~>
(outer
 '(append
   (list `inner)
   (no cat dog)
   nil))
;; ~
(outer
 `(inner ,@(no cat dog)))

因此,替代解释与迄今为止的等价解释是一致的。

这两种解释是否完全兼容?替换解释有一些复杂性,如果我正确阅读 CLHS 链接,等效解释似乎是 CLHS 的一部分。因此,等效解释似乎是真正的交易,而替代解释只是一种方便的错觉。

替代解释的并发症是什么?我已经通过替换解释浏览了反引号附录 C中的所有双重嵌套反引号示例,并且大多数示例(除了四个示例)没有显示任何复杂性。对于大多数示例,替换解释有效并且与实际结果一致。

您遇到并发症的四个例子是:

,@',@
,',@
,,@
,@,@

对于前两个,复杂之处在于您必须在引号字符之前拼接一个列表。在引号字符之前拼接是什么意思?这不代表任何意思。但是如果你认为'somethingas (quote something),那么你可以说在引号字符之前拼接单个元素列表是有意义的,并且它意味着引用该单个元素。如果您像这样修补替换解释,那么它会给出与实际结果一致的结果。

对于最后两个,复杂之处在于您必须在逗号字符(或逗号-at)之前拼接一个列表。在逗号字符前拼接是什么意思?这不代表任何意思。但是如果你说“让它意味着简单地拼接一个列表,然后在每个插入的元素前面放一个逗号(或逗号)”,那么这种修补替换解释给出的结果与实际结果一致。

Emacs Lisp 注意:对于最后两个,Emacs Lisp 和 Common Lisp 给出不同的结果。

常见的 Lisp 实现 注意:对于 comma-quote-comma-at,不同的实现会根据这个线程给出不同的结果

我会远离在我的代码中使用这四种情况中的任何一种。

于 2013-08-03T06:03:33.973 回答
1

我同意这个措辞可能令人困惑,但我认为理解它的关键是关于“如果几个逗号连续出现”的部分,指向应该首先扩展最里面的逗号形式的解释。从某种意义上说,这就是必须发生的事情。毕竟,考虑你的例子:

`(outer `(inner ,@(no ,@(list 'cat 'dog))))

考虑两种评估策略。

  • 一种选择是先尝试评估,@(no ...)。然而,这意味着评估表格

    (no ,@(list 'cat 'dog))
    

    但这并不好,因为现在我们将遇到,@(list 'cat 'dog)没有封闭反引号的情况。(是的,在更广泛的情况下,有一个反引号,但在评估(no ,@(list 'cat 'dog)).

  • 评估时的另一个选择(outer ...)是取消引用并拼接最里面的逗号形式,这意味着评估

    (list 'cat 'dog)
    

    这完全没有问题。完成此操作后,我们就有了您获得的表格:

    (outer `(inner ,@(no cat dog)))
    

为了其他读者遇到这个答案,有一些相关的问题:

于 2013-08-02T15:35:07.980 回答