如何在Scheme中取消定义变量?

How to undefine a variable in Scheme?

提问人:eonil 提问时间:8/15/2010 最后编辑:Brian Tompsett - 汤莱恩eonil 更新时间:11/16/2023 访问量:5457

问:

如何在Scheme中取消定义变量?这可能吗?

变量 方案 未定义

评论

0赞 mathk 8/17/2010
您能告诉我们您为什么需要它或您的意图是什么吗?
0赞 eonil 8/17/2010
我是 Scheme 或函数式语言的新手。我把环境想象成一个哈希表,我担心某种内存管理。我认为在 GC 下释放它的内存需要明确的取消定义。现在我有些困惑。我正在尝试了解 Scheme 的较低级别行为。
0赞 eonil 8/17/2010
当然,很明显 Scheme 不需要释放内存,但我想知道细节。
0赞 Joel J. Adamson 8/17/2010
为什么要这样做?你想达到什么目的?你能发布有用的示例代码吗?
2赞 eonil 8/18/2010
哦,伙计们,请不要对我的问题感到愤怒。我只是作为一个新手问了一个问题,现在我知道这是一个愚蠢的问题:)我以 C 的方式思考,但我意识到这在 Scheme 上不是一个好主意。

答:

1赞 0xYUANTI 8/15/2010 #1

不能在标准 Scheme 中取消绑定变量。你可以设置!变量设置为“未定义”,我猜,或者你可以写一个元解释器来重新定义环境,允许你引入你自己的未定义变量的概念。

6赞 Greg 8/15/2010 #2

在 Scheme 中,变量是用 lambda 或各种 let 之一定义的。如果您希望其中一个是“未定义”的,那么您需要做的就是离开它们所在的范围。当然,这并不是真的取消定义它们,只是变量不再绑定到它以前的定义。

如果你使用(define)进行顶级定义,那么从技术上讲,你正在定义一个函数。由于 Scheme 是函数式的,因此函数永远不会真正消失。我想从技术上讲,它存储在某个地方的某种环境函数中,所以如果你非常熟悉你的实现(并且它没有以某种方式受到保护),你可能会用你自己对 globabl 环境的定义覆盖它。除此之外,我想说你最好的选择是重新定义函数以返回空列表——这真的是空的。

评论

