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。这里LETDEFUN 引入了一个我们用符号编写的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 代码中,defunfoobarsetqbaz 都是符号。

DEFUN 是一个提供宏的符号。 FOO 是提供功能的符号。 SETQ 是提供特殊运算符的符号。 BAZ 是用作数据的符号。因此BAZ 之前的引用。 BAR 是一个变量。在编译代码中不再需要它的符号。

【讨论】:

【参考方案2】:

引用Common Lisp HyperSpec:

symbol n. 一个objecttype symbol。

variable n.“变量”中的bindingnamespace

binding n. namename 所表示的内容之间的关联。 (…)

说明时间。

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) 的值本身就是任何带引号的表达式。当然,还有很多特殊形式不需要引用符号,包括setqletfletdefun等。

【讨论】:

【参考方案3】:

符号是事物的名称。 变量是指向可变存储位置的可变指针。

在您显示的代码 sn-p 中,leta 都是符号。在let 块的范围内,符号a 表示当前绑定到值1 的变量。

但事物的名称并不是事物本身。符号a 不是变量。它是一个变量的名称。但是在这个特定的上下文中。在不同的上下文中,名称a 可以指代完全不同的事物。

示例:符号jaguar 可以根据上下文表示

OSX 10.2 a gaming console a car manufacturer a ground attack military jet airplane another military jet airplane a supercomputer an electric guitar 和a whole lot of other things 哦,我forget something?

【讨论】:

+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-namesymbol-function等函数访问...

如果是变量,您可以通过 ssymbol-value 访问他的值

? (symbol-value 'a)
42

这不是获取 a 值的常见情况。

? a
42

请注意,符号是自我评估的,这意味着如果您询问符号,您会得到符号而不是 symbol-value

? 'a
A

【讨论】:

以上是关于LISP 中的变量和符号有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

common-lisp:绑定和符号之间的区别

Racket(lisp 编程语言)中的 [ ] 和 ( ) 括号有啥区别?

Struts 标签中的 # 、 % 和 $ 符号有啥区别?

C语言里符号常量和常变量有啥区别?

C语言里符号常量和常变量有啥区别?

package.json 中的波浪号(~)和插入符号(^)有啥区别?