LISP中变量​​和符号之间有什么区别?

时间:2010-08-28 09:29:07

标签: functional-programming lisp common-lisp symbols

就范围而言?内存中的实际实现?语法?例如,if(let a 1)是'a'变量还是符号?

7 个答案:

答案 0 :(得分:20)

Jörg的答案指向了正确的方向。让我补充一点。

我将讨论与Common Lisp类似的Lisps。

符号作为数据结构

符号是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

在条形图中,第一个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是一个变量。在编译的代码中,不再需要其符号。

答案 1 :(得分:12)

引用Common Lisp HyperSpec

  

symbol n。 object type symbol

     

variable n。“变量”binding中的namespace

     

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,它是从符号到值的关联表。评估符号意味着在当前环境中查找它。 (环境也是一流的对象,但这超出了本答案的范围。)绑定是指环境中的特定条目。但是,还有一个复杂的问题。

大多数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等。

答案 2 :(得分:10)

符号是一个东西的名称。 变量是指向可变存储位置的可变指针。

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

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

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

答案 3 :(得分:4)

Lisp使用类似于map(key - > value)的环境,但使用额外的内置机制来链接环境和控制绑定。

现在,符号非常多,(特殊形式符号除外),并指向一个值,
功能整数列表等。
由于Common Lisp为您提供了一种更改值的方法,即使用setq,在某些上下文中使用符号 (您的示例)也是变量

答案 4 :(得分:3)

符号是Lisp数据对象。 Lisp“form”表示要评估的Lisp对象。当符号本身用作Lisp形式时,即当您评估符号时,结果是与该符号相关联的值。值与符号相关联的方式是Lisp语言的深层部分。该符号是否被声明为“特殊”或极大地改变了评估的工作方式。

词法值由符号表示,但您无法自己将这些符号作为对象进行操作。在我看来,用“指针”或“位置”来解释Lisp中的任何内容并不是最好的方法。

答案 5 :(得分:1)

符号和变量是两个不同的东西。 就像在数学符号中一样是一种价值。变量与数学中的含义相同。

但是你的困惑来自于符号是变量的元表示的事实。

如果你这样做

(setq a 42)

您只需定义一个变量a。顺便提一下,常见的lisp存储它的方式是抛出结构的符号

在常见的嘴唇中,符号是具有不同属性的结构。每个人都可以通过symbol-namesymbol-function ...

等功能进行访问

如果是变量,您可以通过s symbol-value

访问其值
? (symbol-value 'a)
42

这不是获取a。

值的常见情况
? a
42

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

? 'a
A

答案 6 :(得分:1)

在上述答案中添加旁注:

除了作为变量的名称之外,Lisp的新手通常不确定符号的确切含义。我认为最好的答案是它们就像枚举常量一样,除了你在使用它们之前不必声明它们。当然,正如其他人所解释的那样,它们也是对象。 (这对Java用户来说似乎并不奇怪,其中枚举常量也是对象。)