【问题标题】:How to delete variable/forms in Lisp?如何在 Lisp 中删除变量/表单?
【发布时间】:2014-01-20 10:40:48
【问题描述】:

在 Python 中,我们有用于删除变量的 del 语句。

例如:

a = 1
del a

这在 Lisp 中相当于什么?

(setq foo 1)
;; (del foo) ?

【问题讨论】:

  • 这看起来像XY problem。对这种模式的需求应该很少。
  • @Sylwester 不知道是不是那种问题。但是在摆弄我的 Emacs 时,我一直觉得需要这样的东西已经有一段时间了。因此这个问题。斯克雷斯,我不这么认为。
  • 如果您打算将其用于 elisp,则应该将其标记为这样。
  • 没有“一般的 Lisp”之类的东西。在纯函数式 Lisps 中根本没有 set!,更不用说 makunbound
  • 我想说的是,使用更多标签。 :) 最初发布时,“Lisp”和“Elisp”都完全适合使用。您可以提供的详细信息越多,例如通过在问题正文中提及 Emacs,它越有帮助,越能更好地回答它。 :)

标签: lisp elisp common-lisp


【解决方案1】:

在 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(定义)

【讨论】:

  • 我的 Steel Bank Common Lisp 不知道函数makeunbound(apropos 'makeunbound) 也一无所获......可能是因为这个光荣功能的发明者在他们的键盘上有一个损坏的'e'键并将其命名为makunboundo.O
  • 请注意,makunbound 动态变量并不能防止它干扰同名的词法变量 - 另一个坚持 *earmuffs* 约定的原因,所以你永远不会拥有 i> 同名的词汇和动力学。
【解决方案2】:

Python 名称驻留在命名空间中,del 从命名空间中删除名称。 Common Lisp 有一种不同的设计,它更倾向于编译高效的代码。

在普通的 lisp 中,我们有两种“变量”。词汇变量占其中的大部分。词法变量类似于 C 局部变量。在运行时,词法变量通常被实现为一点存储(比如说在堆栈上),并且与其名称的关联仅保留用于调试目的。在python使用的意义上谈论删除词法变量并没有任何意义,因为与词法变量存在的python命名空间最接近的类比是词法范围,而这纯粹是规范和编译器/评估器使用的抽象.

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

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

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

您可以从包中删除符号(请参阅 unintern)。但这是一件罕见的事情,很可能只会让你的大脑受伤。这是一个简单的操作,但它的边缘会让人感到困惑,因为包装系统有一些聪明的功能,虽然非常有用,但需要一些努力才能适应。因此,从某种意义上说,对您的问题的简短回答是,对于全局符号, unintern 是类似的操作。但是,如果您使用它,您可能会做一些非常特殊的事情(并且可能是错误的)。

【讨论】:

  • 恕我直言,这是我读过的关于 Lisp 功能的最清晰的解释之一。谢谢。
【解决方案3】:

虽然@Ben 写的是真的,但我猜你要找的是makunbound,而不是unintern。前者不会从 obarray(对于 Emacs Lisp)或包(对于 Common Lisp)中删除符号。它只是删除了它的symbol-value,即它的值作为一个变量。如果您希望尝试获取变量值的行为会导致未绑定(也称为 void)错误,请尝试 makunbound

【讨论】:

    【解决方案4】:

    我不是在反驳之前的答案,而是在补充。

    考虑一下 Lisp 有垃圾收集。如您所知,您可以通过多种方式定义符号:setf、defvar、defparameter、make-symbol, 以及许多其他方式。

    但是如何获得一个干净的系统呢?如何确保不会错误地使用变量?例如,您定义了 abc,后来又决定不想使用 abc。相反,您需要一个 abc-1 和一个 abc-2。如果您尝试使用 abc,您希望 Lisp 系统发出错误信号。如果您无法以某种方式擦除 abc,那么系统不会通过发出“未定义”错误信号来阻止您。

    其他答案基本上告诉你 Lisp 没有提供摆脱 abc 的方法。您可以使用 ma​​kunbound 以便符号是新鲜的,没有分配任何值。您可以使用 unintern 使符号不在任何包中。然而 boundp 会告诉你符号仍然存在。

    所以,我认为只要没有其他符号引用abc,垃圾收集最终会摆脱abc。例如,(setf pt-to-abc abc)。只要 pt-to-abc 仍然绑定到符号 abc,即使进行垃圾回收,abc 也会继续存在。

    真正摆脱 abc 的另一种方法是关闭 Lisp 并重新启动它。似乎不太理想。但我认为关闭 Lisp 并重新开始是真正摆脱所有符号的原因。然后你定义你想要的符号。

    您可能通常需要 ma​​kunbound,因为如果您尝试将 abc 的值添加到其他值,系统将发出错误信号,因为 abc em> 没有任何价值。如果您尝试将 abc 附加到字符串,系统将发出错误信号,因为 abc 没有字符串。等等。

    【讨论】:

    • "你可以使用 makunbound ...也可以使用 unintern ...但是 boundp 会告诉你符号仍然存在。" elisp 中绝对不是这种情况,FWIW。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 2015-07-12
    • 2017-06-03
    • 1970-01-01
    • 2021-11-02
    • 1970-01-01
    相关资源
    最近更新 更多