Как узнать, когда или когда не следует использовать одинарные кавычки перед именами переменных?

31

У меня есть ниже:

(setq some-variable "less")

Я смущен, почему я должен использовать одинарную кавычку с, boundpно не с bound-and-true-p.

Пример 1:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))

Результат:

"некоторая переменная меньше"

Пример 2а:

(when (bound-and-true-p some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Результат:

"некоторая переменная меньше"

Пример 2б:

(when (bound-and-true-p 'some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Результат:

и: Неверный аргумент типа: symbolp, (указать некоторую переменную)

Каушал Моди
источник
5
Стоит упомянуть, что setqозначает set quoted, и изначально был макрос, который расширился в (set 'some-variable "less"). В целом, Elisp не очень согласован в отношении аргументов, заключенных в кавычки, а не в кавычки, но любая функция (не макрос), которая должна взаимодействовать с переменной, а не со значением, получит свой аргумент в кавычках ( setqчто является основным исключением).
Шости
2
FWIW, bound-and-true-pэто глупый макрос. Вернее, его имя глупо. 99,99% времени, когда вы хотите это сделать, (and (boundp 'FOO) FOO)вы используете значениеFOO . Вы делаете это не просто для того, чтобы получить истинную ценность. (1) Макрос не нужен - код, который он заменяет, тривиален и мал. (2) Имя вводит в заблуждение - оно касается значения переменной, а не просто проверяет, является ли значение переменной nil.
Дрю

Ответы:

24

Краткий ответ

Если вы пытаетесь использовать саму переменную, используйте 'some-variable. Если вы пытаетесь использовать значение, хранящееся в переменной, используйте some-variable.

  • boundp использует символ, поэтому он будет смотреть на все, что может быть связано, включая функции. Он заботится только о том, есть ли соответствующий символ, а не о его значении.
  • bound-and-truep использует var и возвращает значение. В этом случае вам необходимо указать значение символа для функции. Если символ var не связан или значение равно nil, он вернет nil.

объяснение

Для определения вручную см. Руководство .

'и (quote ...)оба выполняют одну и ту же цель в emacs-lisp.

Цель этого состоит в том, чтобы передать неоцененную форму в окружающую среду, а не оценить ее.

В вашем примере предположим, что у нас было следующее выше

(setq some-variable "less") ;; Rather than just 't for clarity

Тогда оценка идет следующим образом:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))
;; ==> (boundp 'some-variable) ; 't
;; ==> some-variable is "less"

Тогда как без цитаты:

(when (boundp some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))
;; ==> (boundp "less") ; "less" is not a variable. -> Error

Lisp оценивает формы по мере их поступления, заключая в кавычки форму, которую вы запрещаете оценивать, чтобы передать фактическую переменную (или список, или имя функции).

Джонатан Лич-Пепин
источник
Благодарность! Ваш ответ заставил меня прыгнуть в источники обоих и помог мне найти решение. Я также обновил свой вопрос с четкими примерами.
Каушал Моди
6
Я рад, что это помогло оператору, но фактически не отвечает на вопрос (таким образом, полезно для других людей, имеющих тот же вопрос). Вы только объясняете, что символы должны быть заключены в кавычки, иначе они будут оценены. Вы не объясняете, почему это не так, bound-and-truepи вопрос заключался в том, почему я должен цитировать при использовании, boundpа не при использовании bound-and-truep.
tarsius
@tarsius Я фактически включаю различие между boundpтребованием символа (без оценки) и bound-and-truepнеобходимостью значения переменной в точках маркера (отредактировано после первоначального ответа)
Джонатан Лич-Пепин
Я думаю, что важно упомянуть, почему это так (макросы могут решить не оценивать), вместо того, чтобы просто упомянуть, что строка документации говорит об этом. Я думаю, что это очень хороший вопрос, и что boundp vs. bound-and-true-p является лишь примером. На самом деле вопрос сводится к желанию узнать о правилах оценки.
Tarsius
@ tarsius Разве правила оценки не объясняются этим ответом? По крайней мере, основные, включающие только цитату ... Ваш ответ определенно более полный, но он выходит далеко за рамки темы, не так ли?
Т. Веррон
17

Символ, который находится в нефункциональном положении, обрабатывается как имя переменной. 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может быть определена как функция и требует, чтобы аргумент был заключен в кавычки, но потому что он предназначен для случаев, когда вы заранее знаете, какая переменная, которую вы заботитесь о макросе, использовалась, чтобы избавить вас от необходимости вводить '.

Tarsius
источник
Удивительно хорошо ответил.
Чарльз Ричи
2

Из исходного кода boundp:

DEFUN ("boundp", Fboundp, Sboundp, 1, 1, 0,
      doc: /* Return t if SYMBOL's value is not void.
Note that if `lexical-binding' is in effect, this refers to the
global value outside of any lexical scope.  */)

boundpожидает symbolкак вход. 'some-variableявляется символом переменной some-variable.

Из исходного кода 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))

bound-and-true-pожидает variableкак вход.

Внутри bound-and-true-pмакроса, он получает символ, делая (quote ,var). Так что, если ввод some-variable, (quote ,var)приведет к 'some-variable.

Но когда я даю вход 'some-variableна bound-and-true-p, я получаю ошибку: and: Wrong type argument: symbolp, (quote some-variable)потому что макрос не ожидает символ ( 'some-variable) на входе.

Каушал Моди
источник
Просто хедз-ап. ''symbol имеет смысл. Значит (quote (quote symbol)). Причина, по которой вы получаете ошибку, состоит в том, что это недопустимый аргумент boundp.
Малабарба
@ Malabarba Спасибо. Я сделал исправление.
Каушал Моди