2012-04-24 16 views
18

La sezione API Cheatsheet su Elenchi sembra indicare che '() è un costruttore di elenchi, proprio come (list), ma ho scoperto che in pratica non sono esattamente gli stessi. Ad esempio, dato:Qual è la differenza tra '() e (lista) in Clojure?

(def foo "a") 
(def bar "b") 
(def zip "c") 

La seguente dichiarazione:

(apply str '(foo bar zip)) 

produce l'uscita "foobarzip", che non mi aspetto.

Ma il presunto equivalente:

(apply str (list foo bar zip)) 

produce "abc", come mi aspetto.

Cosa sta succedendo qui? Se esiste una "scorciatoia" per un elenco in Clojure (come {} per le mappe e [] per i vettori), che cos'è?

+0

Non esiste una lista abbreviata per un elenco perché un elenco è di secondaria importanza per Clojure. –

risposta

31

Nelle linee libere, ' (come quote) cita i suoi argomenti, vale a dire li conserva esattamente esattamente come scritto nella loro forma S-exp, incluso non valutare nulla all'interno.

Per dirla in altro modo '(foo bar zip) crea un elenco contenente i simboli foo, bar, zip; mentre (list foo bar zip) crea un elenco contenente i valori di foo, bar, zip. Nel primo caso, str convertirà i simboli stessi in stringhe e quindi li concatenerà.

Come dimostrazione di questo:

=> (def foo "a") 
=> (type (first '(foo))) 
clojure.lang.Symbol 
=> (type (first (list foo))) 
java.lang.String 
+2

@ Jonathan Nella mia esperienza, la maggior parte delle volte è più conveniente usare un vettore piuttosto che un elenco quotato. A prescindere dal richiedere meno tasti, un vettore è più facilmente distinguibile come un elenco non valutato rispetto a un elenco quotato. – user100464

+0

Puoi anche commentare i casi vuoti, ''()' e '(lista)'? C'è qualche differenza tra loro? – Lii

+0

@Lii no, credo che siano uguali. – huon

10

La differenza è che, come si può vedere, la sintassi letterale '() è citato. Ciò significa che i simboli all'interno non vengono valutati. Per utilizzare Lista letterali, mentre la valutazione degli elementi è possibile sfruttare la macro syntax-quote lettore:

user=> (apply str `(~foo ~bar ~zip)) 
"abc" 
3

Quando si scrive:

(def foo "a") 
(def bar "b") 
(def zip "c") 

Hai definito tre simboli: foo, bar e zip associati valori: "a", "b" e "c".

L'associazione è memorizzato all'interno della tabella namsepace, quindi se si utilizza il REPL, lo spazio dei nomi sarebbe user per impostazione predefinita, in modo che la tabella di spazio dei nomi utente sarebbe ora contenere:

{foo 
#'user/foo 
bar 
#'user/bar, 
zip 
#'user/zip} 

si può vedere la tabella di spazio dei nomi fare: (ns-interns *ns*)

Ora, se si scrive: (foo bar zip) all'interno Clojure, ci sta per essere quattro modi diversi questo può essere letto dal lettore. Dovrai specificare per il lettore in che modo dovrebbe essere letto. È qui che entrano in gioco `, ' e list.

caso di nessun indicatore:

(foo bar zip) 

Quando semplicemente scritto senza alcun indicatore, il lettore interpretare questo come un S-espressione e interpreterà foo come una mappatura simbolo per una funzione, con bar e zip come simboli che associano ai valori da passare alla funzione foo.

Nel nostro caso, è un'eccezione:

java.lang.ClassCastException: java.lang.String cannot be cast to clojure.lang.IFn 

Ciò è dovuto al fatto alcuna funzione foo è stata definita, foo è stata associata con "a", che è una stringa, non un IFN (una funzione Clojure).

Se la funzione foo stato definito, sarebbe chiamato foo passando come argomento "b" e "c".

caso di list:

(list foo bar zip) 

Quando si utilizza il simbolo di lista, il lettore è in realtà interpreta questo allo stesso modo a seconda del caso nessun indicatore. Cioè, sta cercando un simbolo list che esegue il mapping a una funzione che assumerà i valori associati associati a foo, bar e zip come argomenti. La funzione list è predefinita da Clojure all'interno dello spazio dei nomi clojure.core; restituisce una lista dei suoi argomenti.

Così, quando il lettore cerca list, è trovare la funzione clojure.core, allora sembra per il simbolo foo, e scopre che si associa a "a", e così via. Una volta trovata la mappatura per i simboli, chiama list e gli passa i valori associati di foo bar zip, che sarebbe "a" "b" "c". Quindi (list foo bar zip) è lo stesso di (list "a" "b" "c").

caso di ':

'(foo bar zip) 

Il ' citazione dice al lettore che la forma che segue è da leggere come è. Nel nostro caso, foo, bar e zip sono simboli e (foo bar zip) è un elenco di simboli. Quindi il lettore interpreterà questo come una lista di simboli.

Ecco perché quando corri (apply str '(foo bar zip)) chiamerà str 'foo 'bar 'zip che ti darà foobarzip. Cioè, convertirà ciascun simbolo nell'elenco di simboli in una rappresentazione String, quindi li concatenerà in un'unica stringa.

Prendendo il modulo così com'è, passa come argomento una lista di simboli, senza valutare i simboli, cioè senza cercare con cosa siano associati. Se hai eseguito (eval '(foo bar zip)), passerai un elenco di simboli a eval e eval valuterà i simboli ai valori e restituirà un elenco dei valori ai quali sono mappati i simboli. Quindi puoi pensare alla citazione ' passando il codice come codice.

caso di `:

`(foo bar zip) 

Questo è un po 'più complicato. Il lettore vedrà il retroquadro ` e risolverà i simboli all'interno dell'elenco di simboli in modo ricorsivo per ottenere un elenco di simboli pienamente qualificati.

Fondamentalmente, quando il lettore cerca i simboli all'interno della tabella dei simboli, lo fa dalla tabella dei simboli dello spazio dei nomi corrente. Un simbolo completo, è un simbolo che include le informazioni sullo spazio dei nomi. Pertanto, quando si esegue `(foo bar zip), il lettore sostituirà tali simboli con quelli completi, trasformandolo in (user.foo user.bar user.zip).

È possibile dire al lettore di valutarne alcuni sugli elementi nell'elenco, mentre si cambiano gli altri in simboli pienamente qualificati. Per fare ciò, è prefisso i simboli che si desidera valutati ~ come in:

`(foo ~bar zip) 

vi darà

(clojure.foo "b" clojure.zip) 

In effetti, il backquote ` è molto simile alla citazione ' in quanto fa non valuta, ma semplicemente restituisce il codice, tranne che manipola il codice per essere restituito un po 'con simboli pienamente qualificanti al suo interno. Ciò ha implicazioni per le macro, dove a volte si potrebbe volere un riferimento completo, recuperare da un altro spazio dei nomi e, a volte, si vuole flessibilità nel dire, cercare questo simbolo nello spazio dei nomi corrente.

Problemi correlati