Como saber quando ou quando não usar aspas simples antes dos nomes das variáveis?

31

Eu tenho o abaixo:

(setq some-variable "less")

Estou confuso por que tenho que usar aspas simples com boundpmas não com bound-and-true-p.

Exemplo 1:

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

Resultado:

"alguma variável é menor"

Exemplo 2a:

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

Resultado:

"alguma variável é menor"

Exemplo 2b:

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

Resultado:

e: Argumento de tipo incorreto: symbolp, (cite alguma variável)

Kaushal Modi
fonte
5
Vale ressaltar que setqsignifica set quoted, e originalmente foi uma macro que se expandiu para (set 'some-variable "less"). Em geral, o Elisp não é muito consistente com argumentos entre aspas e não entre aspas, mas qualquer função (não macro) que precise interagir com uma variável em vez de um valor terá seu argumento entre aspas ( setqsendo uma exceção importante).
Shosti
2
FWIW, bound-and-true-pé uma macro estúpida. Ou melhor, seu nome é estúpido. 99,99% do tempo em que você deseja (and (boundp 'FOO) FOO)fazê-lo para usar o valor deFOO . Você não faz isso apenas para obter um valor de verdade. (1) A macro não é necessária - o código que substitui é trivial e pequeno. (2) O nome é enganoso - trata-se do valor da variável, não apenas testando se o valor da variável é ou não nil.
Drew

Respostas:

24

Resposta curta

Se você estiver tentando usar a variável em si, use 'some-variable. Se você estiver tentando usar o valor armazenado na variável, use some-variable.

  • O boundp usa o símbolo para que ele procure qualquer coisa que possa ser vinculada, incluindo funções. Só se importa se existe um símbolo que corresponda, não qual é o valor.
  • bound-and-truep usa var e retorna o valor. Nesse caso, você precisa fornecer o valor do símbolo para a função. Se nenhum símbolo var estiver vinculado ou o valor for nulo, ele retornará nulo.

Explicação

Para a definição do manual, consulte o manual .

'e (quote ...)ambos executam o mesmo propósito no emacs-lisp.

O objetivo disso é passar a forma não avaliada para o ambiente circundante, em vez de avaliá-la.

No seu exemplo, suponha que tivéssemos o seguinte

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

Em seguida, a avaliação é a seguinte:

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

Considerando que sem uma citação:

(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

O Lisp avalia os formulários à medida que são alcançados, citando o formulário que você evita a avaliação para que a variável real (ou lista ou nome da função) seja passada.

Jonathan Leech-Pepin
fonte
Obrigado! Sua resposta me fez pular para as fontes de ambos e me ajudou a encontrar a solução. Também atualizei minha pergunta com exemplos claros.
Kaushal Modi
6
Fico feliz que isso tenha ajudado a operação, mas na verdade não responde à pergunta (de uma maneira útil para outras pessoas que têm a mesma pergunta). Você explica apenas que os símbolos precisam ser citados ou então são avaliados. Você não explica por que não é esse o caso bound-and-truepe a pergunta era por que eu preciso citar ao usar, boundpmas não ao usar bound-and-truep.
tarsius
@tarsius eu realmente incluir a distinção entre boundpa necessidade de um símbolo (não avaliada) e bound-and-truepprecisar o valor da variável nos pontos de bala (editado em após a resposta inicial)
Jonathan Sanguessuga-Pepin
Eu acho que é essencial mencionar o motivo (as macros podem optar por não avaliar) em vez de apenas mencionar que a sequência de documentos diz isso. Penso que esta é uma pergunta muito boa e que boundp vs. bound-and-true-p é apenas um exemplo. O que a questão realmente se resume é o desejo de aprender sobre regras de avaliação.
tarsius
@ tarso As regras de avaliação não são explicadas por esta resposta? Pelo menos os básicos, envolvendo apenas a citação ... Sua resposta é definitivamente mais completa, mas vai muito além do tópico, não é?
T. Verron
17

Um símbolo que está na posição de não função é tratado como o nome de uma variável. In (function variable) functionestá na posição da função (após o parêntese de abertura) e variablenão está. A menos que variáveis ​​citadas explicitamente sejam substituídas por seus valores.

Se você escrever, (boundp my-variable)isso significaria "é o símbolo armazenado no valor da variável my-variablevinculada como variável" e não "é o símbolo my-variablevinculado como variável.

Então, por que bound-and-truepse comporta de maneira diferente?

Essa é uma macro e as regras de avaliação normal (de função) não se aplicam aqui; as macros são livres para decidir se e quando seus argumentos serão avaliados. O que as macros realmente fazem é de alguma forma transformar os argumentos e retornar o resultado como uma lista, que é então avaliada. A transformação e a avaliação final ocorrem em momentos diferentes, chamados macroexpansão e tempo da avaliação.

É assim que a definição de se bound-and-true-pparece:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

Isso usa macros de leitor diferentes das macros lisp (mais sobre isso abaixo). Para não complicar ainda mais, não vamos usar macros de leitor:

(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))

Se você escrever

(bound-and-true-p my-variable)

que é primeiro "traduzido" para

(and (boundp 'my-variable) my-variable)

e então isso é avaliado, retornando nilse my-variablenão for boundpou então o valor de my-variable(que, é claro, também pode ser nil)


Você deve ter notado que a expansão não foi

(and (boundp (quote my-variable)) my-variable)

como poderíamos esperar. quoteé uma forma especial, não uma macro ou função. Como macros, formas especiais podem fazer o que quiser com seus argumentos. Essa forma especial em particular simplesmente retorna seu argumento, aqui um símbolo, em vez do valor variável do símbolo. Na verdade, esse é o único objetivo deste formulário especial: impedir a avaliação! As macros não podem fazer isso sozinhas, elas precisam usá quote- las .

Então, o que se passa '? É uma macro de leitor que, como mencionado acima, não é o mesmo que uma macro lisp . Enquanto as macros são usadas para transformar código / dados, as macros do leitor são usadas anteriormente ao ler texto para transformar esse texto em código / dados.

'something

é uma forma curta para

(quote something)

`usado na definição real de bound-and-true-palso é uma macro de leitor. Se citar um símbolo, como `symbolnele é equivalente a 'symbol, mas quando é usado para citar uma lista, como `(foo bar ,baz)se comporta de maneira diferente, na medida em que os formulários prefixados ,são avaliados.

`(constant ,variable)

é equivalente a

(list (quote constant) variable))

Isso deve responder à pergunta por que símbolos não citados são algumas vezes avaliados (substituídos por seus valores) e outras vezes não; macros podem ser usadas quotepara impedir que um símbolo seja avaliado.

Mas por que bound-and-true-puma macro boundpé enquanto não é? Temos que poder determinar se símbolos arbitrários, que não são conhecidos até o tempo de execução, estão vinculados como símbolos. Isso não seria possível se o boundpargumento fosse automaticamente citado.

bound-and-true-pé usado para determinar se uma variável conhecida está definida e, em caso afirmativo, use seu valor. Isso é útil se uma biblioteca tiver uma dependência opcional em uma biblioteca de terceiros, como em:

(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-ppode ser definido como uma função e exigir que o argumento seja citado, mas porque ele se destina a casos em que você sabe de antemão qual variável se preocupa com uma macro foi usada para evitar que você digite '.

tarso
fonte
Surpreendentemente bem respondido.
Charles Ritchie
2

Do código fonte de 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.  */)

boundpespera a symbolcomo a entrada. 'some-variableé um símbolo para a variável some-variable.

Do código fonte de 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-pespera a variablecomo a entrada.

Dentro da bound-and-true-pmacro, ele obtém o símbolo fazendo (quote ,var). Portanto, se a entrada for some-variable, (quote ,var)resultará em 'some-variable.

Mas quando dou a entrada 'some-variablepara bound-and-true-p, recebo o erro: and: Wrong type argument: symbolp, (quote some-variable)porque a macro NÃO está esperando um símbolo ( 'some-variable) na entrada.

Kaushal Modi
fonte
Apenas um aviso. ''symbol faz sentido. Isso significa (quote (quote symbol)). A razão pela qual você recebe um erro é que não é um argumento válido boundp.
Malabarba
@ Malabarba Thanks. Eu fiz a correção.
precisa