报价和列表有什么区别?
我知道你可以使用' (aka quote ) '来创build一个列表,并且我一直使用它,就像这样: 
 > (car '(1 2 3)) 1 
但它并不总是像我所期望的那样工作。 例如,我尝试创build一个像这样的函数列表,但它不起作用:
 > (define math-fns '(+ - * /)) > (map (lambda (fn) (fn 1)) math-fns) application: not a procedure; expected a procedure that can be applied to arguments given: '+ 
 当我使用list ,它的工作原理: 
 > (define math-fns (list + - * /)) > (map (lambda (fn) (fn 1)) math-fns) '(1 -1 1 1) 
 为什么? 我以为'只是一个方便的速记,为什么这个行为不同? 
  TL; DR:他们不一样; 有疑问时使用list 。 
 经验法则:每当你想要评估参数时使用list ;  quote “分布”在其论据上,所以'(+ 1 2)就像(list '+ '1 '2) 。 你最终会在你的列表中有一个符号,而不是一个函数。 
 深入了解list和quote 
 在Scheme和Racket中, quote和list是完全不同的东西 ,但是由于它们都可以用来生成列表,所以混淆是常见的和可以理解的。 他们之间有一个非常重要的区别: list是一个普通的旧function ,而quote (即使没有特殊'语法)是一种特殊的forms 。 也就是说, list可以在普通的Scheme中实现,但quote不可以。 
  listfunction 
  list函数实际上是两者中较为简单的,所以我们从这里开始。 它是一个接受任意数量参数的函数,它将参数收集到列表中。 
 > (list 1 2 3) (1 2 3) 
 上面这个例子可能会让人困惑,因为结果被打印为一个可quote sexpression式,这是真的,在这种情况下,这两个语法是等价的。 但是,如果我们稍微复杂一点,你会发现它是不同的: 
 > (list 1 (+ 1 1) (+ 1 1 1)) (1 2 3) > '(1 (+ 1 1) (+ 1 1 1)) (1 (+ 1 1) (+ 1 1 1)) 
  quote示例中发生了什么? 那么,我们稍后会讨论这个问题,首先看一下list 。 它只是一个普通的函数,所以它遵循标准的Scheme评估语义:它在传递给函数之前对它的每个参数进行评估。 这意味着像(+ 1 1)这样的expression式在被收集到列表中之前将减less到2 。 
向列表函数提供variables时,这种行为也是可见的:
 > (define x 42) > (list x) (42) > '(x) (x) 
 有了list , x在得到传递给list之前就被评估了。  quote ,事情更复杂。 
 最后,因为list只是一个函数,所以它可以像其他任何函数一样使用,包括更高级的方法。 例如,它可以传递给mapfunction,它会适当地工作: 
 > (map list '(1 2 3) '(4 5 6)) ((1 4) (2 5) (3 6)) 
  quote单 
 与list不同,引用是Lisp的一个特殊部分。  quote表是特殊的,因为它得到了一个特殊的读者缩写, '但是它也是特殊的,即使没有。 与list不同, quote 不是一个函数,因此它不需要像一个行为一样 – 它有它自己的规则。 
Lisp源代码简要讨论
在Lisp中,Scheme和Racket是衍生产品,所有的代码实际上都是由普通的数据结构组成的。 例如,请考虑以下expression式:
 (+ 1 2) 
这个expression式实际上是一个列表 ,它有三个元素:
-   +符号
-  数字1
-  数字2
 所有这些值都是程序员可以创build的正常值。 创build1值是非常简单的,因为它是自己评估的:只需input1 。 但是符号和列表比较困难:默认情况下,源代码中的符号执行variables查找! 也就是说,符号不是自我评估的 : 
 > 1 1 > a a: undefined cannot reference undefined identifier 
事实certificate,虽然符号基本上只是string,事实上我们可以在它们之间进行转换:
 > (string->symbol "a") a 
 列表甚至超过符号,因为默认情况下,源代码中的列表调用一个函数! 做(+ 1 2)查看列表中的第一个元素, +符号,查找与其关联的函数,并用列表中的其余元素调用它。 
 有时候,你可能想要禁用这个“特殊”的行为。 您可能只想获得列表或获取符号,而不进行评估。 要做到这一点,你可以使用quote 。 
报价的含义
 考虑到这一切, quote含义非常明显:它只是“closures”它所包装expression式的特殊评估行为。 例如,请考虑quote一个符号: 
 > (quote a) a 
 同样,考虑quote一个列表: 
 > (quote (abc)) (abc) 
 不pipe你给出的是什么, 总是会把它吐在你身上。 不多不less。 这意味着如果你给它一个列表,没有一个子expression式将被评估 – 不要指望它们是! 如果您需要任何种类的评估,请使用list 。 
 现在,有人可能会问:如果quote符号或列表以外的内容会发生什么? 那么,答案是……什么都没有! 你只要把它拿回来。 
 > (quote 1) 1 > (quote "abcd") "abcd" 
 这是有道理的,因为quote仍然只是吐出你给的东西。 这就是为什么“文字”像数字和string有时在Lisp中被称为“自引用”的原因。 
 还有一件事:如果你quote一个包含quote的expression式会发生什么? 那就是,如果你“双quote ”呢? 
 > (quote (quote 3)) '3 
 那里发生了什么? 那么请记住, '其实只是quote一个直接的缩写,所以没有什么特别的事情发生! 实际上,如果你的Scheme有一个方法来在打印时禁用缩写,它将如下所示: 
 > (quote (quote 3)) (quote 3) 
 不要被特殊的quote所迷惑,就像(quote (+ 1)) ,这里的结果只是一个普通的旧名单。 事实上,我们可以从列表中获得第一个元素:你能猜到它会是什么吗? 
 > (car (quote (quote 3))) quote 
 如果你猜了3 ,你错了。 请记住, quote禁用所有评估 ,并且包含quote符号的expression式仍然只是一个普通的列表。 在REPL中玩这个,直到你感觉舒服为止。 
 > (quote (quote (quote 3))) ''3 (quote (1 2 (quote 3))) (1 2 '3) 
报价非常简单,但它可能会非常复杂,因为它往往不符合我们对传统评估模式的理解。 事实上,这很混乱, 因为它很简单:没有特殊情况,没有规则。 它只是返回你所给的,正如所述(因此名称“报价”)。
附录A:Quasiquotation
所以,如果报价完全禁用评估,有什么好处呢? 那么,除了提前列出所有已知string,符号或数字的列表之外,不要太多。 幸运的是, 准分类的概念提供了一种突破报价的方式,并回到了普通的评估中。
 基础是非常简单的:而不是使用quote ,使用quasiquote 。 通常情况下,这种方式与各种方式完全一样: 
 > (quasiquote 3) 3 > (quasiquote x) x > (quasiquote ((ab) (cd))) ((ab) (cd)) 
 什么使quasiquote特别是认识一个特别标志, unquote 。 无论在哪里出现unquote ,它都会被它所包含的任意expression式取代: 
 > (quasiquote (1 2 (+ 1 2))) (1 2 (+ 1 2)) > (quasiquote (1 2 (unquote (+ 1 2)))) (1 2 3) 
 这使您可以使用quasiquote来构build具有“洞”的模板,并用非quasiquote来填充。 这意味着可以在引用列表中实际包含variables的值: 
 > (define x 42) > (quasiquote (x is: (unquote x))) (x is: 42) 
 当然,使用quasiquote和unquote是相当冗长的,所以他们有自己的缩写,就像' 。 具体来说, quasiquote是(反向),而不是(逗号)。 用这些缩写,上面的例子更可口。 
 > `(x is: ,x) (x is: 42) 
 最后一点:quasiquote实际上可以用一个相当毛茸茸的macros在Racket中实现,而且是这样。 它扩大到list , cons ,当然, quote用法。 
 附录B:在计划中实施list和quote 
 实现list是非常简单的,因为“rest argument”语法是如何工作的。 这是你需要的一切: 
 (define (list . args) args) 
而已!
 相比之下, quote更难 – 事实上,这是不可能的! 这看起来完全可行,因为closures评估的想法听起来像macros一样。 然而,一个天真的尝试揭示了这个麻烦: 
 (define fake-quote (syntax-rules () ((_ arg) arg))) 
 我们只是采取arg和吐出来…但这是行不通的。 为什么不? 那么,我们macros观的结果将被评估,所有这一切都是徒劳的。 我们可以通过扩展到(list ...)和recursion引用元素来扩展某种类似于引用的内容,如下所示: 
 (define impostor-quote (syntax-rules () ((_ (a . b)) (cons (impostor-quote a) (impostor-quote b))) ((_ (e ...)) (list (impostor-quote e) ...)) ((_ x) x))) 
 不幸的是,虽然没有程序macros,但是我们不能在没有quote情况下处理符号。 我们可以用syntax-case来更近一些,但即使如此,我们也只能模仿quote的行为,而不能复制quote的行为。 
附录C:球拍打印公约
 当在Racket中的这个答案中尝试这些例子时,您可能会发现它们并不像预期的那样打印。 通常,他们可能会打印一个领先' ,如在这个例子中: 
 > (list 1 2 3) '(1 2 3) 
 这是因为默认情况下,Racket尽可能地将结果打印为expression式。 也就是说,您应该能够将结果input到REPL中并获取相同的值。 我个人觉得这个行为很好,但是当试图理解引用时会引起混淆,所以如果你想closures引用,可以调用(print-as-expression #f) ,或者把(print-as-expression #f)的打印样式改为“write”语言菜单。