Quando usar '(ou citação) em Lisp?

114

Depois de passar pelas partes principais de um livro introdutório ao Lisp, eu ainda não conseguia entender o que a função do operador especial (quote)(ou equivalente ') faz, mas isso foi em todo o código Lisp que eu vi.

O que isso faz?

Cristián Romo
fonte

Respostas:

178

Resposta curta Ignore as regras de avaliação padrão e não avalie a expressão (símbolo ou s-exp), passando-a adiante para a função exatamente como digitada.

Resposta longa: a regra de avaliação padrão

Quando uma função regular (falarei sobre isso mais tarde) é invocada, todos os argumentos passados ​​para ela são avaliados. Isso significa que você pode escrever isto:

(* (+ a 2)
   3)

Que por sua vez avalia (+ a 2), avaliando ae 2. O valor do símbolo aé procurado no conjunto de vinculação de variável atual e, em seguida, substituído. Say aestá atualmente vinculado ao valor 3:

(let ((a 3))
  (* (+ a 2)
     3))

Obteríamos (+ 3 2), + é então invocado em 3 e 2 resultando em 5. Nossa forma original agora está (* 5 3)gerando 15.

Explique quotejá!

Tudo bem. Como visto acima, todos os argumentos para uma função são avaliados, então se você deseja passar o símbolo a e não seu valor, você não quer avaliá-lo. Os símbolos Lisp podem dobrar como seus valores e marcadores onde você, em outras linguagens, teria usado strings, como chaves para tabelas de hash.

É aqui que quoteentra. Digamos que você queira plotar as alocações de recursos de um aplicativo Python, mas sim fazer o plot em Lisp. Faça com que seu aplicativo Python faça algo assim:

print("'(")
while allocating:
    if random.random() > 0.5:
        print(f"(allocate {random.randint(0, 20)})")
    else:
        print(f"(free {random.randint(0, 20)})")
    ...
print(")")

Fornecendo uma saída parecida com esta (ligeiramente bonita):

'((allocate 3)
  (allocate 7)
  (free 14)
  (allocate 19)
  ...)

Lembra do que eu disse sobre quote("tick") fazer com que a regra padrão não se aplique? Boa. De outra forma, o que aconteceria é que os valores de allocatee freesão procurados, e não queremos isso. Em nosso Lisp, desejamos fazer:

(dolist (entry allocation-log)
  (case (first entry)
    (allocate (plot-allocation (second entry)))
    (free (plot-free (second entry)))))

Para os dados fornecidos acima, a seguinte sequência de chamadas de função teria sido feita:

(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)

Mas e quanto list?

Bem, às vezes você não quer avaliar os argumentos. Digamos que você tenha uma função bacana manipulando um número e uma string e retornando uma lista das ... coisas resultantes. Vamos dar um começo falso:

(defun mess-with (number string)
  '(value-of-number (1+ number) something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))

Ei! Não era isso que queríamos. Queremos avaliar seletivamente alguns argumentos e deixar os outros como símbolos. Experimente o nº 2!

(defun mess-with (number string)
  (list 'value-of-number (1+ number) 'something-with-string (length string)))

Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)

Não apenas quote, masbackquote

Muito melhor! Incidentalmente, esse padrão é tão comum em (principalmente) macros, que existe uma sintaxe especial para fazer exatamente isso. A crase:

