【问题标题】:Is the X in (LET ((x ...) a fully fleshed symbol?(LET ((x ...) 中的 X 是一个完整的符号吗?
【发布时间】:2017-10-23 19:32:34
【问题描述】:

或者换句话说:CL中的变量是否有可能不是符号(部分)?

我想我可能对 CL 中的变量有一个深刻的误解。

我一直认为 CL 没有变量,只有符号,而符号(以及其他属性)具有名称和值单元格(即变量)。
当有人说 “变量 x 的值是 42”时,我认为它是 的缩写“名为 x 的符号的值单元存储值 42” .

但这可能是错误的。

当我输入时

> (let ((a 42))
       (type-of 'a))
SYMBOL
; caught STYLE-WARNING:
;   The variable A is defined but never used.

本例中的词法变量a 是一个完整的符号,其值单元格已设置为 42?

因为警告The variable A is defined but never used 另有说明,并且看起来词法变量与以下形式(type-of 'a) 中的符号a 不同。

【问题讨论】:

    标签: common-lisp


    【解决方案1】:

    Common Lisp 有两种对求值有特殊意义的数据类型:

    • cons cells / lists -> 用在 Lisp 源码中,列表是 Lisp 形式
    • 符号 -> 用作各种用途的名称

    如果你想在 Lisp 代码中使用它们作为数据,那么你必须引用它们。

    在 Lisp 源代码中都使用了两者,但一旦编译代码,它们可能会消失。

    变量在源代码中被写成符号。但在编译后的代码中,它们可能会消失 - 当它们是词法变量时。

    使用 SBCL 的示例:

    一个文件

    (defun test (foo)
      (+ foo foo))
    

    现在我们做:

    CL-USER> (proclaim '(optimize (debug 0)))  ; the compiler saves no debug info
    ; No value
    CL-USER> (compile-file "/tmp/test.lisp")
    ; compiling file "/private/tmp/test.lisp" (written 23 MAY 2017 09:06:51 PM):
    ; compiling (DEFUN TEST ...)
    
    ; /tmp/test.fasl written
    ; compilation finished in 0:00:00.013
    #P"/private/tmp/test.fasl"
    NIL
    NIL
    CL-USER> (find-symbol "FOO")
    FOO
    :INTERNAL
    

    编译器已读取源代码并创建了一个已编译的FASL 文件。我们看到符号 FOO 现在在当前包中。 FOO 为我们的源代码中的变量命名。

    现在退出 SBCL 并重新启动它。

    让我们加载机器码:

    CL-USER> (load "/tmp/test")
    T
    CL-USER> (find-symbol "FOO")
    NIL
    NIL
    

    不再有符号FOO。也不可能使用符号FOO 检索变量FOO 的词法值。没有从符号到词法值的映射(比如某种显式的词法环境)。

    【讨论】:

      【解决方案2】:

      值单元格用于动态(也称为“特殊”)变量,而不是词法变量。词法变量是源代码中的符号,但它们与符号没有任何运行时关系(调试器内部使用除外)。

      所以如果你写了:

      (let ((a 42))
        (declare (special a))
        (print (symbol-value 'a)))
      

      它会起作用,因为声明使它成为一个动态变量,然后您可以访问函数单元格中的值。

      【讨论】:

      • 那么这是否意味着defunlambdalet等创建的词法变量看起来是符号,但编译后实际上不是?
      • 它们只是源代码中的符号,就像函数调用是源代码中的列表一样。当它们被编译时,它们都变成了机器代码,而词法变量只是堆栈位置。
      【解决方案3】:

      您不是在检查绑定变量 a 的类型或其值,而是检查与 let 表单中的变量同名的文字常量符号:

      (let ((a 42))
        (type-of 'literal-symbol))
      ; ==> symbol (since 'literal-symbol evaluates to a symbol, just like 'a does)
      

      要检查绑定 a 的值的类型,您可以不使用文字引号:

      (let ((a 42))
        (type-of a))
      ; ==> (integer 0 281474976710655)
      

      在这里,您实际上检查了 let 绑定值的类型,它是一个整数。对42 是数字而不是符号感到惊讶?

      (let ((a 10) (b 'a))
        (list a b))
      ; ==> (10 a)
      

      变量a 和引用的文字'a 不一样。它们恰好在显示时看起来相同,但 'a 是数据,a 是代码。在 CL 中,编译器可能会在内部使用列表和符号,但它的执行完全取决于实现,并且在大多数实现中,它们会尽可能地堆栈分配,并且评估堆栈分配变量的代码将被替换为选择堆栈索引处的值。 CL 有一个disassemble 函数,如果您检查 SBCL 中的输出,您会发现它比原始 lisp 源代码更类似于 C 编译器的输出。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-04-08
        • 1970-01-01
        • 2013-11-03
        • 2021-07-16
        相关资源
        最近更新 更多