когда процитировать символ в 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-->
- создайте новую ячейку минусов.
- установите автомобиль новой ячейки минусов поле
3
. - установите поле cdr новой ячейки минусов в прежнее (и все еще текущее) значение
a
(т. е. скопировать содержимоеa
переменный слот). - установите переменный слот
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) код должен получить символ для того чтобы установите значение расширенный список.