【发布时间】:2011-04-05 03:35:35
【问题描述】:
在范围方面?内存中的实际实现?语法?例如,if (let a 1) 'a' 是变量还是符号?
【问题讨论】:
-
clisp 是 Common Lisp 的一个特殊实现,你的问题是关于语言的,所以我冒昧地更改了你的标签。
标签: functional-programming lisp common-lisp symbols
在范围方面?内存中的实际实现?语法?例如,if (let a 1) 'a' 是变量还是符号?
【问题讨论】:
标签: functional-programming lisp common-lisp symbols
Jörg 的回答指出了正确的方向。让我补充一点。
我会谈谈类似于 Common Lisp 的 Lisp。
符号作为数据结构
符号是 Lisp 中的真实数据结构。您可以创建符号,可以使用符号,可以存储符号,可以传递符号,并且符号可以是更大数据结构的一部分,例如符号列表。符号有名称,可以有值,也可以有函数值。
所以你可以取一个符号并设置它的值。
(setf (symbol-value 'foo) 42)
通常会写成(setq foo 42)、(set 'foo 42) 或(setf foo 42)。
代码中表示变量的符号
但是!
(defun foo (a)
(setq a 42))
或
(let ((a 10))
(setq a 42))
在以上两种形式的源代码中都有符号,a 写成一个符号,使用函数READ 读取源代码会在某个列表中返回符号a。但是setq 操作不会将a 的符号值设置为42。这里LET 和DEFUN 引入了一个我们用符号编写的VARIABLE a。因此SETQ 操作会将变量值设置为42。
词法绑定
所以,如果我们看一下:
(defvar foo nil)
(defun bar (baz)
(setq foo 3)
(setq baz 3))
我们引入一个全局变量FOO。
在 bar 中,第一个 SETQ 设置全局变量 FOO 的符号值。第二个SETQ 将局部变量BAZ 设置为3。在这两种情况下,我们都使用相同的SETQ,并将变量写为符号,但在第一种情况下,FOO 捐赠了一个全局变量,这些变量将值存储在符号值中。在第二种情况下,BAZ 表示一个局部变量,该值是如何存储的,我们不知道。我们所能做的就是访问变量以获取其值。在 Common Lisp 中,无法获取符号 BAZ 并获取局部变量值。我们无法使用符号访问局部变量绑定及其值。这是 Common Lisp 中局部变量的词法绑定工作原理的一部分。
这导致例如观察,在没有记录调试信息的编译代码中,符号BAZ 消失了。它可以是处理器中的寄存器或以其他方式实现。 FOO 符号仍然存在,因为我们将其用作全局变量。
符号的各种用途
符号是一种数据类型,是 Lisp 中的一种数据结构。
变量是一个概念性的东西。全局变量基于符号。不是局部词法变量。
在源代码中,我们使用符号为函数、类和变量编写各种名称。
有一些概念上的重叠:
(defun foo (bar) (setq bar 'baz))
在上述 SOURCE 代码中,defun、foo、bar、setq 和 baz 都是符号。
DEFUN 是一个提供宏的符号。
FOO 是提供功能的符号。
SETQ 是提供特殊运算符的符号。
BAZ 是用作数据的符号。因此BAZ 之前的引用。
BAR 是一个变量。在编译代码中不再需要它的符号。
【讨论】:
symbol n. 一个object 的type symbol。
说明时间。
Lisp 所称的符号 与许多语言所称的变量相当接近。在第一个近似值中,符号具有值;当你计算表达式x时,表达式的值就是符号x的值;当您编写(setq x 3) 时,您为x 分配了一个新值。在 Lisp 术语中,(setq x 3) 将值 3 绑定到符号 x。
Lisp 的一个大多数语言不具备的特性是符号是普通对象(在编程语言术语中,符号是一等对象)。当您编写(setq x y) 时,x 的值将变为分配时y 的值。但是你可以写(setq x 'y),在这种情况下x的值就是符号y。
从概念上讲,有一个environment,它是一个从符号到值的关联表。评估一个符号意味着在当前环境中查找它。 (环境也是一流的对象,但这超出了此答案的范围。) binding 指的是环境中的特定条目。但是,还有一个复杂的问题。
大多数 Lisp 方言有多个命名空间,至少一个变量命名空间和一个函数命名空间。一个环境实际上可以包含一个符号的多个条目,每个命名空间一个条目。严格来说,变量是环境中变量命名空间中的条目。在日常的 Lisp 术语中,当您感兴趣的是将符号绑定为变量时,通常将其称为变量。
例如,在(setq a 1) 或(let ((a 1)) ...) 中,a 是一个符号。但由于这些构造作用于符号 a 的变量绑定,因此在此上下文中将 a 称为变量是很常见的。
另一方面,在(defun a (...) ...) 或(flet ((a (x) ...)) ...) 中,a 也是一个符号,但这些构造作用于其函数绑定,因此a 不会被视为变量。
在大多数情况下,当一个符号在表达式中出现不带引号时,它会通过查找其变量绑定来计算。主要的例外是在函数调用(foo arg1 arg2 ...) 中,使用了foo 的函数绑定。带引号的符号'x 或(quote x) 的值本身就是任何带引号的表达式。当然,还有很多特殊形式不需要引用符号,包括setq、let、flet、defun等。
【讨论】:
符号是事物的名称。 变量是指向可变存储位置的可变指针。
在您显示的代码 sn-p 中,let 和 a 都是符号。在let 块的范围内,符号a 表示当前绑定到值1 的变量。
但事物的名称并不是事物本身。符号a 不是变量。它是一个变量的名称。但是只在这个特定的上下文中。在不同的上下文中,名称a 可以指代完全不同的事物。
示例:符号jaguar 可以根据上下文表示
【讨论】:
Lisp 使用类似于映射(键 -> 值)的 环境,但具有用于链接环境和控制绑定的额外内置机制。
现在,symbols 几乎就是 keys(特殊形式的符号除外),并指向一个值,
即函数、整数、列表等
由于 Common Lisp 为您提供了一种更改值的方法,即在某些上下文中使用 setq 符号
(你的例子)也是变量。
【讨论】:
符号是一个 Lisp 数据对象。 Lisp 的“表单”是指要被评估的 Lisp 对象。当符号本身用作 Lisp 形式时,即当您评估符号时,结果是与该符号关联的值。值与符号相关联的方式是 Lisp 语言的深层部分。符号是否被声明为“特殊”会极大地改变评估的工作方式。
词法值由符号表示,但您不能自己将这些符号作为对象来操作。在我看来,用“指针”或“位置”来解释 Lisp 中的任何内容都不是最好的方法。
【讨论】:
为上述答案添加旁注:
Lisp 的新手通常不知道符号的确切用途,除了变量的名称。我认为最好的答案是它们就像枚举常量,只是你不必在使用它们之前声明它们。当然,正如其他人所解释的,它们也是对象。 (这对 Java 的用户来说应该不奇怪,其中枚举常量也是对象。)
【讨论】:
符号和变量是两个不同的东西。 就像在数学中的符号是一个值。变量的含义与数学中的含义相同。
但您的困惑来自于符号是变量的元表示这一事实。
如果你这样做了
(setq a 42)
你只需定义一个变量a。顺便说一句,普通 lisp 存储它的方式是抛出 符号结构。
通常的嘴唇符号是具有不同属性的结构。每一个都可以通过symbol-name、symbol-function等函数访问...
如果是变量,您可以通过 ssymbol-value 访问他的值
? (symbol-value 'a)
42
这不是获取 a 值的常见情况。
? a
42
请注意,符号是自我评估的,这意味着如果您询问符号,您会得到符号而不是 symbol-value
? 'a
A
【讨论】: