LISP 中的变量和符号有啥区别?
Posted
技术标签:
【中文标题】LISP 中的变量和符号有啥区别?【英文标题】:What is the difference between a variable and a symbol in LISP?LISP 中的变量和符号有什么区别? 【发布时间】:2011-04-05 03:35:35 【问题描述】:在范围方面?内存中的实际实现?语法?例如,if (let a 1) 'a' 是变量还是符号?
【问题讨论】:
clisp 是 Common Lisp 的一个特殊实现,你的问题是关于语言的,所以我冒昧地更改了你的标签。 【参考方案1】: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
是一个变量。在编译代码中不再需要它的符号。
【讨论】:
【参考方案2】:引用Common Lisp HyperSpec:
symbol n. 一个object 的type symbol。
variable n.“变量”中的bindingnamespace。
binding n. name 与 name 所表示的内容之间的关联。 (…)
说明时间。
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
等。
【讨论】:
【参考方案3】:符号是事物的名称。 变量是指向可变存储位置的可变指针。
在您显示的代码 sn-p 中,let
和 a
都是符号。在let
块的范围内,符号a
表示当前绑定到值1
的变量。
但事物的名称并不是事物本身。符号a
不是变量。它是一个变量的名称。但是只在这个特定的上下文中。在不同的上下文中,名称a
可以指代完全不同的事物。
示例:符号jaguar
可以根据上下文表示
【讨论】:
+1 用于提及在不同上下文中表示不同事物的符号。这在尝试理解 x 和 #'x 和 'x 之间的区别时真的很有帮助。【参考方案4】:Lisp 使用类似于映射(键 -> 值)的 环境,但具有用于链接环境和控制绑定的额外内置机制。
现在,symbols 几乎就是 keys(特殊形式的符号除外),并指向一个值,
即函数、整数、列表等
由于 Common Lisp 为您提供了一种更改值的方法,即在某些上下文中使用 setq
符号
(你的例子)也是变量。
【讨论】:
【参考方案5】:符号是一个 Lisp 数据对象。 Lisp 的“表单”是指要被评估的 Lisp 对象。当符号本身用作 Lisp 形式时,即当您评估符号时,结果是与该符号关联的值。值与符号相关联的方式是 Lisp 语言的深层部分。符号是否被声明为“特殊”会极大地改变评估的工作方式。
词法值由符号表示,但您不能自己将这些符号作为对象来操作。在我看来,用“指针”或“位置”来解释 Lisp 中的任何内容都不是最好的方法。
【讨论】:
【参考方案6】:为上述答案添加旁注:
Lisp 的新手通常不知道符号的确切用途,除了变量的名称。我认为最好的答案是它们就像枚举常量,只是你不必在使用它们之前声明它们。当然,正如其他人所解释的,它们也是对象。 (这对 Java 的用户来说应该不奇怪,其中枚举常量也是对象。)
【讨论】:
有趣的答案【参考方案7】:符号和变量是两个不同的东西。 就像在数学中的符号是一个值。变量的含义与数学中的含义相同。
但您的困惑来自于符号是变量的元表示这一事实。
如果你这样做了
(setq a 42)
你只需定义一个变量a。顺便说一句,普通 lisp 存储它的方式是抛出 符号结构。
通常的嘴唇符号是具有不同属性的结构。每一个都可以通过symbol-name
、symbol-function
等函数访问...
如果是变量,您可以通过 ssymbol-value
访问他的值
? (symbol-value 'a)
42
这不是获取 a 值的常见情况。
? a
42
请注意,符号是自我评估的,这意味着如果您询问符号,您会得到符号而不是 symbol-value
? 'a
A
【讨论】:
以上是关于LISP 中的变量和符号有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章