如何在Lisp中删除变量/表单?

23
在Python中,我们使用del语句来删除变量。

E.g:

a = 1
del a

这在Lisp中的等价物是什么?
(setq foo 1)
;; (del foo) ?

7
这似乎是一个“XY问题”(指提出了解决方法而非实际问题的情况)。需要使用这种模式的情况应该很少。 - Sylwester
@Sylwester 不知道这是否是那种问题。但是当我在玩弄我的Emacs时,我已经感觉到需要这样的东西很长一段时间了。因此有了这个问题。Scrace,我不这么认为。 - Bleeding Fingers
1
如果你打算用elisp编写它,你应该将其标记为elisp。 - Will Ness
1
"没有所谓的“通用Lisp”。在纯函数式的Lisp中根本就不存在set!,更别说makunbound了。" - Will Ness
1
我想说的是,使用更多的标签。 :) 当最初发布时,使用“Lisp”和“Elisp”都是完全合适的。您提供的细节越多,例如在问题正文中提到Emacs,就越有助于更好地回答它。 :) - Will Ness
显示剩余2条评论
4个回答

31

使用Common Lisp。

对于符号作为变量:

CL-USER 7 > (setf foo 42)
42

CL-USER 8 > foo
42

CL-USER 9 > (makunbound 'foo)
FOO

CL-USER 10 > foo

Error: The variable FOO is unbound.

参见:

  • MAKUNBOUND(已定义)
  • SLOT-MAKUNBOUND(已定义)
  • FMAKUNBOUND(已定义)

1
我的Steel Bank Common Lisp不认识函数“makeunbound”。“(apropos 'makeunbound)”也没有任何结果……可能是因为这个辉煌函数的发明者键盘上的'e'键损坏了,所以将其命名为“makunbound”o.O - BitTickler
请注意,对动态变量进行makunbound操作并不能防止它与同名的词法变量产生干扰 - 这是坚持使用 *earmuffs* 约定的另一个原因,这样您就永远不会有相同名称的词法变量和动态变量。 - Mark Reed

13

Python的名称存储在命名空间中,del从命名空间中删除名称。Common Lisp具有不同的设计,更加有利于编译高效的代码。

在Common Lisp中,我们有两种“变量”类型。大多数情况下,词法变量占据了这些变量。词法变量类似于C语言中的局部变量。在运行时,词法变量通常被实现为一小段存储(例如栈)并且与其名称的关联仅保留用于调试目的。使用Python的名称空间存在最接近词法变量的类比是词法作用域,而这只是规范、编译器和求值器使用的抽象概念。

CL中第二个“变量”类型的“变量”是“全局”符号。符号是非常丰富的数据结构,比Python中的标签要丰富得多。它们可以具有许多信息,如值、打印名称、它们的“home”包、函数以及存储在属性列表中的其他任意信息。其中大多数是可选的。当您在源代码中使用名称时,例如(+ X 3),该名称X通常表示一个词法符号。但如果没有失败,编译器/求值器将假设您想要“全局”符号的值。也就是说,您实际上编写了(symbol-value'X),而不是X。由于打字错误、编程惯例和其他一些原因,几十年前的编译器开始抱怨在没有指示符号旨在成为“全局”符号的声明的情况下对“全局”符号的引用。这个声明被称为“special”。是的,这是一个愚蠢的命名约定。更糟糕的是,特殊变量不仅是全局的,它们还具有一个非常有用的功能,称为动态绑定——但这是另一个话题。

几乎总是使用defvar、defparameter或defconstant声明特殊的符号。有一个几乎强制性的编码约定,即它们要唯一拼写,例如*X*而不是X。一些编译器以及大多数开发人员将抱怨您违反该约定。

好的。现在我们可以回到del。特殊变量用符号表示;这类似于python中使用名称表示变量。在python中,名称在当前命名空间中查找。在Common Lisp中,它们在当前包中查找。但查找发生的时间不同。在python中,它在运行时完成,因为名称可以动态添加和删除。在Common Lisp中,名称在编译程序之前从文件中读取时进行查找。(有一些例外,但让我们避免考虑这些。)

您可以从包中删除符号(请参见unintern)。但这是一件罕见的事情,可能只会让您头疼。这是一个简单的操作,但在边缘处会变得混乱,因为包系统具有一些聪明的功能,虽然非常有帮助,但需要花费一些精力才能熟悉。因此,在某种意义上,对于全局符号,unintern是类似的操作。但如果您正在使用它,那么您可能正在做一些非常特殊的事情(并且很可能是错误的)。


对我来说,这是我读过的关于Lisp特性最清晰的解释之一。谢谢。 - gsl

4
尽管@Ben所写的是正确的,但我猜您要找的是makunbound,而不是unintern。对于Emacs Lisp,前者不会从obarray中删除符号,对于Common Lisp,则不会从包中删除。它只是删除其symbol-value,也就是其作为变量的值。如果您想要尝试获取变量值时出现未绑定(又称为空)错误的行为,则请尝试makunbound

1
我并不是与之前的答案相矛盾,而是在补充。请考虑Lisp具有垃圾回收功能。正如您所知,您可以通过多种方式定义符号:setf、defvar、defparameter、make-symbol等等。
但是,如何获得干净的系统呢?如何确保您不会错误地使用变量?例如,您定义了abc,然后后来决定不想使用abc,而是想要一个abc-1和一个abc-2。您希望Lisp系统在尝试使用abc时发出错误信号。如果您无法删除abc,那么系统将不会通过发出“未定义”错误来阻止您。
其他答案基本上告诉您Lisp没有提供一种摆脱abc的方法。您可以使用makunbound使符号成为新的,未分配任何值的符号。您还可以使用unintern使符号不在任何包中。但是boundp将告诉您该符号仍然存在。
所以,只要没有其他符号引用abc,垃圾回收最终会清除abc。例如,(setf pt-to-abc abc)。只要pt-to-abc仍然绑定到符号abc,即使进行了垃圾回收,abc仍将继续存在。
另一种真正摆脱abc的方法是关闭Lisp并重新启动它。看起来不太理想。但我认为关闭Lisp并重新开始是实际上摆脱所有符号的方法。然后您可以定义所需的符号。
通常您可能希望使用makunbound,因为这样系统会发出错误信号,如果您尝试将abc的值添加到其他内容中,因为abc将没有任何值。如果您尝试将abc附加到字符串,则系统将发出错误信号,因为abc没有字符串等。

1
你可以使用makunbound...而且你可以使用unintern...但是boundp会告诉你符号仍然存在。就elisp而言,情况绝对不是这样的。 - phils

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接