gpt4 book ai didi

scheme - 报价单和列表有什么区别?

转载 作者:行者123 更新时间:2023-12-03 08:35:03 26 4
gpt4 key购买 nike

我知道你可以使用 ' (又名 quote )来创建一个列表,我一直使用它,就像这样:

> (car '(1 2 3))
1

但它并不总是像我期望的那样工作。例如,我尝试创建一个函数列表,如下所示,但没有成功:
> (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

经验法则:每当您想要评估参数时,请使用 listquote “分布”在它的参数上,所以 '(+ 1 2) 就像 (list '+ '1 '2) 。您最终会在列表中得到一个符号,而不是一个函数。

深入了解 listquote
在 Scheme 和 Racket 中,quotelist 是完全不同的东西,但是由于它们都可以用于生成列表,因此混淆是常见且可以理解的。它们之间有一个非常重要的区别:list是一个普通的老功能,而quote(即使没有特殊'语法)是一个特殊形式。即 list 可以在普通 Scheme 中实现,但 quote 不能。
list 函数
list 函数实际上是两者中最简单的一个,所以让我们从那里开始。它是一个接受任意数量参数的函数,并将参数收集到一个列表中。

> (list 1 2 3)
(1 2 3)

上面的示例可能会令人困惑,因为结果打印为 quote 的 s 表达式,这是真的,在这种情况下,两种语法是等效的。但是如果我们稍微复杂一点,你会发现它是不同的:
> (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

向列表函数提供变量时,此行为也可见:
> (define x 42)
> (list x)
(42)
> '(x)
(x)

对于 listx 在传递给 list 之前被评估。使用 quote ,事情变得更加复杂。

最后,因为 list 只是一个函数,所以它可以像任何其他函数一样使用,包括以更高阶的方式使用。例如,它可以传递给 map 函数,它会正常工作:
> (map list '(1 2 3) '(4 5 6))
((1 4) (2 5) (3 6))
quote 形式

list 不同,引用是 Lisps 的一个特殊部分。 quote 形式的特殊部分是因为它有一个特殊的读取器缩写 ' ,但即使没有它,它也很特殊。与 list 不同, quote 不是函数,因此它不需要表现得像一个函数——它有自己的规则。

Lisp 源代码的简单讨论

在 Lisp 中,Scheme 和 Racket 是其衍生物,所有代码实际上都是由普通的数据结构组成的。例如,考虑以下表达式:
(+ 1 2)

该表达式实际上是一个列表,它包含三个元素:
  • + 符号
  • 数字 1
  • 数字 2

  • 所有这些值都是程序员可以创建的正常值。创建 1 值真的很容易,因为它会计算自身:您只需键入 1 。但是符号和列表更难:默认情况下,源代码中的符号会执行变量查找!也就是说,符号不是自我评估的:
    > 1
    1
    > a
    a: undefined
    cannot reference undefined identifier

    但事实证明,符号基本上只是字符串,实际上我们可以在它们之间进行转换:
    > (string->symbol "a")
    a

    列表比符号做的更多,因为默认情况下,源代码中的列表调用一个函数!执行 (+ 1 2) 查看列表中的第一个元素,即 + 符号,查找与其关联的函数,并使用列表中的其余元素调用它。

    但是,有时您可能希望禁用这种“特殊”行为。您可能只想获取列表或获取符号而不对其进行评估。为此,您可以使用 quote

    引文的意义

    考虑到所有这些,很明显 quote 做了什么:它只是“关闭”了它包装的表达式的特殊评估行为。例如,考虑 quote 一个符号:
    > (quote a)
    a

    类似地,考虑 quote 列表:
    > (quote (a b c))
    (a b c)

    不管你给什么 quote ,它总是会, 总是 吐出来给你。不多也不少。这意味着如果你给它一个列表,任何子表达式都不会被计算——不要指望它们会被计算!如果您需要任何类型的评估,请使用 list

    现在,有人可能会问:如果您 quote 不是符号或列表,会发生什么?嗯,答案是……没什么!你只要把它拿回来。
    > (quote 1)
    1
    > (quote "abcd")
    "abcd"

    这是有道理的,因为 quote 仍然只是准确地吐出你给它的东西。这就是为什么像数字和字符串这样的“文字”有时在 Lisp 的说法中被称为“自引用”。

    还有一件事:如果你 quote 一个包含 quote 的表达式会发生什么?也就是说,如果你“双 quote”呢?
    > (quote (quote 3))
    '3

    那里发生了什么?好吧,请记住 ' 实际上只是 quote 的直接缩写,所以根本没有发生什么特别的事情!事实上,如果你的 Scheme 有办法在打印时禁用缩写,它会是这样的:
    > (quote (quote 3))
    (quote 3)

    不要被 quote 的特殊性所迷惑:就像 (quote (+ 1)) 一样,这里的结果只是一个普通的旧列表。事实上,我们可以从列表中取出第一个元素:你能猜出它是什么吗?
    > (car (quote (quote 3)))
    quote

    如果你猜到了 3 ,那你就错了。请记住, quote 禁用所有评估,并且包含 quote 符号的表达式仍然只是一个简单的列表。在 REPL 中使用它,直到您对它感到满意为止。
    > (quote (quote (quote 3)))
    ''3
    (quote (1 2 (quote 3)))
    (1 2 '3)

    引用非常简单,但它可能会变得非常复杂,因为它往往会违背我们对传统评估模型的理解。事实上,它是因为它多么简单而令人困惑:没有特殊情况,没有规则。它只是准确地返回你给它的东西,正如所述(因此名称为“引用”)。

    附录 A:准报价

    因此,如果引用完全禁用评估,它有什么用?好吧,除了列出所有提前知道的字符串、符号或数字之外,没有多少。幸运的是,准引用的概念提供了一种打破引用并回到普通评估的方法。

    基础知识非常简单:不要使用 quote ,而是使用 quasiquote 。通常,这在各方面都与 quote 完全一样:
    > (quasiquote 3)
    3
    > (quasiquote x)
    x
    > (quasiquote ((a b) (c d)))
    ((a b) (c d))

    使 quasiquote 特殊的是它识别特殊符号 unquote 。只要 unquote 出现在列表中,就会被它包含的任意表达式替换:
    > (quasiquote (1 2 (+ 1 2)))
    (1 2 (+ 1 2))
    > (quasiquote (1 2 (unquote (+ 1 2))))
    (1 2 3)

    这使您可以使用 quasiquote 来构建具有“漏洞”的各种模板,这些模板将用 unquote 填充。这意味着可以在引用列表中实际包含变量的值:
    > (define x 42)
    > (quasiquote (x is: (unquote x)))
    (x is: 42)

    当然,使用 quasiquoteunquote 比较冗长,所以它们都有自己的缩写,就像 ' 一样。具体来说, quasiquote`(反引号), unquote,(逗号)。有了这些缩写,上面的例子就更可口了。
    > `(x is: ,x)
    (x is: 42)

    最后一点:quasiquote 实际上可以使用一个相当多毛的宏在 Racket 中实现,而且确实如此。它扩展到 listcons ,当然还有 quote 的用法。

    附录 B:在 Scheme 中实现 listquote
    实现 list 非常简单,因为“rest 参数”语法是如何工作的。这就是你所需要的:
    (define (list . args)
    args)

    而已!

    相比之下, quote要困难得多——事实上,这是不可能的!这似乎是完全可行的,因为禁用评估的想法听起来很像宏。然而一个天真的尝试暴露了问题:
    (define fake-quote
    (syntax-rules ()
    ((_ arg) arg)))

    我们只是取 arg 并把它吐出来……但这不起作用。为什么不?好吧,我们的宏的结果将被评估,所以一切都是徒劳的。通过扩展到 quote 并递归引用元素,我们可以扩展到类似于 (list ...) 的东西,如下所示:
    (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 的行为,而不是复制它。

    附录 C: Racket 打印惯例

    在 Racket 中尝试此答案中的示例时,您可能会发现它们不会按预期打印。通常,它们可能会打印前导 ' ,例如在此示例中:
    > (list 1 2 3)
    '(1 2 3)

    这是因为默认情况下,Racket 会在可能的情况下将结果打印为表达式。也就是说,您应该能够将结果输入到 REPL 中并获得相同的值。我个人觉得这种行为很好,但是在尝试理解引用时可能会令人困惑,因此如果您想将其关闭,请致电 (print-as-expression #f) ,或在 DrRacket 语言菜单中将打印样式更改为“写入”。

    关于scheme - 报价单和列表有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34984552/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com