Должно ли поведение функции зависеть от имени ее переменной? - PullRequest
1 голос
/ 25 марта 2011

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

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

Моя система - GNU Emacs 22.2.1 (powerpc-apple-darwin8.11.0, Carbon Version 1.6.0) от 2008-04-05 ong5.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 ]

2 голосов
/ 25 марта 2011

Это не ошибка. Было бы полезно прочитать руководство для Правила определения объема для привязок переменных .

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

Хорошее введение в программирование Emacs lisp можно найти здесь .

ответ geekosaur хорошо показывает, как вы достигнете того, чего хотите.

2 голосов
/ 25 марта 2011

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

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

или

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