Qual è la differenza tra quotazione e lista?

So che puoi usare ' (aka quote ) per creare un elenco, e lo uso sempre, in questo modo:

 > (car '(1 2 3)) 1 

Ma non sempre funziona come mi aspetterei. Ad esempio, ho provato a creare un elenco di funzioni, come questo, ma non ha funzionato:

 > (define math-fns '(+ - * /)) > (map (lambda (fn) (fn 1)) math-fns) application: not a procedure; expected a procedure that can be applied to arguments given: '+ 

Quando uso la list , funziona:

 > (define math-fns (list + - * /)) > (map (lambda (fn) (fn 1)) math-fns) '(1 -1 1 1) 

Perché? Pensavo che ' fosse solo una comoda abbreviazione, quindi perché il comportamento è diverso?

TL; DR: sono diversi; usa la list caso di dubbio.

Una regola empirica: utilizzare l’ list ogni volta che si desidera valutare gli argomenti; quote “distribuisce” sui suoi argomenti, quindi '(+ 1 2) è come (list '+ '1 '2) . Finirai con un simbolo nel tuo elenco, non con una funzione.


Uno sguardo approfondito list e quote

In Schema e Racket, le quote e le list sono cose completamente diverse , ma poiché entrambe possono essere utilizzate per produrre elenchi, la confusione è comune e comprensibile. C’è una differenza incredibilmente importante tra di loro: la list è una semplice vecchia funzione , mentre la quote (anche senza la speciale ' syntax ' ) è una forma speciale . Cioè, la list può essere implementata in Schema semplice, ma la quote non può essere.

La funzione list

La funzione list è in realtà il più semplice dei due, quindi iniziamo da lì. È una funzione che accetta un numero qualsiasi di argomenti e raccoglie gli argomenti in un elenco.

 > (list 1 2 3) (1 2 3) 

Questo esempio sopra può essere fonte di confusione perché il risultato è stampato come un’espressione s in grado di quote , ed è vero, in questo caso le due syntax sono equivalenti. Ma se diventiamo leggermente più complicati, vedrai che è diverso:

 > (list 1 (+ 1 1) (+ 1 1 1)) (1 2 3) > '(1 (+ 1 1) (+ 1 1 1)) (1 (+ 1 1) (+ 1 1 1)) 

Cosa succede nell’esempio di quote ? Bene, ne discuteremo tra un momento, ma prima diamo un’occhiata alla list . È solo una funzione ordinaria, quindi segue la semantica di valutazione Scheme standard: valuta ciascuno dei suoi argomenti prima che vengano passati alla funzione. Ciò significa che espressioni come (+ 1 1) saranno ridotte a 2 prima di essere raccolte nella lista.

Questo comportamento è visibile anche quando si forniscono variabili alla funzione elenco:

 > (define x 42) > (list x) (42) > '(x) (x) 

Con la list , la x viene valutata prima di essere passata alla list . Con quote , le cose sono più complicate.

Infine, poiché la list è solo una funzione, può essere utilizzata come qualsiasi altra funzione, anche in modi di ordine superiore. Ad esempio, può essere passato alla funzione map e funzionerà in modo appropriato:

 > (map list '(1 2 3) '(4 5 6)) ((1 4) (2 5) (3 6)) 

Il modulo di quote

La citazione, a differenza della list , è una parte speciale di Lisps. La forma di quote è speciale in parte perché ottiene un’abbreviazione di lettore speciale, ' , ma è anche speciale anche senza quella. A differenza della list , la quote non è una funzione, e quindi non ha bisogno di comportarsi come una: ha regole proprie.

Una breve discussione sul codice sorgente Lisp

In Lisp, di cui Scheme e Racket sono derivati, tutto il codice è in realtà costituito da normali strutture di dati. Ad esempio, considera la seguente espressione:

 (+ 1 2) 

Quell’espressione è in realtà una lista e ha tre elementi:

  • il simbolo +
  • il numero 1
  • il numero 2

Tutti questi valori sono valori normali che possono essere creati dal programmatore. È davvero facile creare il valore 1 perché valuta a sé stesso: basta digitare 1 . Ma i simboli e le liste sono più difficili: per impostazione predefinita, un simbolo nel codice sorgente esegue una ricerca di variabili! Cioè, i simboli non sono auto-valutanti :

 > 1 1 > a a: undefined cannot reference undefined identifier 

A quanto pare, però, i simboli sono fondamentalmente solo stringhe e infatti possiamo convertirli tra loro:

 > (string->symbol "a") a 

Le liste fanno anche più dei simboli, perché per impostazione predefinita, una lista nel codice sorgente chiama una funzione! Doing (+ 1 2) guarda il primo elemento nella lista, il simbolo + , cerca la funzione ad esso associata e la invoca con il resto degli elementi nell’elenco.

A volte, però, potresti voler disabilitare questo comportamento “speciale”. Potresti voler semplicemente ottenere l’elenco o ottenere il simbolo senza che venga valutato. Per fare questo, è ansible utilizzare la quote .

Il significato della citazione

Con tutto questo in mente, è abbastanza ovvio cosa fa la quote : si limita a “distriggersre” il comportamento di valutazione speciale per l’espressione che avvolge. Ad esempio, considera la quote un simbolo:

 > (quote a) a 

Allo stesso modo, considera la quote un elenco:

 > (quote (abc)) (abc) 

Non importa quello che dai la quote , lo farà sempre, sempre a risponderti. Ne più ne meno. Ciò significa che se gli dai una lista, nessuna delle sottoespressioni sarà valutata, non aspettarti che siano! Se hai bisogno di una valutazione di qualsiasi tipo, usa la list .

Ora, si potrebbe chiedere: cosa succede se si quote qualcosa di diverso da un simbolo o una lista? Bene, la risposta è … niente! L’hai appena recuperato.

 > (quote 1) 1 > (quote "abcd") "abcd" 

Questo ha senso, dal momento che la quote sputa solo esattamente quello che gli dai. Questo è il motivo per cui i “letterali” come numeri e stringhe sono talvolta chiamati “citazioni auto” nella parlata di Lisp.

Un’altra cosa: cosa succede se quote un’espressione che contiene una quote ? Cioè, cosa succede se ” quote “?

 > (quote (quote 3)) '3 

Cosa è successo la? Bene, ricorda che ' realtà è solo un’abbreviazione diretta per la quote , quindi non è successo niente di speciale! Infatti, se il tuo schema ha un modo per disabilitare le abbreviazioni durante la stampa, apparirà come segue:

 > (quote (quote 3)) (quote 3) 

Non lasciatevi ingannare dal fatto che la quote è speciale: proprio come (quote (+ 1)) , il risultato qui è solo una semplice vecchia lista. In effetti, possiamo ottenere il primo elemento dalla lista: puoi indovinare cosa sarà?

 > (car (quote (quote 3))) quote 

Se hai indovinato 3 , ti sbagli. Ricorda, quote disabilita tutta la valutazione e un’espressione che contiene un simbolo di quote è ancora solo una lista semplice. Gioca con questo nel REPL finché non ti senti a tuo agio.

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

La citazione è incredibilmente semplice, ma può risultare molto complessa a causa di come tende a sfidare la nostra comprensione del modello di valutazione tradizionale. In effetti, è fonte di confusione a causa di quanto sia semplice: non ci sono casi speciali, non ci sono regole. Restituisce esattamente quello che gli dai, esattamente come indicato (da qui il nome “citazione”).


Appendice A: quasiquotation

Quindi, se la citazione disabilita completamente la valutazione, a cosa serve? Bene, oltre a fare liste di stringhe, simboli o numeri che sono tutti noti in anticipo, non molto. Fortunatamente, il concetto di quasiquotation fornisce un modo per uscire dalla citazione e tornare alla valutazione ordinaria.

Le basi sono semplicissime: invece di usare una quote , usa quasiquote . Normalmente, funziona esattamente come la quote in ogni modo:

 > (quasiquote 3) 3 > (quasiquote x) x > (quasiquote ((ab) (cd))) ((ab) (cd)) 

Ciò che rende speciale quasiquote è che riconosce un simbolo speciale, unquote . Ovunque appare unquote nell’elenco, viene sostituito dall’espressione arbitraria che contiene:

 > (quasiquote (1 2 (+ 1 2))) (1 2 (+ 1 2)) > (quasiquote (1 2 (unquote (+ 1 2)))) (1 2 3) 

Questo ti permette di usare quasiquote per build modelli di ordinamento che hanno “buchi” da riempire con unquote . Ciò significa che è ansible includere effettivamente i valori delle variabili all’interno di elenchi quotati:

 > (define x 42) > (quasiquote (x is: (unquote x))) (x is: 42) 

Certamente, usare quasiquote e unquote è piuttosto unquote , quindi hanno le loro abbreviazioni, proprio come ' . In particolare, quasiquote è ` (backtick) e unquote is , (virgola). Con queste abbreviazioni, l’esempio sopra è molto più appetibile.

 > `(x is: ,x) (x is: 42) 

Un ultimo punto: quasiquote in realtà può essere implementato in Racket usando una macro piuttosto pelosa, e lo è. Si espande a usi di list , cons e, naturalmente, quote .


Appendice B: list implementazione e quote in schema

La list implementazione è molto semplice a causa di come funziona la syntax “argomento di rest”. Questo è tutto ciò di cui hai bisogno:

 (define (list . args) args) 

Questo è tutto!

Al contrario, la quote è molto più difficile, infatti è imansible! Sembrerebbe del tutto fattibile, dal momento che l’idea di disabilitare la valutazione suona molto simile alle macro. Eppure un tentativo ingenuo rivela il problema:

 (define fake-quote (syntax-rules () ((_ arg) arg))) 

Prendiamo semplicemente l’ arg e lo sputiamo indietro … ma questo non funziona. Perchè no? Bene, il risultato della nostra macro sarà valutato, quindi tutto è inutile. Potremmo essere in grado di espanderci a qualcosa di simile alla quote espandendo a (list ...) e citando in modo ricorsivo gli elementi, in questo modo:

 (define impostor-quote (syntax-rules () ((_ (a . b)) (cons (impostor-quote a) (impostor-quote b))) ((_ (e ...)) (list (impostor-quote e) ...)) ((_ x) x))) 

Sfortunatamente, però, senza macro procedurali, non possiamo gestire i simboli senza quote . Potremmo avvicinarci usando il syntax-case , ma anche in quel caso, dovremmo solo emulare il comportamento della quote , non replicarla.


Appendice C: convenzioni sulla stampa della racchetta

Quando si provano gli esempi in questa risposta in Racket, è ansible che non vengano stampati come ci si aspetterebbe. Spesso, possono stampare con un indicatore ' , come in questo esempio:

 > (list 1 2 3) '(1 2 3) 

Questo perché Racket, per impostazione predefinita, stampa i risultati come espressioni quando ansible. Cioè, dovresti essere in grado di digitare il risultato nel REPL e ottenere lo stesso valore. Personalmente trovo questo comportamento piacevole, ma può essere fonte di confusione quando cerco di capire la citazione, quindi se vuoi distriggersrlo, chiama (print-as-expression #f) , o cambia lo stile di stampa in “write” nel DrRacket menu lingua.