Domanda

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

Che cosa fa?

È stato utile?

Soluzione

Risposta breve Bypassa 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 passati ad essa. Questo significa che puoi scrivere questo:

(* (+ a 2)
   3)

Che a sua volta valuta (+ a 2) , valutando a e 2. Il valore del simbolo a viene cercato in il set di associazione delle variabili corrente e quindi sostituito. Di ' a è attualmente associato al valore 3:

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

Otterremmo (+ 3 2) , + viene quindi invocato su 3 e 2 con resa 5. Il nostro modulo originale ora è (* 5 3) con resa 15 .

Spiega preventivo Già!

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

È qui che entra in gioco quote . Supponiamo che tu voglia tracciare le allocazioni delle risorse da un'applicazione Python, ma piuttosto fare la trama in Lisp. Chiedi alla tua app Python di fare 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(")")

Dandoti un output simile a questo (leggermente discreto):

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

Ricordi cosa ho detto su quote (" tick ") facendo in modo che la regola predefinita non si applichi? Buono. Ciò che altrimenti accadrebbe è che i valori di allocate e free siano cercati, e noi 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 indicati, sarebbe stata effettuata la seguente sequenza di chiamate di funzione:

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

Ma che dire di list?

Beh, a volte vuoi vuoi valutare gli argomenti. Supponi di avere una funzione elegante che manipola un numero e una stringa e restituisce un elenco delle ... cose risultanti. Facciamo un falso inizio:

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

Ehi! Non è quello che volevamo. Vogliamo selettivamente valutare 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 , ma backquote

Molto meglio! Per inciso, questo modello è così comune nelle macro (principalmente), 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 aggiungendoli con una virgola. Il risultato equivale all'utilizzo di list , ma se stai generando codice da una macro spesso vuoi solo valutare piccole parti del codice restituito, quindi il backquote è più adatto. Per elenchi più brevi, list può essere più leggibile.

Ehi, hai dimenticato qu<!

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

Torna a quote , tuttavia:

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-symbol nell'ambito attuale!

Riassumendo

quote , backquote (con virgola) e list sono alcuni degli strumenti che usi per creare elenchi, che non sono solo elenchi di valori, ma come hai visto, possono essere usati come strutture di dati leggere (non è necessario definire una struct )!

Se desideri saperne di più, raccomando il libro di Peter Seibel Lisp pratico comune per un approccio pratico all'apprendimento di Lisp , se sei già interessato alla programmazione in generale. Alla fine nel tuo viaggio Lisp, inizierai anche a usare i pacchetti. La di Idiot's Guide to Common Lisp Packages di Ron Garret ti darà una buona spiegazione di queste.

Happy hacking!

Altri suggerimenti

Dice " non valutarmi " ;. Ad esempio, se si desidera utilizzare un elenco come dati e non come codice, si inserisce un preventivo. Ad esempio,

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

Altre persone hanno risposto mirabilmente a questa domanda, e Matthias Benkard fa apparire un eccellente avvertimento.

NON UTILIZZARE IL PREVENTIVO PER CREARE ELENCHI CHE MODIFICERAI DOPO. La specifica consente al compilatore di trattare gli elenchi quotati come costanti. Spesso un compilatore ottimizza le costanti creando un singolo valore per loro 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 ovvi problemi. Se modifichi una costante, puoi benissimo modificare altri usi della stessa costante in un codice completamente non correlato. Ad esempio, puoi confrontare alcune variabili con '(1 1) in alcune funzioni e, in una funzione completamente diversa, avviare un elenco con' (1 1) e quindi aggiungere più elementi ad essa. 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 un effetto reciproco a causa dell'uso delle costanti. Possono anche verificarsi effetti negativi più folli, come una ripetizione di elenco perfettamente normale improvvisamente loop infinito.

Usa il preventivo quando hai bisogno di un elenco costante, ad esempio per un confronto. Utilizza l'elenco quando modificherai il risultato.

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

Ciò che dice (QUOTE X) è fondamentalmente "non fare nulla, dammi solo X". X non deve necessariamente essere un elenco come in (QUOTE (A B C)) o un simbolo come in (QUOTE FOO). Può essere qualsiasi oggetto qualunque. In effetti, il risultato della valutazione dell'elenco prodotto da (ELENCO QUOTE SOME-OGGETTO) restituirà sempre SOME-OGGETTO, qualunque esso sia.

