backquote

  • 反引用(backquote)

反引用 (backquote) 是引用(quote) 的特别版本,它可以用来创建 Lisp 表达式的模板。反引用最常见的用途之一是用在宏定义里。

反引用字符  得名的原因是:它和通常的引号  相似,只不过方向相反。当单独把反引用作为表达式前

缀的时候,它的行为和引号一样:

‘(a b c) 等价于’(a b c) .

只有在反引用和逗号 , ,以及comma-at “,@  一同出现时才变得有用。如果说反引用创建了一个模板,那

么逗号就在反引用中创建了一个 slot。一个反引用列表等价于将其元素引用起来,调用一次list。也就

是,

‘(a b c) 等价于(list ’a ’b ’c) .

在反引用的作用域里,逗号要求 Lisp:“把引用关掉”。当逗号出现在列表元素前面时,它的效果就相当于取

消引用,让 Lisp 把那个元素按原样放在那里。所以

‘(a ,b c ,d) 等价于(list ’a b ’c d) .

插入到结果列表里的不再是符号b,取而代之的是它的值。无论逗号在嵌套列表里的层次有多深,它都仍

然有效,

> (setq a 1 b 2 c 3)

3>

‘(a ,b c)

(A 2 C)

> ‘(a (,b c))

(A (2 C))

而且它们也可以出现在引用的列表里,或者引用的子列表里:

> ‘(a b ,c (’,(+ a b c)) (+ a b) ’c ’((,a ’b)))

(A B 3 (’6) (+ A B) ’C ’((1 ’B)))

一个逗号能抵消一个反引用的效果,所以逗号在数量上必须和反引用匹配。如果某个操作符出现在逗号的

外层,或者出现在包含逗号的那个表达式的外层,那么我们说该操作符包围了这个逗号。例如在‘(,a ,(b

‘,c)) 中,最后一个逗号就被前一个逗号和两个反引号所包围。通行的规则是:一个被 n 个逗号包围的逗

号必须被至少 n + 1 个反引号所包围。很明显,由此可知:逗号不能出现在反引用的表达式的外面。只要

遵守上述规则,就可以嵌套使用反引用和逗号。下面的任何一个表达式如果输入到 toplevel 下都将造成错

误:

,x ‘(a ,,b c) ‘(a ,(b ,c) d) ‘(,,‘a)

  • 嵌套的反引用(nested backquote)
嵌套的反引用是Lisp宏的难点,正如《On Lisp》所说的:
“为了定义一个定义宏的宏,我们通常会要用到嵌套的反引用。嵌套反引用的难以理解是出了名的。尽管最
终我们会对那些常见的情况了如指掌,但你不能指望随便挑一个反引用表达式,都能看一眼,就能立即说出
它可以产生什么。这不能归罪于 Lisp 。就像一个复杂的积分,没人能看一眼就得出积分的结果,但是我们
不能因为这个就把问题归咎于积分的表示方法。道理是一样的。难点在于问题本身,而非表示问题的方 法。”
stackoverflow有一个问答对Lisp的嵌套反引用解释的很透彻,我们下面来分析一下:
先来说一下嵌套反引用的解析规则:
CLTS2里面说:
如果反引号是嵌套的话,最内层的反引用形式(这是说的是最内层的逗号,其实是最外层的反引号)应该最先被展开。这意味着如果某个表达式有几个连续的逗号的话,最左边的那个逗号属于最里面的反引号。
R5RS Scheme语言规范关于反引号这么定义:
准引用可以嵌套。

举例分析:

``(a ,,(+ 1 2) ,(+ 3 4))
第一次求值得到如下表达式:
`(A ,3 ,(+ 3 4))
解析:1,左边反引用首先被展开(第一个反引用),所以(+ 1 2)被求值因为匹配逗号(第二个逗号)。
      2,另一个表达式,(+ 3 4)因为没有足够的逗号所以未求值。
      3,只有一个反引用被展开,因为反引用不会递归的展开。
第二次求值(展开所有的逗号)
为了在展开所有的反引用,我们采用如下表达式:
(eval ``(a ,,(+ 1 2) ,(+ 3 4)))

所用的反引用被展开,我们带到下面的求值结果:

(A 3 7)
 解析: 其实就是对第一次求值的结果继续求值,就可以得到上面的结果。
 
<<On Lisp>>一书上说,反引用的嵌套一般发生在定义宏的宏上面。下面是书中的一个例子:定义一个宏的简称的的宏。
因为一些CL的名字相当的长,比如destructuring-bind 和multiple-value-bind,所以我们可以定义宏来减少我们输入的字符
(defmacro dbind (&rest args)
   `(destructruing-bind ,@args))
(defmacro mvbind (&rest args)
   `(multiple-value-bind ,@args))
就可以了。我们可以看到dbind和mvbind是何等的相似。对于Lisp来说,宏是抽象和消除重复的好方法,那么我们为什么不再定义一个宏来
消除重复呢?假设我们想要得到一个abbrev宏,它允许我们使用(abbrev mvbind mutiple-value-bind)来定义缩写mvbind。下面是这个宏的定义:
(defmacro abbrev (short long)
  `(defmacro ,short (&rest args)
     `(,',long ,@args)))
卧槽,(,',XXXX),到这一步,我相信初学Lisper肯定凌乱了。其实我何尝不是呢。下面让我们一步一步分析这个宏定义是怎么来的。
我们可以从它的展开式开始,我们最终要一个如下的展开式:
(defmacro mvbind (&rest args)
`(multiple-value-bind ,@args))
我们如果先把multiple-value-bind从反引用中拉出来的话,推到就容易一点,得到如下等价的定义
(defmacro mvbind (&rest args)
(let ( (name 'multiple-value-bind ))
`(,name ,@args) ) )
现在我们将这个展开式转化为一个模板。我们把反引用放到前面,然后将可变的表达式变为一个变量
`(defmacro ,short (& rest args)
(let (( name ',long ))
`(,name ,@args) ) )
最后一步,我们把name 从内层反引用中消除,得到abbrev的宏的主体:
`(defmacro ,short (&rest args)
`(,',long ,@args) ) )
下面我们来正向分析,来展开abbrev宏,例如(abbrev mvbind mutiple-value-bind)
第一步:
首先展开最内层的反引用,和第一个逗号,得到结果
`(DEFMACRO ,SHORT (&REST ARGS) (LIST* ',LONG ARGS))
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值