开发者

Should the behaviour of a function depend on the name of its variable?

Here is a short elisp code which shows that the behaviour of a function depends on the name of its variable开发者_高级运维. Is this a bug?

A function is declared using a variable x. When that function is called with a variable named anything other than x, it works as expected. But if it is called with a variable named x, it fails!

My system is GNU Emacs 22.2.1 (powerpc-apple-darwin8.11.0, Carbon Version 1.6.0) of 2008-04-05 on g5.tokyo.stp.isas.jaxa.jp


Paste this on an emacs buffer, put the cursor after the last parehthesis and press \C-x\C-e to see that the function make-zero does now work correctly when called the second time.

(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."))


It's not a bug so much as the nature of Elisp's (and Lisp in general) dynamic binding. ' doesn't pass a reference (that is, it's not like & in C/C++), it passes an unevaluated symbol; what it then evaluates to depends on the scope in which it's evaluated, which means it gets the x that's in scope inside the function.

In Lisp-think, the normal way around this would be to use a macro.

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

or

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


It's not a bug. It'd be worth your while reading the manual entry for Scoping Rules For Variable Bindings.

The function you wrote calls set, which takes a symbol (the value of the first argument) and changes its value to the value of the 2nd argument. The make-zero you wrote binds x locally to its input argument, so when you pass in the symbol x, set changes the first binding for x it finds, which happens to be the local binding.

Here's a different example, let's say you just had the following:

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

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

Looking at that snippet of code, does it make sense that the set line changes the local value of something?

It doesn't matter whether or not there's a global setting for the symbol something.

Another example is the following:

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

Which binding would you expect the set line to change? The one created by the let or the global one created by defvar?

The function you wrote is (pretty much) doing exactly the same thing as the let, you're creating a local binding for a variable which is seen before the global one.

If you want to pass around a reference to a variable then the only safe way to do that is via macros, which I recommend, but not until you've grasped the basics of lisp (b/c macros are definitely more complicated). That said, don't let me stop you from diving into macros if that's your passion.

A good introduction to programming Emacs lisp can be found here.

geekosaur's answer does a nice job of showing how you'd achieve what you want.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