Quando usare '(o citazione) in Lisp?


114

Dopo aver attraversato le parti principali di un libro Lisp introduttivo, non riuscivo ancora a capire cosa fa la funzione operatore speciale (quote)(o equivalente '), eppure questo è stato tutto il codice Lisp che ho visto.

Che cosa fa?

Risposte:


178

Risposta breve Ignora le regole di valutazione predefinite e non valuta l'espressione (simbolo o s-exp), passandola alla funzione esattamente come è stata digitata.

Risposta lunga: la regola di valutazione predefinita

Quando viene invocata una funzione regolare (ci tornerò più avanti), vengono valutati tutti gli argomenti ad essa passati. Ciò significa che puoi scrivere questo:

(* (+ a 2)
   3)

Che a sua volta valuta (+ a 2), valutando ae 2. Il valore del simbolo aviene cercato nell'insieme di associazione della variabile corrente e quindi sostituito. Say aè attualmente vincolato al valore 3:

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

Otterremmo (+ 3 2), + viene quindi invocato su 3 e 2 dando 5. La nostra forma originale ora (* 5 3)restituisce 15.

Spiega quotegià!

Tutto a posto. Come visto sopra, tutti gli argomenti di una funzione vengono valutati, quindi se vuoi passare il simbolo a e non il suo valore, non vuoi valutarlo. I simboli Lisp possono raddoppiare sia i loro valori che i marcatori in cui in altre lingue avresti usato stringhe, come le chiavi delle tabelle hash.

È qui che quoteentra in gioco. Supponiamo di voler tracciare le allocazioni di risorse da un'applicazione Python, ma piuttosto di tracciare in Lisp. Fai fare alla tua app Python qualcosa del genere:

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

Dando un output simile a questo (leggermente carino):

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

Ricordi cosa ho detto riguardo al fatto quoteche la regola predefinita non si applicava ("tick")? Buona. Ciò che altrimenti accadrebbe è che i valori di allocatee freevengano cercati, e non lo vogliamo. Nel nostro Lisp, desideriamo fare:

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

Per i dati sopra riportati, sarebbe stata eseguita la seguente sequenza di chiamate di funzione:

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

Ma per quanto riguarda list ?

Beh, a volte si fa desidera valutare le argomentazioni. Supponiamo che tu abbia una funzione ingegnosa che manipola un numero e una stringa e restituisce un elenco delle cose ... risultanti. Facciamo una falsa partenza:

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

Hey! Non è quello che volevamo. Vogliamo valutare selettivamente alcuni argomenti e lasciare gli altri come simboli. Prova # 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)

Non solo quote , mabackquote

Molto meglio! Per inciso, questo modello è così comune nelle (principalmente) macro, che esiste una sintassi speciale per fare proprio questo. Il backquote:

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

È come usare quote, ma con l'opzione di valutare esplicitamente alcuni argomenti anteponendoli con virgola. Il risultato è equivalente all'uso list, ma se stai generando codice da una macro, spesso vuoi valutare solo piccole parti del codice restituito, quindi il backquote è più adatto. Per elenchi più brevi, listpuò essere più leggibile.

Ehi, te ne sei dimenticato quote!

Allora, dove ci lascia questo? Oh giusto, cosa fa quoteeffettivamente? Restituisce semplicemente i suoi argomenti non valutati! Ricordi cosa ho detto all'inizio sulle funzioni regolari? Risulta che alcuni operatori / funzioni devono non valutano i loro argomenti. Ad esempio IF: non vorresti che il ramo else fosse valutato se non fosse stato preso, giusto? Cosiddetto operatori speciali , insieme alle macro, funzionano in questo modo. Gli operatori speciali sono anche l '"assioma" del linguaggio - insieme minimo di regole - su cui puoi implementare il resto del Lisp combinandoli insieme in modi diversi.

Ma torniamo a quote:

Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL

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

Confronta con (su 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] 

Perché non c'è spiffy-symbolnel campo di applicazione attuale!

Riassumendo

quote, backquote(con virgola), e listsono alcuni degli strumenti che usi per creare elenchi, che non sono solo elenchi di valori, ma come hai visto possono essere usati come structstrutture dati leggere (non c'è bisogno di definire a )!

Se desideri saperne di più, ti consiglio il libro Practical Common Lisp di Peter Seibel per un approccio pratico all'apprendimento del Lisp, se sei già interessato alla programmazione in generale. Alla fine, durante il tuo viaggio in Lisp, inizierai a utilizzare anche i pacchetti. Ron Garret's The Idiot's Guide to Common Lisp Packages ti darà una buona spiegazione di questi.

Buon hacking!


Nel mio emacs SBCL è impostato e quando digito `` questo 'è' vero `Restituisce solo l'ultimo, cioè TRUE in output. Anche in Portacle sto ottenendo lo stesso output
Totoro

@Totoro Il valore di ritorno di una funzione o solo di più istruzioni in lisp è l'ultima espressione, quindi in realtà ritorna this, quindi is, quindi true, ma vedi solo l'ultima restituita. (questo è vero e sono affermazioni separate)
Wezl

52

Dice "non valutarmi". Ad esempio, se si desidera utilizzare un elenco come dati e non come codice, è necessario inserire una citazione prima di esso. Per esempio,

(print '(+ 3 4))stampa "(+ 3 4)", mentre (print (+ 3 4))"7"


Come potrebbe valutarlo quindi, ad esempio, c'è un unquotecomando?
Lime

3
@William Lisps hanno una comoda funzione chiamata eval: (print (eval '(+ 3 4))). Questo è ciò che rende Lisp così eccezionale: gli elenchi sono codice e il codice è elenchi, quindi un programma Lisp può manipolare se stesso.
darkfeline

18

Altre persone hanno risposto in modo ammirevole a questa domanda e Matthias Benkard fa apparire un eccellente avvertimento.

NON UTILIZZARE QUOTE PER CREARE LISTE CHE VERRANNO MODIFICATE IN SEGUITO. La specifica consente al compilatore di trattare gli elenchi tra virgolette come costanti. Spesso, un compilatore ottimizzerà le costanti creando un singolo valore per esse in memoria e quindi facendo riferimento a quel singolo valore da tutte le posizioni in cui appare la costante. In altre parole, può trattare la costante come una variabile globale anonima.

Ciò può causare problemi evidenti. Se modifichi una costante, potrebbe benissimo modificare altri usi della stessa costante in codice completamente non correlato. Ad esempio, puoi confrontare una variabile con '(1 1) in qualche funzione, e in una funzione completamente diversa, iniziare un elenco con' (1 1) e quindi aggiungere più cose ad esso. Eseguendo queste funzioni, potresti scoprire che la prima funzione non corrisponde più correttamente alle cose, perché ora sta cercando di confrontare la variabile con '(1 1 2 3 5 8 13), che è ciò che la seconda funzione ha restituito. Queste due funzioni sono completamente indipendenti, ma hanno effetto l'una sull'altra a causa dell'uso di costanti. Possono verificarsi anche effetti negativi più folli, come un'iterazione di lista perfettamente normale che improvvisamente si ripete all'infinito.

Usa citazione quando hai bisogno di un elenco costante, ad esempio per il confronto. Usa l'elenco quando modifichi il risultato.


Quindi sembra che dovresti usare la (list (+ 1 2)) maggior parte del tempo. In caso affermativo come si impedisce la valutazione (+ 1 2)all'interno di un simile esempio? C'è un unquotecomando?
Lime

1
Vuoi l'equivalente di '((3))o l'equivalente di '((+ 1 2))? In quest'ultimo caso, è necessario utilizzare più list: (list (list '+ 1 2)). O se volessi l'equivalente di '(+ 1 2), solo (list '+ 1 2). E ricorda, se non stai modificando l'elenco, sentiti libero di usare le virgolette: niente di sbagliato '(+ 1 2)se stai solo confrontando con esso o qualcosa del genere.
Xanthir

1
Ti dispiace fare riferimento a dove gli elenchi citati dovrebbero essere trattati come costanti?
Lime

HyperSpec clhs.lisp.se/Body/s_quote.htm dice che il comportamento non è definito se l'oggetto citato viene modificato in modo distruttivo. È implicito che ciò consenta a impls di trattare i valori come valori atomici.
Xanthir

14

Una risposta a questa domanda dice che QUOTE "crea strutture di dati di elenchi". Questo non è del tutto corretto. QUOTE è più fondamentale di questo. In effetti, QUOTE è un operatore banale: il suo scopo è impedire che accada qualsiasi cosa. In particolare, non crea nulla.

Quello che dice (QUOTE X) è fondamentalmente "non fare nulla, dammi solo X". X non deve essere un elenco come in (QUOTE (ABC)) o un simbolo come in (QUOTE FOO). Può essere qualsiasi oggetto qualunque. In effetti, il risultato della valutazione della lista prodotta da (LISTA 'QUOTE ALCUNO-OGGETTO) restituirà sempre e solo ALCUNO-OGGETTO, qualunque esso sia.

Ora, il motivo per cui (QUOTE (ABC)) sembra aver creato una lista i cui elementi sono A, B e C è che tale lista è davvero ciò che restituisce; ma al momento della valutazione del modulo QUOTE, l'elenco è generalmente già esistente da un po '(come componente del modulo QUOTE!), creato dal caricatore o dal lettore prima dell'esecuzione del codice.

Un'implicazione di ciò che tende a far inciampare i neofiti abbastanza spesso è che non è molto saggio modificare un elenco restituito da un modulo QUOTE. I dati restituiti da QUOTE sono, a tutti gli effetti, da considerare come parte del codice in esecuzione e devono quindi essere trattati come di sola lettura!


11

Il preventivo impedisce l'esecuzione o la valutazione di un modulo, trasformandolo invece in dati. In generale puoi eseguire i dati valutandoli.

quote crea strutture di dati di elenco, ad esempio, le seguenti sono equivalenti:

(quote a)
'a

Può anche essere utilizzato per creare elenchi (o alberi):

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

Probabilmente è meglio ottenere un libro introduttivo su lisp, come Practical Common Lisp (che è disponibile per la lettura in linea).


3

In Emacs Lisp:

Cosa si può citare?

Liste e simboli.

La citazione di un numero restituisce il numero stesso: '5è lo stesso di 5.

Cosa succede quando citi elenchi?

Per esempio:

'(one two) valuta a

(list 'one 'two) che restituisce

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

(intern "one")crea un simbolo chiamato "uno" e lo memorizza in una hash-map "centrale", quindi ogni volta che dici 'oneil simbolo nominato "one"verrà cercato in quella hash-map centrale.

Ma cos'è un simbolo?

Ad esempio, nei linguaggi OO (Java / Javascript / Python) un simbolo potrebbe essere rappresentato come un oggetto che ha un namecampo, che è il nome del simbolo come "one"sopra, e dati e / o codice possono essere associati a questo oggetto.

Quindi un simbolo in Python potrebbe essere implementato come:

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

In Emacs Lisp, ad esempio, un simbolo può avere 1) dati ad esso associati E (allo stesso tempo - per lo stesso simbolo) 2) codice ad esso associato - a seconda del contesto, vengono chiamati i dati o il codice.

Ad esempio, in Elisp:

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

valuta a 4.

Perché (add add add)valuta come:

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

Quindi, ad esempio, usando la Symbolclasse che abbiamo definito in Python sopra, questo addsimbolo ELisp potrebbe essere scritto in Python come Symbol("add",(lambda x,y: x+y),2).

Molte grazie per le persone su IRC #emacs per avermi spiegato simboli e citazioni.


2

Quando vogliamo passare un argomento stesso invece di passare il valore dell'argomento, utilizziamo virgolette. È principalmente correlato alla procedura che passa durante l'uso di liste, coppie e atomi che non sono disponibili nel linguaggio di programmazione C (la maggior parte delle persone inizia a programmare usando la programmazione C, quindi ci confondiamo) Questo è il codice nel linguaggio di programmazione Scheme che è un dialetto di lisp e immagino che tu possa capire questo codice.

(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

L'ultima riga (atom? 'Abc) sta passando abc come è per la procedura per verificare se abc è un atomo o meno, ma quando passi (atom? Abc) allora controlla il valore di abc e passa il valore a esso. Da allora, non abbiamo fornito alcun valore ad esso


1

Quote restituisce la rappresentazione interna dei suoi argomenti. Dopo aver analizzato troppe spiegazioni su ciò che la citazione non fa, è allora che la lampadina si è accesa. Se il REPL non ha convertito i nomi delle funzioni in MAIUSCOLO quando li ho citati, potrebbe non essermi reso conto.

Così. Le funzioni Lisp ordinarie convertono i loro argomenti in una rappresentazione interna, valutano gli argomenti e applicano la funzione. Quote converte i suoi argomenti in una rappresentazione interna e la restituisce. Tecnicamente è corretto dire che la citazione dice "non valutare", ma quando stavo cercando di capire cosa faceva, dirmi cosa non fa era frustrante. Il mio tostapane non valuta nemmeno le funzioni Lisp; ma non è così che spieghi cosa fa un tostapane.


1

Anoter risposta breve:

quotesignifica senza valutarlo, e il backquote è una citazione ma lascia le porte sul retro .

Un buon riferimento:

Emacs Lisp Reference Manual lo rende molto chiaro

9.3 Citazioni

La citazione in forma speciale restituisce il suo unico argomento, come scritto, senza valutarlo. Ciò fornisce un modo per includere simboli ed elenchi costanti, che non sono oggetti di auto-valutazione, in un programma. (Non è necessario citare oggetti che si auto-valutano come numeri, stringhe e vettori.)

Forma speciale: oggetto preventivo

This special form returns object, without evaluating it. 

Poiché quote è usato così spesso nei programmi, Lisp fornisce una comoda sintassi di lettura per esso. Un carattere apostrofo ('' ') seguito da un oggetto Lisp (nella sintassi di lettura) si espande in una lista il cui primo elemento è virgolette e il cui secondo elemento è l'oggetto. Pertanto, la sintassi di lettura 'x è un'abbreviazione di (quote x).

Di seguito sono riportati alcuni esempi di espressioni che utilizzano virgolette:

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

(quote foo)
      foo

'foo
      foo

''foo
      (quote foo)

'(quote foo)
      (quote foo)

9.4 Backquote

I costrutti backquote consentono di citare una lista, ma valutano selettivamente gli elementi di quella lista. Nel caso più semplice, è identico alla citazione del modulo speciale (descritta nella sezione precedente; vedere Virgolette). Ad esempio, queste due forme producono risultati identici:

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

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

Il marcatore speciale "," all'interno dell'argomento di backquote indica un valore che non è costante. Il valutatore Emacs Lisp valuta l'argomento di ',' e inserisce il valore nella struttura della lista:

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

La sostituzione con "," è consentita anche a livelli più profondi della struttura dell'elenco. Per esempio:

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

È inoltre possibile unire un valore valutato nell'elenco risultante, utilizzando il marcatore speciale ', @'. Gli elementi della lista unita diventano elementi allo stesso livello degli altri elementi della lista risultante. Il codice equivalente senza usare '`' è spesso illeggibile. Ecco alcuni esempi:

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

1
Code is data and data is code.  There is no clear distinction between them.

Questa è un'affermazione classica che ogni programmatore lisp conosce.

Quando citi un codice, quel codice sarà dato.

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

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

Quando citi un codice, il risultato saranno dati che rappresentano quel codice. Quindi, quando vuoi lavorare con dati che rappresentano un programma, citi quel programma. Questo vale anche per le espressioni atomiche, non solo per gli elenchi:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

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

1 ]=> code
;Unbound variable: code

Supponendo che tu voglia creare un linguaggio di programmazione incorporato in lisp, lavorerai con programmi che sono citati in schema (come '(+ 2 3)) e che vengono interpretati come codice nel linguaggio che crei, dando ai programmi un'interpretazione semantica. In questo caso è necessario utilizzare il preventivo per conservare i dati, altrimenti verranno valutati in linguaggio esterno.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.