函数的行为是否应该依赖于其变量的名称?

时间:2011-03-24 21:32:50

标签: elisp

这是一个简短的elisp代码,它显示函数的行为取决于其变量的名称。这是一个错误吗?

使用变量x声明函数。当使用名为x之外的任何变量调用该函数时,它按预期工作。但如果使用名为x的变量调用它,则会失败!

我的系统是gub.tokyo.stp.isas.jaxa.jp上的2008-04-05的GNU Emacs 22.2.1(powerpc-apple-darwin8.11.0,Carbon Version 1.6.0)


将其粘贴到emacs缓冲区,将光标放在最后一个parehthesis之后,然后按下\ C-x \ C-e,以便在第二次调用时看到函数make-zero现在正常工作。

(progn
  (defun make-zero (x) 
    "Simple function to make a variable zero."
    (set x 0))

  (setq x 10)

  (insert "\n Variable x is now equal to " (number-to-string x))

  (setq y 20)

  (insert "\n Variable y is now equal to " (number-to-string y))

  (insert "\n\n Let us apply make-zero to y")

  (make-zero 'y)

  (insert "\n Variable y is now equal to " (number-to-string y))

  (insert "\n\n Let us apply make-zero to x")

  (make-zero 'x)

  (insert "\n Variable x is now equal to " (number-to-string x))

  (insert "\n\n Why make-zero had no effect on x?  Is it because the name of the
variable in the definition of make-zero, namely 'x', is the same as the name of
the variable when make-zero was called?  If you change the name of the variable
in the definition of make-zero from x to z, this strange behaviour will
disappear.  This seems to be a bug in elisp."))

2 个答案:

答案 0 :(得分:2)

这不是一个错误,而是Elisp(以及一般的Lisp)动态绑定的本质。 '没有传递引用(也就是说,它不像C / C ++中的&),它传递了一个未经评估的符号;然后它评估的内容取决于它的评估范围,这意味着它获得了函数内范围的x

在Lisp-think中,通常的方法是使用宏。

(defmacro make-zero (x) (list 'set x 0))

(require 'cl)
(defmacro make-zero (x) `(set ,x 0))

答案 1 :(得分:2)

这不是一个错误。在阅读Scoping Rules For Variable Bindings的手册条目时,您是值得的。

您编写的函数调用set,它接受​​一个符号(第一个参数的值)并将其值更改为第二个参数的值。您编写的make-zerox本地绑定到其输入参数,因此当您传入符号x时,设置更改它找到的x的第一个绑定,这恰好发生在是当地的约束力。

这是一个不同的例子,让我们说你刚才有以下内容:

(defun print-something (something)
  (set 'something "NEW VALUE")
  (insert something))

(print-something "OLD")    ; inserts "NEW VALUE"

查看代码片段,set行更改something的本地值是否有意义?

是否存在符号something的全局设置并不重要。

另一个例子如下:

(defvar x "some global value") ;# could have used setq here
(let ((x "local binding"))
  (set 'x "new value"))

您希望set行改变哪种绑定?由let创建的或defvar创建的全局

您编写的函数(几乎)与let完全相同,您可以为全局变量创建一个本地绑定。

如果你想传递对变量的引用,那么唯一安全的方法就是通过macros,我建议,但是直到你掌握了lisp的基础知识(b / c宏肯定更复杂)。也就是说,如果这是你的热情,不要让我阻止你潜入宏中。

可以找到编程Emacs lisp的一个很好的介绍here

geekosaur's answer很好地展示了你如何实现自己想要的目标。