Ora, la ragione per cui (QUOTE (A B C)) sembra aver creato un elenco i cui elementi sono A, B e C è che tale elenco è realmente ciò che restituisce; ma al momento della valutazione del modulo QUOTE, l'elenco è generalmente già in vigore 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 inciampare abbastanza spesso nei neofiti è che è poco saggio modificare un elenco restituito da un modulo di PREVENTIVO. I dati restituiti da QUOTE devono, a tutti gli effetti, essere considerati parte del codice in esecuzione e pertanto devono essere trattati in sola lettura!

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

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

(quote a)
'a

Può anche essere usato per creare liste (o alberi):

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

Probabilmente ti conviene ottenere un libro introduttivo su lisp, come Practical Common Lisp (che è disponibile per la lettura on-line).

Quando vogliamo passare un argomento stesso invece di passare il valore dell'argomento, usiamo la citazione. È principalmente correlato alla procedura che passa durante l'utilizzo di liste, coppie e atomi che non sono disponibili nel linguaggio di programmazione C (la maggior parte delle persone inizia a programmare utilizzando la programmazione C, quindi ci confondiamo) Questo è un 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 (atomo? 'abc) sta passando abc com'è alla procedura per verificare se abc è un atomo o no, ma quando si passa (atom? abc) quindi controlla il valore di abc e dei passaggi il valore ad esso. Da allora, non abbiamo fornito alcun valore

In Emacs Lisp:

Cosa si può citare?

Elenchi e simboli.

La citazione di un numero corrisponde al numero stesso: '5 è uguale a 5 .

Che cosa succede quando si citano elenchi?

Ad esempio:

'(uno due) restituisce

(elenca 'uno' due) che valuta

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

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

Ma cos'è un simbolo?

Ad esempio, nelle lingue OO (Java / Javascript / Python) un simbolo potrebbe essere rappresentato come un oggetto che ha un campo name , 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 associati ad esso E (allo stesso tempo - per lo stesso simbolo) 2) codice associato ad esso - a seconda del contesto, vengono chiamati i dati o il codice .

Ad esempio, in Elisp:

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

restituisce 4 .

Perché (aggiungi aggiungi aggiungi) viene valutato come:

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

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

Mille grazie per la gente su IRC #emacs per avermi spiegato simboli e citazioni.

Il preventivo restituisce la rappresentazione interna dei suoi argomenti. Dopo aver analizzato troppe spiegazioni di ciò che la citazione non fa, è allora che la lampadina si è accesa. Se il REPL non ha convertito i nomi delle funzioni in UPPER-CASE quando le ho citate, potrebbe non essermi reso conto.

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

Risposta breve di Anoter:

quote significa senza valutazione, e backquote è citazione ma lascia backdoor .

Un buon riferimento:

Il manuale di riferimento di Emacs Lisp lo rende molto chiaro

9.3 Citando

La citazione del modulo speciale restituisce il suo singolo argomento, come scritto, senza valutarlo. Ciò fornisce un modo per includere simboli ed elenchi costanti, che non sono oggetti autovalutanti, in un programma. (Non è necessario citare oggetti autovalutanti come numeri, stringhe e vettori.)

Modulo speciale: oggetto preventivo

This special form returns object, without evaluating it. 

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

Ecco alcuni esempi di espressioni che usano quote:

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

(quote foo)
     ⇒ foo

'foo
     ⇒ foo

''foo
     ⇒ (quote foo)

'(quote foo)
     ⇒ (quote foo)

9.4 Backquote

I costrutti di backquote consentono di quotare un elenco, ma di valutare selettivamente gli elementi di tale elenco. Nel caso più semplice, è identico al preventivo del modulo speciale (descritto nella sezione precedente; vedere Citazioni). 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 del backquote indica un valore che non è costante. Il valutatore Emacs Lisp valuta l'argomento di "," e inserisce il valore nella struttura dell'elenco:

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

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

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

Puoi anche unire un valore valutato nell'elenco risultante, usando il marcatore speciale ", @". Gli elementi dell'elenco di giunzioni diventano elementi allo stesso livello degli altri elementi dell'elenco 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)
Code is data and data is code.  There is no clear distinction between them.

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

Quando citate un codice, quel codice saranno dati.

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

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

Quando si cita un codice, il risultato saranno i dati che rappresentano quel codice. Quindi, quando si desidera lavorare con dati che rappresentano un programma, si cita 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 di voler creare un linguaggio di programmazione incorporato in lisp - lavorerai con programmi citati nello schema (come '(+ 2 3) ) e 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 un linguaggio esterno.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top