正常函数中的反引号,无引号和非引号拼接

时间:2018-02-04 19:17:08

标签: list scheme common-lisp backquote

我仍然在理解宏,虽然我认为我理解“反引号”“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) "  ")))))))

我没有测试过。

我的问题是,

为什么在函数中这是必要的,用例是什么?

它和宏有什么不同?

3 个答案:

答案 0 :(得分:5)

quasiquote,unquote和unquote-splicing只是引用数据listcons组合的语法糖。想象一下:

`(,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*创建第一个值的列表,并将它们放在最后一个值的前面,这有用的应该是一个列表。

列表创建

创建列表的方法和风格有很多种。这两个:

  1. 使用嵌套函数,例如conslistlist*append,...当需要计算许多元素时,此功能特别有用。
  2. 使用反引号运算符列出模板,,@,.进行评估。当存在具有固定对象和几个要计算的对象的嵌套列表时,这尤其有用。
  3. 因此,当您考虑要填写的列表模板时,反引用的方法很有用。无论何时你想创建(嵌套)列表,这些列表基于具有常量结构(意味着对象和嵌套)的模板,那么这是一种方法。这不仅限于宏 - 它是构建列表的一般机制。

    您还可以将模板视为反转:

    1. 默认情况下评估函数,并且需要引用常量元素
    2. 反引用模板默认情况下不进行评估,可变元素需要不加引号
    3. 警告

      反引号表达本身并不需要是纯粹的列表。反引号表达式的内部表示是未定义的,实现实际上是不同的。

      矢量

      请注意,这也适用于矢量:

      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有两个辅助机制:

  1. 取消引用 - 表示(,)

  2. unquote-splicing - 表示(,@)

  3. 使用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)
    

    来源:我参加的编译课程The Common Lisp Cookbook - Macros and Backquote