Символ, который находится в нефункциональном положении, обрабатывается как имя переменной. In (function variable) functionнаходится в функции-положении (после открывающей скобки) и variableне находится. Если явно не заключенные в кавычки переменные заменены их значениями.
Если бы вы написали (boundp my-variable), это означало бы «это символ, который хранится в значении переменной, my-variableсвязанной как переменная», а не «это символ, my-variableсвязанный как переменная.
Так почему же bound-and-truepведет себя по-другому?
Это макрос, и нормальные (функциональные) правила оценки здесь не применяются, макросы могут сами решать, когда и когда их аргументы оцениваются. Что на самом деле делают макросы, так это как-то преобразовывают аргументы и возвращают результат в виде списка, который затем оценивается. Преобразование и окончательная оценка происходят в разное время, называемое временем макроразложения и временем оценки.
Вот как bound-and-true-pвыглядит определение :
(defmacro bound-and-true-p (var)
"Return the value of symbol VAR if it is bound, else nil."
`(and (boundp (quote ,var)) ,var))
При этом используются макросы чтения, которые отличаются от макросов lisp (подробнее об этом ниже). Чтобы не усложнять это далее, давайте не будем использовать какие-либо макросы чтения:
(defmacro bound-and-true-p (var)
"Return the value of symbol VAR if it is bound, else nil."
(list 'and (list 'boundp (list 'quote var)) var))
Если ты пишешь
(bound-and-true-p my-variable)
который сначала "переводится" на
(and (boundp 'my-variable) my-variable)
и затем это вычисляется, возвращая, nilесли my-variableэто не так boundpили иначе значение my-variable(которое, конечно, также может быть nil).
Вы могли заметить, что расширение не было
(and (boundp (quote my-variable)) my-variable)
как мы могли ожидать. quoteэто специальная форма, а не макрос или функция. Как и макросы, специальные формы могут делать что угодно со своими аргументами. Эта особая специальная форма просто возвращает свой аргумент, здесь символ, вместо значения переменной символа. Это единственная цель этой специальной формы: предотвращение оценки! Макросы не могут сделать это самостоятельно, они должны использовать quoteэто.
Так в чем дело '? Это макрос для чтения , который, как упоминалось выше, не совпадает с макросом LISP . В то время как макросы используются для преобразования кода / данных, макросы чтения используются ранее при чтении текста, чтобы преобразовать этот текст в код / данные.
'something
это короткая форма для
(quote something)
`в настоящем определении bound-and-true-pтакже используется макрос для чтения. Если он заключает в кавычки символ как в `symbolнем, это эквивалентно 'symbol, но когда используется для цитирования списка, так как `(foo bar ,baz)он ведет себя по-разному в тех формах, с которыми ставится префикс ,, оцениваются.
`(constant ,variable)
эквивалентно
(list (quote constant) variable))
Это должно ответить на вопрос, почему символы без кавычек иногда оцениваются (заменяются их значениями), а иногда нет; Макросы можно использовать quoteдля предотвращения оценки символа.
Но почему bound-and-true-pмакроса пока boundpнет? Мы должны быть в состоянии определить, связаны ли произвольные символы, которые не известны до времени выполнения, как символы. Это было бы невозможно, если boundpаргумент был автоматически указан.
bound-and-true-pиспользуется, чтобы определить, определена ли известная переменная и, если да, использовать ее значение. Это полезно, если библиотека имеет необязательную зависимость от сторонней библиотеки, как в:
(defun foo-get-value ()
(or (bound-and-true-p bar-value)
;; we have to calculate the value ourselves
(our own inefficient or otherwise undesirable variant)))
bound-and-true-pможет быть определена как функция и требует, чтобы аргумент был заключен в кавычки, но потому что он предназначен для случаев, когда вы заранее знаете, какая переменная, которую вы заботитесь о макросе, использовалась, чтобы избавить вас от необходимости вводить '.
setqозначаетset quoted, и изначально был макрос, который расширился в(set 'some-variable "less"). В целом, Elisp не очень согласован в отношении аргументов, заключенных в кавычки, а не в кавычки, но любая функция (не макрос), которая должна взаимодействовать с переменной, а не со значением, получит свой аргумент в кавычках (setqчто является основным исключением).