когда процитировать символ в Lisp в Emacs

Я начинаю изучать программирование с Emacs Lisp. Меня так смущает цитата из символов. Например:

(progn
  (setq a '(1 2))
  (prin1 a)
  (add-to-list 'a 3)
  (prin1 a)
  (setcar a 4)
  (prin1 a)
  (push 5 a)
  ""
)

почему функция " добавить в список "нуждается в кавычках в качестве первого аргумента, в то время как функция" setcar "и" push " не нуждаются в кавычках аргументов?

3 ответов


вот диаграмма, которая представляет символ a и его значение после (setq a '(1 2)). Поля-это элементарные структуры данных (символы и минусы), а стрелки-указатели (где часть данных ссылается на другую). (Я немного упрощаю.)

 symbol                     cons              cons
+-------+----------+       +------+------+   +------+------+
|name:  |variable: |       |car:  |cdr:  |   |car:  |cdr:  |
| a     |    |     |       | 1    |  |   |   | 2    | nil  |
+-------+----|-----+       +------+--|---+   +------+------+
             |             ​↑         |       ↑
             +-------------+         +-------+

выражение '(1 2) строит два минусов справа, которые составляют список из двух элементов. Выражение (setq a '(1 2)) создает символ a если он не существует, то делает свой "переменный слот" (часть, которая содержит значение символа) указывает на вновь созданный список. setq-это встроенный макрос, а (setq a '(1 2)) сокращенно от (set 'a '(1 2)). Первый аргумент set - это символ для изменения,а второй аргумент-значение для установки слота переменной символа.

(add-to-list 'a 3) эквивалентно (set 'a (cons 3 a)) здесь, потому что 3 нет в списке. Это выражение делает четыре вещи:--40-->

  1. создайте новую ячейку минусов.
  2. установите автомобиль новой ячейки минусов поле 3.
  3. установите поле cdr новой ячейки минусов в прежнее (и все еще текущее) значение a (т. е. скопировать содержимое aпеременный слот).
  4. установите переменный слот a к новой ячейке минусов.

после этого вызова структуры данных выглядят следующим образом:

 symbol                     cons              cons              cons
+-------+----------+       +------+--|---+   +------+------+   +------+------+
|name:  |variable: |       |car:  |cdr:  |   |car:  |cdr:  |   |car:  |cdr:  |
| a     |    |     |       | 3    |  |   |   | 1    |  |   |   | 2    | nil  |
+-------+----|-----+       +------+--|---+   +------+--|---+   +------+------+
             |             ​↑         |       ↑         |       ↑
             +-------------+         +-------+         +-------+

вызов setcar не создает никакой новой структуры данных и не действует на символ a но по его значению, которое минусы клетки чьи car в настоящее время содержит 3. После (setcar a 4) структуры данных выглядит следующим образом:

 symbol                     cons              cons              cons
+-------+----------+       +------+--|---+   +------+------+   +------+------+
|name:  |variable: |       |car:  |cdr:  |   |car:  |cdr:  |   |car:  |cdr:  |
| a     |    |     |       | 4    |  |   |   | 1    |  |   |   | 2    | nil  |
+-------+----|-----+       +------+--|---+   +------+--|---+   +------+------+
             |             ​↑         |       ↑         |       ↑
             +-------------+         +-------+         +-------+

push макрос; здесь (push 5 a) эквивалентно (set 'a (cons 5 a)).

setq и push макросы (setq - это "специальная форма", которая, насколько нам известно, означает макрос, определение которого встроено в интерпретатор и не предусмотрено в Lisp). Макросы получают свои аргументы не оцененными и могут выбрать, расширять их или нет. set, setcar и add-to-list являются функциями, которые получают свои аргументы. Оценка символа возвращает содержимое его слота переменной, например, после начального (setq a '(1 2)) значение символа a является ячейкой минусов, чья машина содержит 1.

если вы все еще смущены, я предлагаю экспериментировать с (setq b a) и увидеть для себя, какое из выражений изменить b когда вы действуете на a (те, которые действуют на символ a) и который не (те, которые действуют на значение символов a).


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

add-to-list выполняет мутацию на месте своего первого аргумента, поэтому ему нужен цитируемый символ.

push - это не функция, а макрос; поэтому он может принимать некотируемые аргументы без оценки. Встроенные формы, как setcar, также не имеют, что ограничение.


другие ответы, данные до сих пор уточнить использование quote и разница между функции, С одной стороны, и макрос и специальные формы С другой стороны.

однако, они не получают к другой части вопроса:почему и add-to-list как это? почему требует ли он, чтобы его первый аргумент был символ? Это отдельный вопрос от того, или нет, он оценивает аргумент. Это реальный вопрос за дизайном add-to-list.

можно представьте себе, это add-to-list оценил его args и ожидал, что значение первого arg будет списком, а затем добавил значение второго arg в этот список в качестве элемента и вернул результат (новый список или тот же список). Это позволит вам сделать (add-to-list foo 'shoe) добавить символ shoe в список, который является значением foo -- сказать (1 2 buckle) --, чтобы дать (1 2 buckle shoe).

дело в том, что такая функция было бы не очень полезно. Почему? Потому что значение списка не обязательно доступно. Переменная foo можно подумать, как способ доступа к нему - "ручка" или "указатель" на него. Но это не относится к списку, что функция возвращает. Этот возвращаемый список может состоять из новой структуры списка, и обычно нет ничего (без переменной), указывающего на этот список. Функция add-to-list никогда видит символ (переменную) foo -- он не может знать, что значение списка он получает в качестве первого аргумента обязан foo. Если add-to-list были разработаны таким образом, тогда вам все равно нужно будет назначить возвращаемый результат вашей переменной списка.

IOW,add-to-list оценивает его args, потому что это функция, но это мало что объясняет. Он ожидает символ как значение его первого arg. И он ожидает значение этой переменной (символ) списка. Он добавляет значение своего второго arg в список (возможно, изменяя структуру списка), и это устанавливает значение переменной это значение его первого arg в этом списке.

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

и да, другой способ пойти - использовать макрос или специальную форму, как в push. Это та же идея: push хочет символ как его второй arg. Разница в том, что push не оценивает свои args поэтому символу не нужно быть закавыченным. Но в обоих случаях (в Emacs Lisp) код должен получить символ для того чтобы установите значение расширенный список.