我仍然在理解宏,虽然我认为我理解“反引号”“unquote”和“unquote splicing”的基础知识,但我认为它们只在宏中使用/有用。
但是我从Rosetta代码(calender task)中看到了这个Common Lisp代码,
(defun month-strings (year month)
"Collect all of the strings that make up a calendar for a given
MONTH and YEAR."
`(,(date-calc:center (month-to-word month) (length *day-row*))
,*day-row*
;; We can assume that a month calendar will always fit into a 7 by 6 block
;; of values. This makes it easy to format the resulting strings.
,@ (let ((days (make-array (* 7 6) :initial-element nil)))
(loop :for i :from (date-calc:day-of-week year month 1)
:for day :from 1 :to (date-calc:days-in-month year month)
:do (setf (aref days i) day))
(loop :for i :from 0 :to 5
:collect
(format nil "~{~:[ ~;~2,d~]~^ ~}"
(loop :for day :across (subseq days (* i 7) (+ 7 (* i 7)))
:append (if day (list day day) (list day))))))))
这里在正常函数中使用back-quote,unquote和unquote splicing,它可以创建一个字符串列表。
虽然我不使用Scheme,但是Racket解决方案有类似的东西,
(define days
(let ([? (if (= mn 12) (λ(x y) y) (λ(x y) x))])
(round (/ (- (find-seconds 0 0 12 1 (? (+ 1 mn) 1) (? yr (+ 1 yr))) s)
60 60 24))))
(list* (~a mname #:width 20 #:align 'center) "Su Mo Tu We Th Fr Sa"
(map string-join
(nsplit 7 `(,@(make-list pfx " ")
,@(for/list ([d days])
(~a (+ d 1) #:width 2 #:align 'right))
,@(make-list (- 42 pfx days) " ")))))))
我没有测试过。
我的问题是,
为什么在函数中这是必要的,用例是什么?
它和宏有什么不同?
答案 0 :(得分:5)
quasiquote,unquote和unquote-splicing只是引用数据list
和cons
组合的语法糖。想象一下:
`(,a b c) ; == (cons a '(b c))
`(a b ,c) ; == (list 'a 'b c)
`(a b ,@c d) ; == (cons 'a (cons 'b (append c '(d))))
这些都是一些小小的例子,所以你可以想象右手边可能会变得疯狂复杂,但很高兴知道quasiquote魔法在需要的时候会产生新的缺点并保持文字在尾巴中。因此,使用nconc
的quasiquoted表达式在第一种情况下不起作用,但在第二种情况和第三种情况下,因为在这些情况下需要最后的缺点。
如果你有一个创建列表结构的函数,quasiquote会使代码更清晰,更简洁,因为表单看起来更像结果。它与宏没有什么不同,因为它们都创建了列表结构。宏与结果的不同之处不同。在函数中,返回值并在宏代码中替换。
您可以查看使用macroexpand
的宏:
(macroexpand '`(,a ,b ,@c))
; ==> (cons a (cons b c))
; ==> t
答案 1 :(得分:4)
<强>反引号强>
请参阅Backquote上的CLHS。
示例
该示例与此代码类似:
CL-USER 14 > (let ((a 1)
(b 2)
(c '(3 4 5)))
`(,a ; a gets evaluated and put in
,b ; b gets evaluated and put in
,@c)) ; c gets evaluated and spliced in
(1 2 3 4 5)
上述代码的效果类似于使用函数list*
。
CL-USER 15 > (let ((a 1)
(b 2)
(c '(3 4 5)))
(list* a b c))
(1 2 3 4 5)
您使用的版本主要是品味。
list*
创建第一个值的列表,并将它们放在最后一个值的前面,这有用的应该是一个列表。
列表创建
创建列表的方法和风格有很多种。这两个:
cons
,list
,list*
,append
,...当需要计算许多元素时,此功能特别有用。,
,,@
,,.
进行评估。当存在具有固定对象和几个要计算的对象的嵌套列表时,这尤其有用。因此,当您考虑要填写的列表模板时,反引用的方法很有用。无论何时你想创建(嵌套)列表,这些列表基于具有常量结构(意味着对象和嵌套)的模板,那么这是一种方法。这不仅限于宏 - 它是构建列表的一般机制。
您还可以将模板视为反转:
警告强>
反引号表达本身并不需要是纯粹的列表。反引号表达式的内部表示是未定义的,实现实际上是不同的。
矢量
请注意,这也适用于矢量:
CL-USER 20 > (let ((a 1)
(b 2)
(c '(3 4 5)))
`#(,a
,b
,@c))
#(1 2 3 4 5)
答案 2 :(得分:2)
Quasi-Quote(QQ)是Scheme中的列表构造函数。
它比引用(&#39;),列表或缺点更灵活,因为它允许将符号与表达式评估混合。
QQ有两个辅助机制:
取消引用 - 表示(,)
unquote-splicing - 表示(,@)
使用QQ时,会启动报价上下文。 Unquote允许我们暂时逃避引用上下文并在unquote之后立即评估表达式。 Unquote-sclicing既可以从引用上下文中转义整个列表,也可以“解包”列表。 考虑:
(define b 5)
(define s (list 1 2))
请注意以下表达式的差异:Quote,Quasi-Quote。
输入:
'(a b c)
`(a b c)
输出
> (a b c)
> (a b c)
输入
'(a ,b c)
`(a ,b c)
输出
> (a ,b c)
> (a 5 c)
输入
'(a ,s c)
`(a ,s c)
输出
> (a ,s c)
> (a (1 2) c)
输入
'(a ,@s c)
`(a ,@s c)
输出
> (a ,@s c)
> (a 1 2 c)