когда цитировать символ в Emacs Lisp - PullRequest
12 голосов
/ 29 декабря 2011

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

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

почему для функции "add-to-list" нужен символ в кавычках в качестве первого аргумента, в то время как функциям "setcar" и "push" не нужна кавычка аргумента?

Ответы [ 3 ]

18 голосов
/ 29 декабря 2011

Вот диаграмма, которая представляет символ 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 нет в списке. Это выражение делает четыре вещи:

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

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

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

Вызов setcar не создает никакой новой структуры данных и не воздействует на символ a, но на его значение, которое является ячейкой cons, в которой 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 - это «специальная форма», что, как мы здесь говорим, означает макрос, определение которого встроено в интерпретатор и не предоставлено в Лиспе). Макросы получают свои аргументы без оценки и могут расширить их или нет. set, setcar и add-to-list - это функции, которые получают свои аргументы оцененными. Оценка символа возвращает содержимое его переменной ячейки, например, после начального (setq a '(1 2)) значение символа a - это ячейка cons, в которой содержится 1.

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

13 голосов
/ 29 декабря 2011

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

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

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

8 голосов
/ 30 декабря 2011

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

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

Можно представить , что add-to-list оценил его аргументы и ожидал, что значение первого аргумента будет списком, а затем добавил значение второго аргумента в этот список в качестве элемента и вернул результат (новый список или тот же список). Это позволило бы вам сделать (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 оценивает свои аргументы, потому что это функция, но это мало что объясняет. Ожидается символ в качестве значения первого аргумента. И он ожидает, что значение этой переменной (символа) будет списком. Он добавляет значение своего второго аргумента в список (возможно, изменяя структуру списка) и устанавливает значение переменной , которая является значением ее первого аргумента, в этот список .

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

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

...