Lisp 为什么这个全局变量只能设置两次,而不能设置更多?

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

我试图在elisp中创建一个简单的解析器,遇到了一个问题,我
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")