4.11 准引述:quasiquote

+The Racket ReferenceQuasiquoting: quasiquote, unquote, and unquote-splicing一节中也提供了quasiquote的文档。

quasiquote 形式类似于 quote

(quasiquote datum)

然而,对于每一个出现在 datum 中的 (unquote expr)expr 都会被求值,所产生的值会在 unquote 子形式中占据对应的位置。

例如:
> (quasiquote (1 2 (unquote (+ 1 2)) (unquote (- 5 1))))

'(1 2 3 4)

此形式和用于编写根据具体模式来构建列表的函数。

例如:
> (define (deep n)
    (cond
      [(zero? n) 0]
      [else
       (quasiquote ((unquote n) (unquote (deep (- n 1)))))]))
> (deep 8)

'(8 (7 (6 (5 (4 (3 (2 (1 0))))))))

甚至还可以用很低的代价通过编程来构造表达式(当然,九成的情况下可行), 你可以使用来完成它(剩下的一成在你阅读像 PLAI 这样的书时可以做到)。

例如:
> (define (build-exp n)
    (add-lets n (make-sum n)))
> (define (add-lets n body)
    (cond
      [(zero? n) body]
      [else
       (quasiquote
        (let ([(unquote (n->var n)) (unquote n)])
          (unquote (add-lets (- n 1) body))))]))
> (define (make-sum n)
    (cond
      [(= n 1) (n->var 1)]
      [else
       (quasiquote (+ (unquote (n->var n))
                      (unquote (make-sum (- n 1)))))]))
> (define (n->var n) (string->symbol (format "x~a" n)))
> (build-exp 3)

'(let ((x3 3)) (let ((x2 2)) (let ((x1 1)) (+ x3 (+ x2 x1)))))

unquote-splicing 形式类似于 unquote,不过其 expr 必须产生一个列表,且 unquote-splicing 形式必须出现在产生列表或向量 的上下文中。顾名思义,其结果列表会被切分成它所使用的上下文。

例如:
> (quasiquote (1 2 (unquote-splicing (list (+ 1 2) (- 5 1))) 5))

'(1 2 3 4 5)

通过使用切分,我们只需一个 let 表达式和一个 + 就能修订前面 示例表达式的构造。

例如:
> (define (build-exp n)
    (add-lets
     n
     (quasiquote (+ (unquote-splicing
                     (build-list
                      n
                      (λ (x) (n->var (+ x 1)))))))))
> (define (add-lets n body)
    (quasiquote
     (let (unquote
           (build-list
            n
            (λ (n)
              (quasiquote
               [(unquote (n->var (+ n 1))) (unquote (+ n 1))]))))
       (unquote body))))
> (define (n->var n) (string->symbol (format "x~a" n)))
> (build-exp 3)

'(let ((x1 1) (x2 2) (x3 3)) (+ x1 x2 x3))

若某个 quasiquote 形式嵌套在另一个 quasiquote 形式中, 那么内部的 quasiquote 实际上会取消掉一层 unquoteunquote-splicing 形式,因此需要第二个 unquoteunquote-splicing

例如:
> (quasiquote (1 2 (quasiquote (unquote (+ 1 2)))))

'(1 2 (quasiquote (unquote (+ 1 2))))

> (quasiquote (1 2 (quasiquote (unquote (unquote (+ 1 2))))))

'(1 2 (quasiquote (unquote 3)))

> (quasiquote (1 2 (quasiquote ((unquote (+ 1 2)) (unquote (unquote (- 5 1)))))))

'(1 2 (quasiquote ((unquote (+ 1 2)) (unquote 4))))

前面的求值实际上并不会打印成上面所示那样。而是会使用 quasiquoteunquote 的简写信息:`(即反引号)和 ,(即逗号)。同样的简写也可在表达式中使用:

例如:
> `(1 2 `(,(+ 1 2) ,,(- 5 1)))

'(1 2 `(,(+ 1 2) ,4))

unquote-splicing 的简写形式为 ,@

例如:
> `(1 2 ,@(list (+ 1 2) (- 5 1)))

'(1 2 3 4)