这个反引号“语法”在Lisp中是如何工作的?

5

以下是来自Paul Graham的《On Lisp》一书中的简化示例(scheme语法)。

(define-macro (bar)
  (let ((x 10) (y '(1 2 3)) (z 'foo))
    `(list ,x `(,',z ,,@y))))

我知道,,@y应该如何工作,但不确定,',z应该如何工作,应该先评估什么以及以什么顺序评估(我知道它应该评估为符号foo,因为在guile中返回(10(foo 1 2 3)),但我不确定确切的步骤是什么)。

我需要这个用于我的JavaScript中的Lisp,其中我有以下结果:

(10 ((unquote z) 1 2 3))

因为它只从左到右评估(我只处理特殊的,,和多个逗号)。如何评估这个表达式。

书中还有这个例子:

(defmacro propmacro (propname)
   `(defmacro ,propname (obj)
       `(get ,obj ',',propname)))

如何评估',',?在这种情况下有哪些步骤?

使用反引号/准引号的其他奇怪边缘情况是否还有?您能展示一些例子,并说明它们应该如何评估以及以什么顺序进行吗?


1
谷歌搜索“lisp嵌套反引号”,你会找到很多解释。 - Barmar
2
特别是这个很完整:https://3e8.org/pub/scheme/doc/Quasiquotation%20in%20Lisp%20(Bawden).pdf - coredump
1个回答

9
,',z 的工作原理如下:
`(list ,x `(,',z ,,@y))))
          ^ ^
          |  `- this comma
           `- belongs to this backquote

上述逗号插值,将表达式',z(quote ,z)插入到内部反引号中。而那个,z又属于外部反引号。
因此,z的值被插入到(quote ,z)中,以产生(quote <value-of-z>)
然后实际上,内部反引号就像`(,'<value-of-z>)一样行为。
具体来说,假设z包含列表(+ 2 2)。那么我们可以通过将外部反引号插入(+ 2 2)到内部反引号中来理解它,从而产生`(,'(+ 2 2) ...)。现在可以很容易地理解:当评估内部反引号时,(+ 2 2)被保护免受评估,从而产生对象((+ 2 2) ...)
模式,',',', ... ,',expr用于在评估最外层反引号期间获得对expr的单次评估,使得该值随后通过其余反引号嵌套的任意数量的评估轮次传播,而无需进一步评估。这里有一种“反引号代数”在起作用,其中“逗号和引号相互抵消”。
您还可以将,',','...视为一种钻头,它可以穿过嵌套的层数,允许您在结构中的任何位置放置字面值。例如:
(defmacro super-nested-macro (arg)
  `(... `(.... `(.....`(we simply want arg down here ,',',',arg)))))
super-nested-macro的作者想要将arg的值插入到一个被三个反引号嵌套的模板中的某个位置。因此,通常的,arg不能使用:逗号会被误解为属于最内层的反引号。

在反引号/准引号中还有其他奇怪的边缘情况吗?

在反引号中的一个奇怪的边缘情况是尝试将值拼接到点位置:
`(a b c . ,@foo)  ;; not allowed

`(a b c . ,foo)   ;; OK: equivalent to `(a b c ,@foo)

我不确定各种实现如何处理点位置上的反引号:

`(a b c . `(d e f))

这并没有什么意义,我怀疑实际获得的结果将取决于反引号实现内部。

并非所有对象都会被遍历以进行取消引用:

 `#c(,(sin theta) ,(cos theta)) ;; Not required by ANSI CL, oops!

这可以通过实现的扩展来实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接