问题描述
我知道您可以使用'
(又名quote
)创建列表,并且我一直在使用它,就像这样:
I know that you can use '
(aka quote
) to create a list, and I use this all the time, like this:
> (car '(1 2 3))
1
但是它并不总是能像我期望的那样工作.例如,我试图创建一个像这样的函数列表,但是没有用:
But it doesn’t always work like I’d expect. For example, I tried to create a list of functions, like this, but it didn’t work:
> (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
时,它会起作用:
When I use list
, it works:
> (define math-fns (list + - * /))
> (map (lambda (fn) (fn 1)) math-fns)
'(1 -1 1 1)
为什么?我以为'
只是一个方便的简写,所以行为为何不同?
Why? I thought '
was just a convenient shorthand, so why is the behavior different?
推荐答案
TL; DR:它们是不同的;如有疑问,请使用list
.
经验法则:每当您要对参数求值时,请使用list
; quote
通过其参数分布",因此'(+ 1 2)
类似于(list '+ '1 '2)
.您最终会在列表中看到一个符号,而不是一个函数.
TL;DR: They are different; use list
when in doubt.
A rule of thumb: use list
whenever you want the arguments to be evaluated; quote
"distributes" over its arguments, so '(+ 1 2)
is like (list '+ '1 '2)
. You’ll end up with a symbol in your list, not a function.
在Scheme和Racket中,quote
和list
是完全不同的,但是由于它们都可以用于生成列表,因此混淆是常见且可以理解的.它们之间有一个非常重要的区别:list
是普通的函数,而quote
(即使没有特殊的'
语法)也是特殊形式 .也就是说,list
可以用普通方案实现,而quote
则不能.
In Scheme and Racket, quote
and list
are entirely different things, but since both of them can be used to produce lists, confusion is common and understandable. There is an incredibly important difference between them: list
is a plain old function, while quote
(even without the special '
syntax) is a special form. That is, list
can be implemented in plain Scheme, but quote
cannot be.
list
函数实际上是两者中最简单的函数,所以让我们从这里开始.该函数接受任意数量的参数,并将参数收集到列表中.
The list
function is actually by far the simpler of the two, so let’s start there. It is a function that takes any number of arguments, and it collects the arguments into a list.
> (list 1 2 3)
(1 2 3)
上面的示例可能会造成混淆,因为结果打印为quote
able s表达式,并且的确如此,在这种情况下,这两种语法是等效的.但是,如果我们变得稍微复杂一点,您会发现它有所不同:
This above example can be confusing because the result is printed as a quote
able s-expression, and it’s true, in this case, the two syntaxes are equivalent. But if we get slightly more complicated, you’ll see that it is different:
> (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)
这样的表达式在被收集到列表之前将被简化为2
.
What’s going on in the quote
example? Well, we’ll discuss that in a moment, but first, take a look at list
. It’s just an ordinary function, so it follows standard Scheme evaluation semantics: it evaluates each of its arguments before they get passed to the function. This means that expressions like (+ 1 1)
will be reduced to 2
before they get collected into the list.
向列表函数提供变量时,此行为也可见:
This behavior is also visible when supplying variables to the list function:
> (define x 42)
> (list x)
(42)
> '(x)
(x)
对于list
,x
在传递给list
之前先经过评估.使用quote
,事情变得更加复杂.
With list
, the x
gets evaluated before getting passed to list
. With quote
, things are more complicated.
最后,由于list
只是一个函数,因此可以像使用任何其他函数一样使用它,包括以更高阶的方式使用.例如,可以将其传递给map
函数,并且可以正常工作:
Finally, because list
is just a function, it can be used just like any other function, including in higher-order ways. For example, it can be passed to the map
function, and it will work appropriately:
> (map list '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
quote
形式
与list
不同,报价是Lisps的特殊组成部分. quote
格式之所以很特殊,部分原因是它得到了特殊的读者缩写'
,但也也很特殊.与list
不同,quote
是不是一个函数,因此它不需要表现得像一个函数-它具有自己的规则.
The quote
form
Quotation, unlike list
, is a special part of Lisps. The quote
form is special in part because it gets a special reader abbreviation, '
, but it’s also special even without that. Unlike list
, quote
is not a function, and therefore it does not need to behave like one—it has rules of its own.
在Lisp中,Scheme和Racket是派生的,所有代码实际上都是由普通数据结构组成的.例如,考虑以下表达式:
In Lisp, of which Scheme and Racket are derivatives, all code is actually made up of ordinary data structures. For example, consider the following expression:
(+ 1 2)
该表达式实际上是一个列表,它包含三个元素:
That expression is actually a list, and it has three elements:
-
+
符号 - 电话号码
1
- 电话号码
2
- the
+
symbol - the number
1
- the number
2
所有这些值都是程序员可以创建的普通值.创建1
值真的很容易,因为它会自动求值:您只需键入1
.但是符号和列表更难:默认情况下,源代码中的符号会进行变量查找!也就是说,符号不是自我评估:
All of these values are normal values that can be created by the programmer. It’s really easy to create the 1
value because it evaluates to itself: you just type 1
. But symbols and lists are harder: by default, a symbol in the source code does a variable lookup! That is, symbols are not self-evaluating:
> 1
1
> a
a: undefined
cannot reference undefined identifier
事实证明,符号基本上只是字符串,实际上我们可以在它们之间进行转换:
As it turns out, though, symbols are basically just strings, and in fact we can convert between them:
> (string->symbol "a")
a
列表的作用甚至超过了符号,因为默认情况下,源代码中的列表会调用一个函数!进行(+ 1 2)
会查看列表中的第一个元素,即符号+
,查找与其关联的功能,并使用列表中的其余元素调用该功能.
Lists do even more than symbols, because by default, a list in the source code calls a function! Doing (+ 1 2)
looks at the first element in the list, the +
symbol, looks up the function associated with it, and invokes it with the rest of the elements in the list.
但是,有时您可能想禁用此特殊"行为.您可能只想获取列表或获取符号而不对其进行评估.为此,您可以使用quote
.
Sometimes, though, you might want to disable this "special" behavior. You might want to just get the list or get the symbol without it being evaluated. To do this, you can use quote
.
牢记所有这些,quote
的作用非常明显:它只是关闭"了它包装的表达式的特殊求值行为.例如,考虑quote
插入一个符号:
With all this in mind, it’s pretty obvious what quote
does: it just "turns off" the special evaluation behavior for the expression that it wraps. For example, consider quote
ing a symbol:
> (quote a)
a
类似地,考虑quote
列出列表:
Similarly, consider quote
ing a list:
> (quote (a b c))
(a b c)
无论您提供什么quote
,它都会总是 始终 吐出来给您.不多不少.这意味着,如果给它一个列表,则不会评估任何子表达式,不要期望它们会被评估!如果您需要任何评估,请使用list
.
No matter what you give quote
, it will always, always spit it back out at you. No more, no less. That means if you give it a list, none of the subexpressions will be evaluated—do not expect them to be! If you need evaluation of any kind, use list
.
现在,有人可能会问:如果quote
除了符号或列表以外的其他内容,会发生什么?好吧,答案是……什么都没有!你就把它拿回来.
Now, one might ask: what happens if you quote
something other than a symbol or a list? Well, the answer is... nothing! You just get it back.
> (quote 1)
1
> (quote "abcd")
"abcd"
这是有道理的,因为quote
仍然只是吐出您提供的内容.这就是为什么Lisp用语将数字和字符串之类的文字"有时称为自引用".
This makes sense, since quote
still just spits out exactly what you give it. This is why "literals" like numbers and strings are sometimes called "self-quoting" in Lisp parlance.
另一件事:如果quote
包含quote
的表达式会发生什么?也就是说,如果您加倍quote
"怎么办?
One more thing: what happens if you quote
an expression containing quote
? That is, what if you "double quote
"?
> (quote (quote 3))
'3
那里发生了什么?好吧,请记住,'
实际上只是quote
的直接缩写,因此根本没有发生什么特别的事情!实际上,如果您的Scheme有一种在打印时禁用缩写的方法,它将看起来像这样:
What happened there? Well, remember that '
is actually just a direct abbreviation for quote
, so nothing special happened at all! In fact, if your Scheme has a way to disable the abbreviations when printing, it will look like this:
> (quote (quote 3))
(quote 3)
不要被quote
的特殊之处所迷惑:就像(quote (+ 1))
一样,这里的结果只是一个普通的旧列表.实际上,我们可以从列表中删除第一个元素:您能猜出它将是什么吗?
Don’t be fooled by quote
being special: just like (quote (+ 1))
, the result here is just a plain old list. In fact, we can get the first element out of the list: can you guess what it will be?
> (car (quote (quote 3)))
quote
如果您猜到了3
,则说明您是错的.请记住,quote
禁用所有求值,并且包含quote
符号的表达式仍只是普通列表.在REPL中玩这个游戏,直到您对它感到满意为止.
If you guessed 3
, you are wrong. Remember, quote
disables all evaluation, and an expression containing a quote
symbol is still just a plain list. Play with this in the REPL until you are comfortable with it.
> (quote (quote (quote 3)))
''3
(quote (1 2 (quote 3)))
(1 2 '3)
报价非常简单,但是由于它可能违背我们对传统评估模型的理解,因此可能变得非常复杂.实际上,它的简单程度令人困惑,因为它们没有这么简单:没有特殊情况,也没有规则.它只是完全按照您所说的那样返回您给出的内容(因此称为引号").
Quotation is incredibly simple, but it can come off as very complex because of how it tends to defy our understanding of the traditional evaluation model. In fact, it is confusing because of how simple it is: there are no special cases, there are no rules. It just returns exactly what you give it, precisely as stated (hence the name "quotation").
因此,如果引用完全禁用评估,那么它有什么用?好吧,除了列出所有事先已知的字符串,符号或数字之外,还不多.幸运的是,准报价概念提供了一种突破报价并回到常规评估中的方法.
So if quotation completely disables evaluation, what is it good for? Well, aside from making lists of strings, symbols, or numbers that are all known ahead of time, not much. Fortunately, the concept of quasiquotation provides a way to break out of the quotation and go back into ordinary evaluation.
基础非常简单:使用quasiquote
代替使用quote
.通常,这在各个方面都与quote
完全相同:
The basics are super simple: instead of using quote
, use quasiquote
. Normally, this works exactly like quote
in every way:
> (quasiquote 3)
3
> (quasiquote x)
x
> (quasiquote ((a b) (c d)))
((a b) (c d))
使quasiquote
如此特殊的原因是可以识别特殊符号unquote
.列表中出现unquote
的任何地方,都将替换为它包含的任意表达式:
What makes quasiquote
special is that is recognizes a special symbol, unquote
. Wherever unquote
appears in the list, then it is replaced by the arbitrary expression it contains:
> (quasiquote (1 2 (+ 1 2)))
(1 2 (+ 1 2))
> (quasiquote (1 2 (unquote (+ 1 2))))
(1 2 3)
这使您可以使用quasiquote
来构建具有空洞"的模板,这些空洞需要用unquote
填充.这意味着可以将变量的值实际包含在带引号的列表内:
This lets you use quasiquote
to construct templates of sorts that have "holes" to be filled in with unquote
. This means it’s possible to actually include the values of variables inside of quoted lists:
> (define x 42)
> (quasiquote (x is: (unquote x)))
(x is: 42)
当然,使用quasiquote
和unquote
相当冗长,因此它们都有自己的缩写,就像'
一样.具体来说,quasiquote
是`
(反引号),unquote
是,
(逗号).有了这些缩写,上面的示例就更美味了.
Of course, using quasiquote
and unquote
is rather verbose, so they have abbreviations of their own, just like '
. Specifically, quasiquote
is `
(backtick) and unquote
is ,
(comma). With those abbreviations, the above example is much more palatable.
> `(x is: ,x)
(x is: 42)
最后一点:准报价实际上可以使用相当多毛的宏在Racket中实现,确实如此.它扩展到list
,cons
,当然还有quote
的用法.
One final point: quasiquote actually can be implemented in Racket using a rather hairy macro, and it is. It expands to usages of list
, cons
, and of course, quote
.
实现"list
"非常简单,因为"rest arguments"语法是如何工作的.这就是您所需要的:
Implementing list
is super simple because of how "rest argument" syntax works. This is all you need:
(define (list . args)
args)
就这样!
相反,quote
要难得多-实际上,这是不可能的!这似乎是完全可行的,因为禁用评估的想法听起来很像宏.然而,幼稚的尝试揭示了麻烦:
In contrast, quote
is a lot harder—in fact, it’s impossible! It would seem totally feasible, since the idea of disabling evaluation sounds a lot like macros. Yet a naïve attempt reveals the trouble:
(define fake-quote
(syntax-rules ()
((_ arg) arg)))
我们只是拿arg
并将其吐回去...但这是行不通的.为什么不?好了,我们的宏的结果将被评估,所以一切都是徒劳的.通过扩展到(list ...)
并递归引用元素,我们也许可以扩展到类似quote
之类的东西:
We just take arg
and spit it back out... but this doesn’t work. Why not? Well, the result of our macro will be evaluated, so all is for naught. We might be able to expand to something sort of like quote
by expanding to (list ...)
and recursively quoting the elements, like this:
(define impostor-quote
(syntax-rules ()
((_ (a . b)) (cons (impostor-quote a) (impostor-quote b)))
((_ (e ...)) (list (impostor-quote e) ...))
((_ x) x)))
但是,不幸的是,如果没有程序宏,没有quote
的情况下我们将无法处理符号.我们可以更接近地使用syntax-case
,但是即使那样,我们也只会模仿quote
的行为,而不是复制它.
Unfortunately, though, without procedural macros, we can’t handle symbols without quote
. We could get closer using syntax-case
, but even then, we would only be emulating quote
’s behavior, not replicating it.
在Racket中尝试此答案中的示例时,您可能会发现它们的打印效果不理想.通常,它们可能以前导'
进行打印,例如在以下示例中:
When trying the examples in this answer in Racket, you may find that they do not print as one would expect. Often, they may print with a leading '
, such as in this example:
> (list 1 2 3)
'(1 2 3)
这是因为默认情况下,Racket会将结果打印为表达式.也就是说,您应该能够将结果键入REPL并获得相同的值.我个人认为这种行为很好,但是在尝试理解报价时可能会造成混淆,因此,如果要关闭它,请调用(print-as-expression #f)
,或在DrRacket语言菜单中将打印样式更改为写".
This is because Racket, by default, prints results as expressions when possible. That is, you should be able to type the result into the REPL and get the same value back. I personally find this behavior nice, but it can be confusing when trying to understand quotation, so if you want to turn it off, call (print-as-expression #f)
, or change the printing style to "write" in the DrRacket language menu.
这篇关于quote和list有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!