本文共 9341 字,大约阅读时间需要 31 分钟。
'(value-of-number (1+ number) something-with-string (length string)))
运行之后结果如下:Lisp> (mess-with 20 "foo")(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))
但是我们实际的意思是想要 number的值和 String的长度组成的一个列表。上面的写法是不能实现我们的要求的。看下面的这个定义:
(defun mess-with (number string) (list 'value-of-number (1+ number) 'something-with-string (length string)))
执行结果如下:
Lisp> (mess-with 20 "foo")(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)
反引用和引用是等价的,下面是上面函数定义的反引用版本:
(defun mess-with (number string ) `(value-of-string ,(+ 1 number ) something-with-string ,(length string )) )
下面是另一个例子:
Lisp> (quote spiffy-symbol)SPIFFY-SYMBOLLisp> 'spiffy-symbol ; '是quote的等价形式,和上一个表达式相同SPIFFY-SYMBOL
如果我们不使用引用,那么结果如下:
Lisp> spiffy-symbol
Unbound variable: SPIFFY-SYMBOL [Condition of type UNBOUND-VARIABLE]Restarts: 0: [CONTINUE] Retry getting the value of SPIFFY-SYMBOL.Unbound variable: SPIFFY-SYMBOL [Condition of type UNBOUND-VARIABLE]Restarts: 0: [CONTINUE] Retry getting the value of SPIFFY-SYMBOL.
因为没有spiffy-symbol。
所以quote 和反引用(和逗号)和list 函数等是你创造列表的工具。 列表并不是简单的值的序列,其实可以看做一个轻量级的数据结构(不一定是结构)。 进深阅读:Ron Garret's
嵌套的反引用是Lisp宏的难点,正如《On Lisp》所说的:
stackoverflow有一个问答对Lisp的嵌套反引用解释的很透彻,我们下面来分析一下:
先来说一下嵌套反引用的解析规则:
CLTS2里面说:
如果反引号是嵌套的话,最内层的反引用形式(这是说的是最内层的逗号,其实是最外层的反引号)应该最先被展开。这意味着如果某个表达式有几个连续的逗号的话,最左边的那个逗号属于最里面的反引号。
``(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)
解析: 其实就是对第一次求值的结果继续求值,就可以得到上面的结果。
<>一书上说,反引用的嵌套一般发生在定义宏的宏上面。下面是书中的一个例子:定义一个宏的简称的的宏。
因为一些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))
英文:
The introduces a template of a data structure to be built. For example, writing
`(cond ((numberp ,x) ,@y) (t (print ,x) ,@y))is roughly equivalent to writing
(list 'cond (cons (list 'numberp x) y) (list* 't (list 'print x) y))Where a comma occurs in the template, the following the comma is to be evaluated to produce an to be inserted at that point. Assume b has the value 3, for example, then evaluating the denoted by `(a b ,b ,(+ b 1) b) produces the result (a b 3 4 b).
If a comma is immediately followed by an , then the following the is evaluated to produce a of . These are then ``spliced'' into place in the template. For example, if x has the value (a b c), then
`(x ,x ,@x foo ,(cadr x) bar ,(cdr x) baz ,@(cdr x))=> (x (a b c) a b c foo b bar (b c) baz b c)The backquote syntax can be summarized formally as follows. * `basic is the same as 'basic, that is, (quote basic), for any basic that is not a or a general . * `,form is the same as form, for any form, provided that the representation of form does not begin with or . (A similar caveat holds for all occurrences of a form. after a .) * `,@form has undefined consequences. * `(x1 x2 x3 ... xn . atom) may be interpreted to mean
(append [ x1] [ x2] [ x3] ... [ xn] (quote atom))where the brackets are used to indicate a transformation of an xj as follows: -- [form] is interpreted as (list `form), which contains a backquoted form. that must then be further interpreted. -- [,form] is interpreted as (list form). -- [,@form] is interpreted as form. * `(x1 x2 x3 ... xn) may be interpreted to mean the same as the backquoted form `(x1 x2 x3 ... xn . ), thereby reducing it to the previous case. * `(x1 x2 x3 ... xn . ,form) may be interpreted to mean
(append [ x1] [ x2] [ x3] ... [ xn] form)where the brackets indicate a transformation of an xj as described above. * `(x1 x2 x3 ... xn . ,@form) has undefined consequences. * `#(x1 x2 x3 ... xn) may be interpreted to mean (apply #'vector `(x1 x2 x3 ... xn)).
Anywhere ``,@'' may be used, the syntax ``,.'' may be used instead to indicate that it is permissible to operate destructively on the produced by the form. following the ``,.'' (in effect, to use instead of ).
If the backquote syntax is nested, the innermost backquoted form. should be expanded first. This means that if several commas occur in a row, the leftmost one belongs to the innermost .
An is free to interpret a backquoted F1 as any F2 that, when evaluated, will produce a result that is the under as the result implied by the above definition, provided that the side-effect behavior. of the substitute F2 is also consistent with the description given above. The constructed copy of the template might or might not share 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) ')) (list c) d ')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))
Nested Backquote
This is what the Common Lisp HyperSpec says about nested :
If the backquote syntax is nested, the innermost backquoted form. should be expanded first. This means that if several commas occur in a row, the leftmost one belongs to the innermost backquote.
The R5RS Scheme spec also includes these details about :
Quasiquote forms may be nested. Substitutions are made only for unquoted components appearing at the same nesting level as the outermost backquote. The nesting level increases by one inside each successive quasiquotation, and decreases by one inside each unquotation.
Also keep in mind that only one backtick gets collapsed per evaluation, just like a regular quote, it's not recursive.
To see how these three details interact, let's expand your example a bit. This expression...
``(a ,,(+ 1 2) ,(+ 3 4))
Gets evaluated to this (in SBCL notation):
`(A ,3 ,(+ 3 4))
To get rid of the other backtick, another level of evaluation is needed:
(eval ``(a ,,(+ 1 2) ,(+ 3 4)))
Both backticks are gone, and we're left with a plain list:
(A 3 7)
参考:
http://stackoverflow.com/questions/7549550/using-two-backquotes-and-commas-common-lisp
进一步阅读:
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/133735/viewspace-742882/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/133735/viewspace-742882/