2010-03-07 16 views
5

Ho appena iniziato a giocare con Clojure e ho scritto una piccola sceneggiatura per aiutarmi a capire alcune delle funzioni. Comincia così:Confuso da "let" in Clojure

(def *exprs-to-test* [ 
    "(filter #(< % 3) '(1 2 3 4 3 2 1))" 
    "(remove #(< % 3) '(1 2 3 4 3 2 1))" 
    "(distinct '(1 2 3 4 3 2 1))" 
]) 

Poi si passa attraverso *exprs-to-test*, li valuta, e stampa l'output come questo:

(doseq [exstr *exprs-to-test*] 
    (do 
     (println "===" (first (read-string exstr)) "=========================") 
     (println "Code: " exstr) 
     (println "Eval: " (eval (read-string exstr))) 
    ) 
) 

Il codice di cui sopra è tutto bene a lavorare. Tuttavia, (read-string exstr) si ripete così ho cercato di usare let per eliminare la ripetizione in questo modo:

(doseq [exstr *exprs-to-test*] 
    (let [ex (read-string exstr)] (
     (do 
      (println "===" (first ex) "=========================") 
      (println "Code: " exstr) 
      (println "Eval: " (eval ex)) 
     ) 
    )) 
) 

Ma questo funziona una volta per il primo elemento nella *exprs-to-test*, poi blocca con un NullPointerException. Perché l'aggiunta di let provoca l'arresto anomalo?

risposta

7

Si dispone di un ulteriore gruppo di parentesi attorno al modulo do. Il tuo codice sta facendo questo:

((do ...)) 

Sta cercando di esecuzione (come una chiamata di funzione) il valore di tutto il modulo do, ma do sta tornando nil, perché l'ultimo println in forma do restituisce nil.

Nota, lo stile di indentazione non è standard. Non si dovrebbe mettere il paren di chiusura sulle proprie linee. E let ha un implicito do quindi non ce n'è bisogno. Prova questo:

user> (doseq [exstr *exprs-to-test*] 
     (let [ex (read-string exstr)] 
      (println "===" (first ex) "=========================") 
      (println "Code: " exstr) 
      (println "Eval: " (eval ex)))) 
=== filter ========================= 
Code: (filter #(< % 3) '(1 2 3 4 3 2 1)) 
Eval: (1 2 2 1) 
=== remove ========================= 
Code: (remove #(< % 3) '(1 2 3 4 3 2 1)) 
Eval: (3 4 3) 
=== distinct ========================= 
Code: (distinct '(1 2 3 4 3 2 1)) 
Eval: (1 2 3 4) 
+0

Che risolto. Grazie anche per il suggerimento di stile indentato. –

1

Brian già risposto alla tua domanda, quindi voglio solo darvi alcune indicazioni generali per la forma let-:

4

Penso che le altre risposte ignorino l'elefante nella stanza: perché lo stai facendo? Ci sono un sacco di cose nel codice che mi fanno preoccupare si stanno forgiando sulla strada sbagliata attraverso l'apprendimento Clojure:

  • Utilizzando attacchi globali (exprs-to-test)
  • Utilizzando doseq/println a provare il codice in sequenza
  • Utilizzare eval

il modo migliore per imparare le API di Clojure è attraverso il REPL. Dovresti configurare il tuo ambiente, sia esso Vim, Emacs o un IDE in modo tale da poter facilmente spostarti avanti e indietro tra il codice statico nei file di testo e un REPL interattivo. Here is a good breakdown of a number of Clojure IDEs.

Ora, per quanto riguarda il codice, alcune cose da ricordare. Innanzitutto, non c'è quasi mai una buona ragione per usare eval. Se ti trovi a farlo, chiediti se è davvero necessario. In secondo luogo, ricorda che Clojure è un linguaggio funzionale e in generale non è necessario utilizzare il set di macro "do".I macro "do" sono utili quando è necessario avere effetti collaterali (nel tuo esempio, l'effetto collaterale è println to * out *) Infine, si dovrebbe anche evitare l'utilizzo di vars globali. Se hai bisogno di usare vars, dovresti prendere in considerazione l'uso della macro dei binding per associare localmente il vars al thread ai valori immutabili, quindi non ci sono problemi di concorrenza.

Consiglio vivamente di dedicare del tempo alla programmazione di Clojure o a un altro riferimento più approfondito a LISP per comprendere veramente lo spostamento necessario nel modo in cui pensi alla programmazione per sfruttare efficacemente Clojure. Il vostro piccolo campione qui mi fa sentire come se steste cercando di scrivere il codice imperativo in Clojure, che non funzionerà affatto bene.

+0

So che i globali sono fuori controllo in questo script a 10 righe, e non userò più le macro "do" e le eval, anche nelle situazioni in cui sono effettivamente necessarie. Prenderò anche il tuo consiglio sul REPL perché ho una memoria impeccabile di lingue che ho usato solo per 10 minuti e non ho bisogno di salvare il mio lavoro per riferimenti futuri. Come professionista esperto di Clojure, la tua non risposta virtuosa mi riempie di vergogna. Proverò più difficile la prossima volta. –

+0

Che commento disgustoso e sarcastico. Stavo cercando di aiutare. Vergognatevi. –

+0

Non penso sia appropriato ignorare la domanda e ragionare su come il codice di un principiante non è perfetto. Sembrava più che stavi soffiando la tua tromba piuttosto che cercare di aiutare. –