Common Lisp, ссылка на значение и фактическое значение - PullRequest
1 голос
/ 02 мая 2011

Рассмотрим этот фрагмент кода:

(defvar lst '(1 1))

(defmacro get-x (x lst)
  `(nth ,x ,lst))

(defun get-y (y lst)
  (nth y lst))

Теперь давайте предположим, что я хочу изменить значение элементов списка с именем lst , car с get-x и cdr с get-y .Когда я пытаюсь изменить значение с помощью get-x setf ), все идет хорошо, но если я пытаюсь это сделать с помощью get-y , это сигнализирует об ошибке (сокращено):

;поймали СТИЛЬ ПРЕДУПРЕЖДЕНИЕ:;undefined функция: (SETF GET-STUFF)

Почему это происходит?

Я сам подозреваю, что это происходит, потому что макрос просто расширяется, а функция nth просто возвращаетссылка на значение элемента в списке, а функция с другой стороны оценивает вызов функции в nth и возвращает значение ссылочного значения (звучит странно).

Я прав в своих подозрениях?Если я прав, то как узнать, что такое просто ссылка на значение и фактическое значение?

Ответы [ 2 ]

8 голосов
/ 02 мая 2011

Ошибка не возникает с версией макроса, потому что, как вы предполагали, выражение (setf (get-x some-x some-list) some-value) будет расширено (во время компиляции) до чего-то вроде (setf (nth some-x some-list) some-value) (не совсем, но - деталиsetf -развитие сложное), и компилятор знает, как с этим справиться (т. Е. Для функции nth определен подходящий setf расширитель.

Однако вв случае get-y компилятор не имеет расширителя setf, если вы его не предоставите.Самый простой способ сделать это -

(defun (setf get-y) (new-value x ls)    ; Note the function's name: setf get-y 
    (setf (nth x ls) new-value))

Обратите внимание, что есть несколько соглашений относительно setf -экспандеров:

  1. Новое значение всегда указывается как первоеаргумент функции setf
  2. Все функции setf должны возвращать новое значение в качестве результата (то есть то, что должна возвращать вся форма setf)

Между прочим, в Common Lisp не существует такого понятия, как «ссылка» (по крайней мере, не в смысле C ++), хотя когда-то существовали диалекты Lisp, которые имели локативов .Обобщенные формы мест (т. Е. setf и его механизмы) работают совсем не так, как обычные ссылки в стиле C ++.Смотрите CLHS, если вам интересно узнать подробности.

4 голосов
/ 02 мая 2011

SETF - это макрос.

Идея состоит в том, что установка и чтение элементов из структур данных - это две операции, но обычно требуется два разных имени (или, может быть, даже что-то более сложное).SETF теперь позволяет использовать только одно имя для обоих:

(get-something x)

Выше считывается структура данных.Тогда обратное просто:

(setf (get-something x) :foobar)

Приведенный выше устанавливает структуру данных в X с помощью: FOOBAR.

SETF не рассматривает (получить что-то x) как ссылку или что-то в этом роде.Он просто имеет базу данных обратных операций для каждой операции.Если вы используете GET-SOMETHING, он знает, что такое обратная операция.

Откуда это знает SETF?Просто: вы должны сказать это.

Для операции NTH, SETF знает, как установить n-й элемент.Это встроено в Common Lisp.

Для вашей собственной операции GET-Y в SETF такой информации нет.Вы должны сказать это.Посмотрите Common Lisp HyperSpec для примеров.Одним из примеров является использование DEFUN и (SETF GET-Y) в качестве имени функции.

Также обратите внимание на следующие проблемы стиля в вашем примере:

  • lst не подходитимя для переменной DEFVAR.Используйте * list * в качестве имени, чтобы прояснить, что это специальная переменная, объявленная DEFVAR (или аналогичной ей).

  • '(1 2) - литеральная константа.Если вы пишете программу Common Lisp, последствия ее изменения не определены.Если вы хотите изменить список позже, вам следует добавить его в LIST или что-то вроде COPY-LIST.

...