Um símbolo que está na posição de não função é tratado como o nome de uma variável. In (function variable)
function
está na posição da função (após o parêntese de abertura) e variable
nã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-variable
vinculada como variável" e não "é o símbolo my-variable
vinculado como variável.
Então, por que bound-and-truep
se 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-p
parece:
(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 nil
se my-variable
não for boundp
ou 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-p
also é uma macro de leitor. Se citar um símbolo, como `symbol
nele é 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 quote
para impedir que um símbolo seja avaliado.
Mas por que bound-and-true-p
uma 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 boundp
argumento 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-p
pode 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 '
.
setq
significaset 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 (setq
sendo uma exceção importante).