Должно ли поведение функции зависеть от имени ее переменной?
Вот короткий код elisp, который показывает, что поведение функции зависит от имени ее переменной. Это ошибка?
Функция объявляется с использованием переменной x. Когда эта функция вызывается с переменной с именем, отличным от x, она работает как положено. Но если он вызывается с переменной с именем x, это не удастся!
Моя система - GNU Emacs 22.2.1 (powerpc-apple-darwin8.11.0, Carbon Version 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 ответа
Это не ошибка, а природа динамического связывания Элиспа (и Лиспа в целом). '
не передает ссылку (то есть &
в C/C++) он передает неоцененный символ; то, к чему это тогда оценивает, зависит от области, в которой это оценивается, что означает, что это получает x
это находится внутри области действия функции.
В Lisp-думать нормальным способом было бы использовать макрос.
(defmacro make-zero (x) (list 'set x 0))
или же
(require 'cl)
(defmacro make-zero (x) `(set ,x 0))
Это не ошибка. Это будет стоить того, чтобы вы прочли руководство для 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
вы создаете локальную привязку для переменной, которая видна перед глобальной.
Если вы хотите передать ссылку на переменную, то единственный безопасный способ сделать это - использовать макросы, которые я рекомендую, но не до тех пор, пока вы не освоите основы lisp (макросы b/c определенно более сложные). Тем не менее, не дай мне помешать тебе погрузиться в макросы, если это твоя страсть.
Хорошее введение в программирование Emacs lisp можно найти здесь.
Ответ geekosaur отлично показывает, как вы добьетесь того, чего хотите.