(defun mess-with (number string)
  `(value-of-number ,(1+ number) something-with-string ,(length string)))

É como usar quote, mas com a opção de avaliar explicitamente alguns argumentos prefixando-os com vírgulas. O resultado é equivalente a usar list, mas se você estiver gerando código a partir de uma macro, você geralmente deseja avaliar apenas pequenas partes do código retornado, então a crase é mais adequada. Para listas mais curtas, listpode ser mais legível.

Ei, você se esqueceu quote!

Então, onde isso nos deixa? Oh certo, o que quoterealmente faz? Ele simplesmente retorna seu (s) argumento (s) não avaliados! Lembra do que eu disse no começo sobre funções regulares? Acontece que alguns operadores / funções não precisam avaliar seus argumentos. Como IF - você não gostaria que o branch else fosse avaliado se não fosse levado, certo? Os chamados operadores especiais , junto com macros, funcionam assim. Operadores especiais também são o "axioma" da linguagem - conjunto mínimo de regras - sobre o qual você pode implementar o resto do Lisp combinando-os de maneiras diferentes.

Mas voltando a quote:

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL

Compare com (no Steel-Bank Common Lisp):

Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING   {A69F6A9}>:
  The variable SPIFFY-SYMBOL is unbound.

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [ABORT] Exit debugger, returning to top level.

(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0] 

Porque não há spiffy-symbolno escopo atual!

Resumindo

quote, backquote(com vírgula) e listsão algumas das ferramentas que você usa para criar listas, que não são apenas listas de valores, mas, como você viu, podem ser usadas como structestruturas de dados leves (sem necessidade de definir a )!

Se você deseja aprender mais, eu recomendo o livro Practical Common Lisp de Peter Seibel para uma abordagem prática para aprender Lisp, se você já estiver programando em geral. Eventualmente, em sua jornada Lisp, você começará a usar pacotes também. O Guia do Idiota para Pacotes Lisp Comuns de Ron Garret lhe dará uma boa explicação sobre eles.

Feliz hacking!

Mikael Jansson
fonte
No meu emacs, o SBCL está configurado e quando eu digito `'isto' é 'verdadeiro` Ele retorna apenas por último, ou seja, TRUE na saída. Mesmo no portacle estou obtendo a mesma saída
Totoro
@Totoro O valor de retorno de uma função ou apenas várias instruções em Lisp é a última expressão, para que ele realmente retorna this, então is, em seguida true, mas você só vê o último retornado. (isto é e verdade são declarações separadas)
Wezl
52

Diz "não me avalie". Por exemplo, se você quiser usar uma lista como dados, e não como código, deve colocar uma citação antes dela. Por exemplo,

(print '(+ 3 4))imprime "(+ 3 4)", enquanto (print (+ 3 4))imprime "7"

Adam Rosenfield
fonte
Como pode avaliá-lo, por exemplo, há um unquotecomando?
Lima,
3
@William Lisps tem uma função conveniente chamado eval: (print (eval '(+ 3 4))). Isso é o que torna o Lisps tão bom: listas são códigos e código são listas, então um programa Lisp pode se manipular.
darkfeline
18

Outras pessoas responderam a essa pergunta de maneira admirável, e Matthias Benkard traz um excelente aviso.

NÃO USE AS CITAÇÕES PARA CRIAR LISTAS QUE VOCÊ IRÁ MODIFICAR MAIS TARDE. A especificação permite que o compilador trate as listas citadas como constantes. Freqüentemente, um compilador otimizará constantes criando um único valor para elas na memória e, em seguida, referenciando esse único valor de todos os locais onde a constante aparece. Em outras palavras, ele pode tratar a constante como uma variável global anônima.

Isso pode causar problemas óbvios. Se você modificar uma constante, pode muito bem modificar outros usos da mesma constante em um código completamente não relacionado. Por exemplo, você pode comparar alguma variável a '(1 1) em alguma função, e em uma função completamente diferente, começar uma lista com' (1 1) e então adicionar mais coisas a ela. Ao executar essas funções, você pode descobrir que a primeira função não corresponde mais às coisas corretamente, porque agora está tentando comparar a variável com '(1 1 2 3 5 8 13), que é o que a segunda função retornou. Essas duas funções são completamente independentes, mas têm um efeito uma na outra devido ao uso de constantes. Mesmo efeitos ruins mais loucos podem acontecer, como uma iteração de lista perfeitamente normal de repente um loop infinito.

Use aspas quando precisar de uma lista constante, como para comparação. Use a lista quando for modificar o resultado.

Xanthir
fonte
Portanto, parece que você deve usar a (list (+ 1 2)) maior parte do tempo. Se sim, como você evita a avaliação de (+ 1 2)dentro de tal exemplo? Existe um unquotecomando?
Lima,
1
Você quer o equivalente a '((3)), ou o equivalente a '((+ 1 2))? Neste último caso, você tem que usar mais list: (list (list '+ 1 2)). Ou se você quiser o equivalente a '(+ 1 2), apenas (list '+ 1 2). E lembre-se, se você não está modificando a lista, sinta-se à vontade para usar citação: nada de errado em '(+ 1 2)se você está apenas comparando com ela ou algo assim.
Xanthir,
1
Você se importa em fazer referência a onde as listas citadas devem ser tratadas como constantes?
Lima,
O HyperSpec clhs.lisp.se/Body/s_quote.htm diz que o comportamento é indefinido se o objeto citado for modificado destrutivamente. Está implícito que isso permite que impls tratem os valores como valores atômicos.
Xanthir
14

Uma resposta a esta pergunta diz que QUOTE “cria estruturas de dados de lista”. Isso não está certo. QUOTE é mais fundamental do que isso. Na verdade, QUOTE é um operador trivial: seu objetivo é impedir que qualquer coisa aconteça. Em particular, não cria nada.

O que (QUOTE X) diz é basicamente "não faça nada, apenas me dê X". X não precisa ser uma lista como em (QUOTE (ABC)) ou um símbolo como em (QUOTE FOO). Pode ser qualquer objeto, seja qual for. Na verdade, o resultado da avaliação da lista que é produzida por (LIST 'CITAÇÃO DE ALGUM-OBJETO) sempre retornará apenas ALGUM-OBJETO, seja ele qual for.

Agora, a razão pela qual (QUOTE (ABC)) parece ter criado uma lista cujos elementos são A, B e C é que essa lista realmente é o que ela retorna; mas no momento em que o formulário QUOTE é avaliado, a lista geralmente já existe há algum tempo (como um componente do formulário QUOTE!), criada pelo carregador ou pelo leitor antes da execução do código.

Uma implicação disso que tende a enganar os novatos com bastante frequência é que não é muito inteligente modificar uma lista retornada por um formulário de CITAÇÃO. Os dados retornados por QUOTE devem , para todos os efeitos, ser considerados como parte do código que está sendo executado e, portanto, devem ser tratados como somente leitura!

Matthias Benkard
fonte
11

A cotação impede a execução ou avaliação de um formulário, transformando-o em dados. Em geral, você pode executar os dados avaliando-os.

quote cria estruturas de dados de lista, por exemplo, as seguintes são equivalentes:

(quote a)
'a

Também pode ser usado para criar listas (ou árvores):

(quote (1 2 3))
'(1 2 3)

Provavelmente, é melhor você obter um livro introdutório ao lisp, como Practical Common Lisp (que está disponível para leitura on-line).

Kyle Burton
fonte
3

Em Emacs Lisp:

O que pode ser citado?

Listas e símbolos.

Citar um número avalia o próprio número: '5é o mesmo que 5.

O que acontece quando você cita listas?

Por exemplo:

'(one two) avalia para

(list 'one 'two) que avalia para

(list (intern "one") (intern ("two"))).

(intern "one")cria um símbolo denominado "um" e o armazena em um mapa hash "central", então sempre que você disser 'one, o símbolo denominado"one" será procurado naquele hash-map central.

Mas o que é um símbolo?

Por exemplo, em linguagens OO (Java / Javascript / Python) um símbolo pode ser representado como um objeto que tem um namecampo, que é o nome do símbolo como"one" acima, e dados e / ou código podem ser associados a este objeto.

Portanto, um símbolo em Python pode ser implementado como:

class Symbol:
   def __init__(self,name,code,value):
       self.name=name
       self.code=code
       self.value=value

No Emacs Lisp, por exemplo, um símbolo pode ter 1) dados associados a ele E (ao mesmo tempo - para o mesmo símbolo) 2) código associado a ele - dependendo do contexto, os dados ou o código são chamados.

Por exemplo, em Elisp:

(progn
  (fset 'add '+ )
  (set 'add 2)
  (add add add)
)

avalia para 4.

Porque é (add add add)avaliado como:

(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4

Então, por exemplo, usando a Symbolclasse que definimos em Python acima, este addsímbolo ELisp poderia ser escrito em Python comoSymbol("add",(lambda x,y: x+y),2) .

Muito obrigado ao pessoal do IRC #emacs por explicar os símbolos e citações para mim.

Jhegedus
fonte
2

Quando queremos passar um argumento em vez de passar o valor do argumento, usamos aspas. Está principalmente relacionado ao procedimento que passa durante o uso de listas, pares e átomos que não estão disponíveis na linguagem de programação C (a maioria das pessoas começa a programar usando a programação C, portanto, ficamos confusos). Este é o código na linguagem de programação Scheme, que é um dialeto de lisp e eu acho que você pode entender este código.

(define atom?              ; defining a procedure atom?
  (lambda (x)              ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f

A última linha (atom? 'Abc) está passando abc como é para o procedimento para verificar se abc é um átomo ou não, mas quando você passa (atom? Abc), então ele verifica o valor de abc e passa o valor para isto. Desde então, não fornecemos nenhum valor para ele

erro desconhecido
fonte
1

Quote retorna a representação interna de seus argumentos. Depois de procurar muitas explicações sobre o que a citação não faz, foi quando a lâmpada acendeu. Se o REPL não converteu os nomes das funções em MAIÚSCULAS quando os citei, pode não ter percebido.

Assim. Funções Lisp comuns convertem seus argumentos em uma representação interna, avaliam os argumentos e aplicam a função. Quote converte seus argumentos em uma representação interna e apenas retorna isso. Tecnicamente, é correto dizer que a citação diz "não avalie", mas quando eu estava tentando entender o que ele fazia, me dizer o que ele não fazia era frustrante. Minha torradeira também não avalia funções Lisp; mas não é assim que você explica o que uma torradeira faz.

Steve
fonte
1

Outra resposta curta:

quotesignifica sem avaliá-lo, e backquote é citação, mas deixa portas traseiras .

Uma boa referência:

O Manual de Referência do Emacs Lisp deixa isso muito claro

9.3 Citando

A forma especial quote retorna seu único argumento, conforme escrito, sem avaliá-lo. Isso fornece uma maneira de incluir símbolos e listas constantes, que não são objetos de autoavaliação, em um programa. (Não é necessário citar objetos de autoavaliação, como números, strings e vetores.)

Formulário Especial: objeto de cotação

This special form returns object, without evaluating it. 

Porque quote é usado com tanta freqüência em programas, Lisp fornece uma sintaxe de leitura conveniente para ele. Um caractere apóstrofo ('' ') seguido por um objeto Lisp (na sintaxe de leitura) se expande para uma lista cujo primeiro elemento é aspas e cujo segundo elemento é o objeto. Assim, a sintaxe de leitura 'x é uma abreviatura de (aspas x).

Aqui estão alguns exemplos de expressões que usam aspas:

(quote (+ 1 2))
      (+ 1 2)

(quote foo)
      foo

'foo
      foo

''foo
      (quote foo)

'(quote foo)
      (quote foo)

9.4 Backquote

Construções de crase permitem que você cite uma lista, mas avalie seletivamente os elementos dessa lista. No caso mais simples, é idêntico ao formulário especial de citação (descrito na seção anterior; consulte Citação). Por exemplo, essas duas formas produzem resultados idênticos:

`(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

'(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

O marcador especial ',' dentro do argumento de crase indica um valor que não é constante. O avaliador Emacs Lisp avalia o argumento de ',' e coloca o valor na estrutura da lista:

`(a list of ,(+ 2 3) elements)
      (a list of 5 elements)

A substituição por ',' também é permitida em níveis mais profundos da estrutura da lista. Por exemplo:

`(1 2 (3 ,(+ 4 5)))
      (1 2 (3 9))

Você também pode unir um valor avaliado na lista resultante, usando o marcador especial ', @'. Os elementos da lista emendada tornam-se elementos no mesmo nível que os outros elementos da lista resultante. O código equivalente sem usar '`' geralmente é ilegível. aqui estão alguns exemplos:

(setq some-list '(2 3))
      (2 3)

(cons 1 (append some-list '(4) some-list))
      (1 2 3 4 2 3)

`(1 ,@some-list 4 ,@some-list)
      (1 2 3 4 2 3)
Andrew_1510
fonte
1
Code is data and data is code.  There is no clear distinction between them.

Esta é uma declaração clássica que qualquer programador lisp conhece.

Quando você cita um código, esse código será um dado.

1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)

1 ]=> (+ 2 3 4)
;Value: 9

Quando você cita um código, o resultado são dados que representam esse código. Portanto, quando você deseja trabalhar com dados que representam um programa, você cita esse programa. Isso também é válido para expressões atômicas, não apenas para listas:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

1 ]=> '"ok"
;Value: "ok"

1 ]=> code
;Unbound variable: code

Supondo que você queira criar uma linguagem de programação embutida em lisp - você trabalhará com programas que são citados em um esquema (como '(+ 2 3)) e que são interpretados como código na linguagem que você cria, dando aos programas uma interpretação semântica. Neste caso você precisa usar orçamento para guardar os dados, caso contrário, eles serão avaliados em idioma externo.

alinsoar
fonte