2010-01-14 4 views
5

Sto lavorando con clojure e mentre mi sono dilettato con le lisps in precedenza, ho problemi a trovare un modo pulito per annidare le istruzioni in dichiarazioni cond. Ad esempio, si consideri la seguente funzione:let inside cond

(defn operate-on-list [xs] 
    (let [[unpack vector] (first xs)] 
    (cond 
     [(empty? xs) 'empty 
     unpack vector 
     :else (operate-on-list (rest xs))]))) 

E 'un'operazione ricorsiva abbastanza standard in un elenco, ma ha bisogno di fare un certo lavoro sul primo elemento della lista prima che funziona con i contenuti. Il problema, ovviamente, è che la lista potrebbe essere vuota.

In questo esempio, non sarebbe difficile cambiare unpack-((first xs) 0) e vector-((first xs) 1), ma questo diventa rapidamente brutto se più lavoro deve essere fatto sui primi (xs).

Esiste un modo per utilizzare efficacemente un'istruzione Let in modo parziale attraverso un cond?

Grazie.

-Nate

+0

Hi - non è chiaro per me quello che sta cercando di ottenere qui. Potresti trovare questo interessante http://www.assembla.com/spaces/clojure/tickets/200 e http://groups.google.com/group/clojure/browse_thread/thread/c1097ce07506fc39 Si prega di prendere in considerazione l'indicazione della funzione presunta da fare nella tua domanda, e alcuni esempi di input e output. La sintassi non assomiglia a una dichiarazione di cond valida in quanto il corpo del cond è racchiuso in un vettore. –

risposta

11

In casi come questi, si sta meglio fuori usando if-let:

(defn operate-on-list [xs] 
    (if-let [[unpack v] (first xs)] 
    (cond 
     unpack v 
     :else (operate-on-list (rest xs))))) 

Questo codice cammina la data lista ss-grado (lista, vettore, array ...) di vettori e restituisce il secondo elemento del primo vettore il cui primo elemento è vero (ovvero non false o nil). nil viene restituito se non viene trovato alcun vettore di questo tipo.

Nota che vector è una funzione incorporata, quindi ho scelto v come nome della variabile, nel caso in cui la necessità di utilizzare la funzione nel corpo si presenti in futuro. Ancora più importante, stai usando troppe parentesi nella sintassi cond; risolto in questa versione.

UPDATE: Due cose aggiuntive pena notare merito if-let:

  1. Il modo if-let opere, se (first xs) sembra essere nil (false sarebbe lo stesso), il legame destrutturazione non avviene, così Clojure non si lamenterà di non essere in grado di vincolare nil a [unpack v].

  2. Inoltre, if-let accetta una clausola else (in cui non è possibile fare riferimento alle variabili legate in if-let attacchi vettore - anche se siete nella clausola altra cosa, si sa che dove false o nil comunque).

+2

Vedere anche "when-let" che è più idiomatico quando si ha solo un ramo. – kotarak

+0

Una seconda nota: attenzione che "if-let" funziona qui, perché la lista dovrebbe contenere vettori. In generale '(when-let [x (first s)] ...)' è * non * un sostituto di '(when-let [s (seq s)] (let [f (first s)] ...)) '. – kotarak

2

Un po 'come questo, con un let all'interno del campo di applicazione della cond?

(defn operate-on-list [list] 
    (let [ el_first (first list) ] 
    (cond 
     (nil? el_first) (println "Finished") 
     :else (do 
     (let [ list_rest (rest list) ] 
       (println el_first) 
       (operate-on-list list_rest)))))) 

(operate-on-list '(1 2 3)) 

L'output è:

1 
2 
3 
Finished 
+4

Il tuo 'do' è superfluo. –

+0

@Brian Carper: hai ragione; puoi semplicemente omettere il do. Era una sbornia da Elisp da parte mia (in particolare, progn). –