我仍在理解宏的过程中,尽管我认为我了解“反引号”,“取消引号”和“取消引用的拼接”的基本知识,但我认为它们仅在宏中使用/有用。
但是我从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))))))))
在这里,在常规函数中使用了反引号,取消引号和取消引号拼接,它可以创建字符串列表。
虽然我不使用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) " ")))))))
我没有测试。
我的问题是
为什么在功能中必须这样做,用例是什么?
它和宏有什么不同?
最佳答案
准引用,取消引用和取消引用拼接只是引用的基准,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))))
这些都是小的琐碎示例,因此您可以想象右侧可能变得非常复杂,但是很高兴知道准引用魔术会在需要时产生新的弊端,并保持字面量不变。因此,在第一种情况下不能使用准引用表达式的
nconc
,但在第二种情况和第三种情况下则无法使用,因为在这些情况下需要最后一个缺点。如果您具有创建列表结构的函数,则准引用将使代码更清晰明了,因为表单看起来更像结果。它和宏没有什么不同,因为两者都创建了列表结构。宏的结果不同。在函数中返回值,在宏代码中替换。
您可以在将宏与
macroexpand
一起使用后检查发生了什么:(macroexpand '`(,a ,b ,@c))
; ==> (cons a (cons b c))
; ==> t