2011-03-24 2 views
1

Вот короткий код elisp, который показывает, что поведение функции зависит от имени ее переменной. Это ошибка?Если поведение функции зависит от имени ее переменной?

Функция объявляется с использованием переменной x. Когда эта функция вызывается с переменной с именем, отличной от x, она работает так, как ожидалось. Но если он вызывается с переменной с именем x, он терпит неудачу!

Моя система GNU Emacs 22.2.1 (PowerPC-яблочно-darwin8.11.0, углеродистая версия 1.6.0) от 2008-04-05 на g5.tokyo.stp.isas.jaxa.jp


Вставьте это в буфер emacs, поместите курсор после последнего parehthesis и нажмите \ Cx \ Ce, чтобы увидеть, что функция 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

Это не ошибка, связанная с природой динамической привязки Elisp (и Lisp вообще). ' не передает ссылку (то есть, она не похожа на & в C/C++), она передает неоценимый символ; то, что он затем оценивает, зависит от области, в которой он оценивается, а это значит, что он получает x, который находится внутри области действия.

В Lisp-думаю, нормальный путь вокруг этого будет заключаться в использовании макроса.

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

или

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

Не нужно требовать cl.el для второго примера. Он должен работать так же хорошо без него. – Drew

2

Это не ошибка. Это было бы полезно, если бы вы читали ручную запись для Scoping Rules For Variable Bindings.

Функция, которую вы написали, вызывает set, которая принимает символ (значение первого аргумента) и изменяет его значение на значение второго аргумента. make-zero вы написали x локально на свой входной аргумент, поэтому, когда вы передаете символ 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 делает хорошую работу, показывающую, как вы достигнете того, чего хотите.

Смежные вопросы