1赞 Nathan Shively-Sanders 8/16/2010
你也可以执行 (set! x (display 'ignored)) 因为返回 “an unspecified value”。display
1赞 Anthony 8/17/2010
您可以在 Scheme 的顶层定义非函数值,并且本地绑定的函数确实会“消失”。
0赞 eonil 8/18/2010
谢谢你的回答。我意识到我的问题有问题:)我意识到“define”在顶级范围内定义了一些东西,所以它只是一直保留到范围结束(程序退出)
2赞 Joel J. Adamson 8/17/2010 #3
(set! no-longer-needed #f)

这是否达到了您想要的效果?您还可以在顶层使用定义。

guile> (define nigel "lead guitar")
guile> nigel
"lead guitar"
guile> (define nigel #f)
guile> nigel
#f
guile> 

然后,您可以重新设置变量。当然,这完全取决于变量的范围:参见 Greg 的回答define

评论

0赞 Greg 8/18/2010
给 Eonil 的注意事项:设置!破坏了引用的透明度,因为它引入了副作用,因此如果你想坚持纯粹的功能,就应该避免。
1赞 ceving 3/27/2017
将变量设置为 false 与取消定义它不同。
1赞 Brian Postow 8/17/2010 #4

我认为,如果你的观点是做相当于“免费”或取消分配的事情,那么不,你几乎不走运。您无法取消分配变量。你可以把它重新定义为一些小的东西,比如 #f,但是一旦你完成了(定义 foo 'bar),变量 foo 将以某种形式存在,直到你结束程序。

另一方面,如果你使用 let 或 letrec,当然,这个名字只存在到相关的关闭括号......

评论

0赞 eonil 8/18/2010
谢谢你的回答。我意识到这只是一个范围问题:)
10赞 Luis Casillas 12/13/2011 #5

你在这里触动了神经。Scheme 对顶级环境的工作方式没有非常明确的标准概念。为什么?因为 Scheme 标准代表了两组人之间的妥协,他们对 Scheme 应该如何运作有着截然不同的想法:

  • 解释性人群,他们看到您上面描述的顶级环境:一个运行时哈希表,随着程序解释的进行,绑定会逐步添加。
  • 然后是编译人群,他们认为顶级环境在编译时必须是完全可计算的(即,编译器必须能够最终识别将在顶级环境中绑定的所有名称)。

您的“如何取消定义变量”问题仅在第一个模型中才有意义。

请注意,解释模型(其中程序的顶级绑定取决于所采用的代码路径)使得 Scheme 代码的高效编译变得更加困难,原因有很多。例如,如果过程的名称是一个顶级绑定,它不仅可能在运行时发生变化,甚至可能消失在虚无中,那么 Scheme 编译器如何内联过程调用?

我坚定地站在编译阵营中,所以我建议你避免编写依赖于在运行时添加或删除顶级绑定的能力的代码,或者甚至需要使用顶级变量的代码(尽管这些通常是不可避免的)。一些 Scheme 系统(例如,Racket)能够生成相当好的编译代码,但如果你做出这些假设,你会在这方面绊倒它们。

评论

0赞 eonil 12/13/2011
哦,很好的解释。我可以将解释性与编译视为动态与静态(在顶层)吗?
1赞 Luis Casillas 12/13/2011
是的,基本上。为了实现高效编译,您希望所有顶级环境都是静态已知的。这意味着要知道存在于顶层的所有绑定,以及哪些绑定在程序中的任何位置都会发生突变。然后,任何不可变的顶级过程绑定都可以编译为直接函数调用,并且可以很容易地内联;对可能发生突变的顶级绑定的调用必须添加间接查找(因为绑定可能会在运行时更改),并且可能不容易内联。
0赞 FrankHB 7/24/2021
虽然这确实反映了一些计划用户的想法,但从技术上讲并非如此。更具体地说,这里是:在分析具有可能的重新绑定和未定义绑定(无论是顶级绑定还是非顶级绑定)的程序时,没有本质的困难。滥用环境突变不好的,但这在定义和未定义上是平等的,除非那些能够在不修改环境的情况下重写(即 家庭)。definelet
1赞 Jiří Vyšata 11/29/2014 #6

我认为你的问题并不愚蠢。在AutoLISP中,具有不存在的(未定义的)变量先验支持值“nil”(即使该变量在内存中不存在 - 这意味着 - 如果它不在变量表中 - 则该值为“nil” - “false”)。这也意味着错误。而且它也是空列表。如果你编写某种列表处理函数,只需通过以下方式进行初始测试就足够了:

(if input-list ....)

当您想要显式取消定义任何变量时,您可以这样做:

(setq old-var nil); or: (setq old-var ())

我喜欢。关键字“setq”的意思是“定义”。其他方言中的有界变量和无界变量哪个更好?你必须测试它们是否存在,如果它们是列表,你需要垃圾收集器,你不能取消定义变量来显式释放内存。如果未定义变量“my-list”,则无法编写以下命令:

(define my-list (cons 2 my-list))

因此,我认为 AutoLISP 方法更适合编程。我写的可能性,你可以在那里使用。遗憾的是,AutoLISP 只能在某些 CAD 工程图形系统中工作。

评论

0赞 FrankHB 7/24/2021
虽然这个问题不一定是愚蠢的,但 AutoLISP 中的设计是有争议的。就像JavaScript一样,普遍认为是有害的。现在所有主要的LISP方言都默认不使用动态作用域,除了AutoLISP和Emacs Lisp之外,它也很容易出错。nilundefined
3赞 soegaard 12/1/2014 #7

如其他答案所述,在 Scheme 中没有操作命名空间的标准方法。对于特定的实现,可能有一个解决方案。

在 Racket 中,顶级变量存储在命名空间中。您可以使用 删除变量。namespace-undefined-variable

无法删除局部变量。

http://docs.racket-lang.org/reference/Namespaces.html?q=namespace#%28def。%28%28报价。~23~25内核%29._namespace-未定义变量%21%29%29

4赞 ceving 3/27/2017 #8

方案 (R7RS) 没有符合标准的方法来删除顶级绑定。

如果计算不存在的变量,则会出现错误:

(eval 'a)
; => ERROR: undefined variable: a

如果定义它,则该变量将添加到顶级环境中。

(define a 1)
(eval 'a)
; => 1

从现在开始,无论你做什么,如果你访问变量,你都不会得到错误。

如果将其设置为 false,则将得到 false:

(set! a #f)
(eval 'a)
; => #f

即使将其设置为未指定的内容,也不太可能出现错误:

(set! a (if #f #t))
(eval 'a)
; =>

但是,某些方案可能具有删除顶级绑定的非标准方法。MIT Scheme 提供了 unbind-variable 函数。