2016-03-08 13 views
6

in Clojure, si può iterare su una successione con l'aiuto della funzione for o simile con doseq per effetti collaterali e ottenere nil come valore di ritorno:Utilizzo doseq/i con sequenze infinite

(doseq [x (range 3)] 
    (prn x)) 

; 0 
; 1 
; 2 

per la caso la sequenza è infinita, c'è un modo per introdurre una condizione di rottura:

(doseq [x (range) :while (< x 3)] 
    (prn x)) 

Questo produrrebbe la stessa uscita di cui sopra.

Come specialità esiste un comportamento molto interessante quando si utilizza più di una sequenza. Come la chiama la documentazione: "Le collezioni sono iterate in modo annidato, all'estrema destra".

(doseq [x (range 3) y (range 3)] 
    (prn x y)) 

; 0 0 
; 0 1 
; 0 2 
; 1 0 
; 1 1 
; 1 2 
; 2 0 
; 2 1 
; 2 2 

Cosa succede se le sequenze sono di nuovo infinite. Quando l'ultimo è infinito, funziona abbastanza bene. questo funzionerebbe ugualmente come esempio prima:

(doseq [x (range 3) y (range) :while (< y 3)] 
    (prn x y)) 

Se il primo è infinito, l'uscita risultante è come previsto, ma per qualche motivo l'anello non si ferma dopo l'ultima riga viene stampata. In altre parole: il repl continua a funzionare.

(doseq [x (range) y (range 3) :while (< x 3)] 
    (prn x y)) 

Qualcuno può spiegare questo comportamento?

+0

ha dato un'occhiata a '(source doseq)' - e facoltativamente su '(macroexpand ')'? – birdspider

+0

oh, ho appena dato un'occhiata. Un po 'troppo profondo per la mia conoscenza fino ad ora ... –

risposta

13

Questo non ha senso:

(doseq [x (range) 
     y (range 3) 
     :while (< x 3)] 
    (prn x y)) 

Dovrebbe essere:

(doseq [x (range) 
     :while (< x 3) 
     y (range 3)] 
    (prn x y)) 

... che termina.

pensare in questo modo:

(doseq [x (range)] 
    (doseq [y (range 3) 
      :while (< x 3)] 
    (prn x y))) 

vs:

(doseq [x (range) 
     :while (< x 3)] 
    (doseq [y (range 3)] 
    (prn x y))) 

Nella versione originale, ciclo esterno è infinito, avendo che :while nei cicli interni non fa la differenza. Il ciclo continua, non fa nulla. Nella versione fissa :while termina il ciclo esterno.

+0

sembra buono e funziona perfettamente! grazie per la spiegazione .. –

+1

Wow, non ho mai saputo che l'ordine/posizionamento della condizione era significativo. –

0

Data l'eccellente spiegazione di muhuk, sembra un buon argomento per essere esplicito e non sovra-comprimere il codice. Quindi forse in questo modo:

(doseq [x (range 3)] 
    (doseq [y (range 3)] 
    (prn x y))) 

se è possibile controllare la sequenza di ingresso, o come questo:

(let [x-vals (range) 
     y-vals (range) ] 
    (doseq [x x-vals :while (< x 3)] 
    (doseq [y y-vals :while (< y 3)] 
     (prn x y)))) 

se la sequenza di ingresso (ad esempio x-vals o y-vals) è fornito dal chiamante.