Lisp 为什么这个全局变量只能设置两次,而不能设置更多?
我试图在elisp中创建一个简单的解析器,遇到了一个问题,我Lisp 为什么这个全局变量只能设置两次,而不能设置更多?,lisp,elisp,emacs24,Lisp,Elisp,Emacs24,我试图在elisp中创建一个简单的解析器,遇到了一个问题,我defvara global,然后我setq在其中添加了一个新值。这是第一次。但是,以后的setq每次都会失败 以下代码是问题的简化: (defvar buf '(BUF)) (defvar head nil) (defun pparse (seq) (defun status () (princ (format "Parse: %40s || %-20s\n" (prin1-to-string s
defvar
a global,然后我setq
在其中添加了一个新值。这是第一次。但是,以后的setq
每次都会失败
以下代码是问题的简化:
(defvar buf '(BUF))
(defvar head nil)
(defun pparse (seq)
(defun status ()
(princ (format "Parse: %40s || %-20s\n"
(prin1-to-string seq) (prin1-to-string buf))))
(while seq
(status)
(setq head (car seq))
(setq seq (cdr seq))
(cond ((equal "x" head)
(nconc buf (list head)))
((equal "," head)
(setq buf '(BUF))
;;(setcdr buf nil) <- fixes it but doesn't answer my question
)))
(status))
(pparse '("x" "," "x" "," "x" "," "x"))
正如您所看到的,第二列被剪裁了一次,但以后每次都会增长
如果取消注释setcdr
行,这将按预期工作(输出如下)。您甚至可以转储setq
。我理解为什么这会修复它,但不理解为什么最初的错误会发生
Parse: ("x" "," "x" "," "x" "," "x") || (BUF)
Parse: ("," "x" "," "x" "," "x") || (BUF "x")
Parse: ("x" "," "x" "," "x") || (BUF)
Parse: ("," "x" "," "x") || (BUF "x")
Parse: ("x" "," "x") || (BUF)
Parse: ("," "x") || (BUF "x")
Parse: ("x") || (BUF)
Parse: nil || (BUF "x")
顺便说一句,即使我关闭了词法范围,行为也是一样的。您不能改变文字数据,例如
'(BUF)
,并期望得到合理的结果。在您的代码中,ncoc
是一个变异操作
就你看到的行为的“为什么”而言,这是由于
(setq buf'(buf))
表达式造成的。每次都将它设置为同一个对象,因为它是一个文本数据,您不应该使用ncoc
之类的方法对其进行变异。如果您将其更改为(setq buf(list'buf))
,则每次都会生成一个新对象,并且您可以安全地ncoc
。您不能更改文本数据,例如”(buf)
,并期望得到正确的结果。在您的代码中,ncoc
是一个变异操作
就你看到的行为的“为什么”而言,这是由于
(setq buf'(buf))
表达式造成的。每次都将它设置为同一个对象,因为它是一个文本数据,您不应该使用ncoc
之类的方法对其进行变异。如果将其更改为(setq buf(list'buf))
,则每次都会生成一个新对象,并且您可以安全地ncoc
。这是因为数据标识在Elisp中的工作方式。如果将ncoc
替换为append
,您将与这些问题隔离开来。我强烈建议您一般不要使用ncoc
(以及像setcdr
这样的朋友),尤其是只要您对Elisp不太熟悉。这是因为数据标识在Elisp中的工作方式。如果将ncoc
替换为append
,您将与这些问题隔离开来。我强烈建议您一般不要使用ncoc
(以及像setcdr
这样的朋友),特别是只要您对Elisp不太熟悉。请注意defun
确实定义了全局函数,而不是局部函数。对于本地函数,可以使用cl-flet
或cl-labels
。可能重复的和其他相关的Q&A:注意defun
定义的是全局函数,而不是本地函数。对于本地函数,可以使用cl-flet
或cl-labels
。可能的重复和其他相关的问题与解答:使用反引号来构造列表是否也有效,因为它的全部用途是构造新的列表?@JoelMWard通过一些简单的测试,Emacs在这方面似乎遵循与Scheme相同的规则,因此,我将引用R7RS对它的描述:对于在表达式求值期间在运行时构造的任何结构,Quasikote表达式可能返回新分配的可变对象或文本结构。不需要重建的部分总是文字。(强调我的。)@JoelMWard换句话说,除非你知道准量子表达式是如何构建的,否则最好假设它也是不可变的。但如果你知道自己在做什么,你就可以逍遥法外。:-)在上面的代码中,`(,'BUF)
是有效的(从我的测试中),但不是`(BUF)
。这似乎有点像C中的静态变量。你只需一次初始化,从那时起它们就一样了。当然,正如我们所看到的,这是依赖于实现的,而且作为一个程序员,可能非常难以跟踪。如果文字数据也是常量,这种错误可能会最小化,就像在C中一样。至少错误消息会为您指明正确的方向;所以你需要一种不同的语法来定义常量数据,在这一点上你需要记住使用这种语法,在这一点上你回到了你开始的地方--需要知道什么时候你可以,什么时候不能以正常的方式引用数据。使用反引号来构造列表也行吗,因为它的全部目的是构造新的列表?@JoelMWard通过一些简单的测试,Emacs在这方面似乎遵循与Scheme相同的规则,所以我将引用R7RS对它的说法:一个quasikote表达式可能返回新分配的,表达式求值期间在运行时构造的任何结构的可变对象或文字结构。不需要重建的部分总是文字。(强调我的。)@JoelMWard换句话说,除非你知道准量子表达式是如何构建的,否则最好假设它也是不可变的。但如果你知道自己在做什么,你就可以逍遥法外。:-)在上面的代码中,`(,'BUF)
是有效的(从我的测试中),但不是`(BUF)
。这似乎有点像C中的静态变量。你只需一次初始化,从那时起它们就一样了。当然,正如我们所看到的,这是依赖于实现的,而且作为一个程序员,可能非常难以跟踪。如果文字数据也是常量,这种错误可能会最小化,就像在C中一样。至少错误消息会为您指明正确的方向;因此,您需要使用不同的语法来定义常量数据,此时您需要记住
Parse: ("x" "," "x" "," "x" "," "x") || (BUF)
Parse: ("," "x" "," "x" "," "x") || (BUF "x")
Parse: ("x" "," "x" "," "x") || (BUF)
Parse: ("," "x" "," "x") || (BUF "x")
Parse: ("x" "," "x") || (BUF)
Parse: ("," "x") || (BUF "x")
Parse: ("x") || (BUF)
Parse: nil || (BUF "x")