在《Racket参考》中“(quote)”部分也有关于quote的文档。
quote表产生一个常数:
(quote datum)
datum的语法在技术上被指定为read函数解析为一个单个元素的任何内容。quote表的值是相同的值,其read将产生给定的datum。
datum可以是一个符号、一个布尔值、一个数值、一个(字符或字节)字符串、一个字符、一个关键字、一个空列表、一个包含更多类似值的点对(或列表),一个包含更多类似值的向量,一个包含更多类似值的哈希表,或者一个包含其它类似值的格子。
Examples:
> (quote apple) 'apple
> (quote #t) #t
> (quote 42) 42
> (quote "hello") "hello"
> (quote ()) '()
> (quote ((1 2 3) #("z" x) . the-end)) '((1 2 3) #("z" x) . the-end)
> (quote (1 2 . (3))) '(1 2 3)
正如上面最后的示例所示,datum不需要匹配一个值的格式化的打印表。一个datum不能作为一个以#<开始的打印呈现,所以不能是#<void>、#<undefined>或一个过程。
quote表很少用于一个datum,它是一个布尔值、数字或字符串本身,因为这些值的打印表已经可以用作常量。quote表更常用于符号和列表,当没有被引用时,它具有其它含义(标识、函数调用等等)。
一个表达式:
'datum
是以下内容的一个简写
(quote datum)
这个简写几乎总是用来代替quote。这个简写甚至应用于datum中,因此它可以生成一个包含quote的列表。
在《Racket参考》里的“(parse-quote)”提供有更多关于'简写的内容。
Examples:
> 'apple 'apple
> '"hello" "hello"
> '(1 2 3) '(1 2 3)
> (display '(you can 'me)) (you can (quote me))
在《Racket参考》中的“(quasiquote)”部分也有关于quasiquote的文档。
quasiquote表类似于quote:
(quasiquote datum)
然而,对出现在datum之中的每个(unquote expr),expr被求值以产生一个替代unquote子表的值。
Example:
> (quasiquote (1 2 (unquote (+ 1 2)) (unquote (- 5 1)))) '(1 2 3 4)
此表可用于编写根据特定模式建造列表的函数。
Examples:
> (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))))))))
甚至可以以编程方式方便地构造表达式。(当然,第9次就超出了10次,你应该使用一个《宏》来做这个(第10次是当你学习了一本像《PLAI》那样的教科书之后)。)
Examples:
> (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表必须出现在一个产生一个列表或一个向量的上下文里。顾名思义,这个结果列表被拼接到它自己使用的上下文中。
Example:
> (quasiquote (1 2 (unquote-splicing (list (+ 1 2) (- 5 1))) 5)) '(1 2 3 4 5)
使用拼接,我们可以修改上边我们的示例表达式的构造,以只需要一个单个的let表达式和一个单个的+表达式。
Examples:
> (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有效地取消unquote表和unquote-splicing表的一层,结果一个第二层unquote或unquote-splicing表被需要。
Examples:
> (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))))
上面的求值实际上不会像显示那样打印。相反,quasiquote和unquote的速记形式将被使用:`(即一个反引号)和,(即一个逗号)。同样的简写可在表达式中使用:
Example:
> `(1 2 `(,(+ 1 2) ,,(- 5 1))) '(1 2 `(,(+ 1 2) ,4))
unquote-splicing的简写形式是,@:
Example:
> `(1 2 ,@(list (+ 1 2) (- 5 1))) '(1 2 3 4)