2012-01-11 9 views
12

Ho imparato Clojure e perplesso dal seguente:Interpellato: Clojure per loop con: while -> comportamento inaspettato?

user=> (for [a (range 1 4) b (range 1 4)] [a b]) 
([1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3]); _no surprise here_ 

Aggiungiamo :while (not= a b), mi aspetto di vedere una lista vuota come il ciclo dovrebbe smettere se la condizione è falsa. In questo caso è il primo oggetto in cui a = b = 1. Vediamo:

user=> (for [a (range 1 4) b (range 1 4) :while (not= a b) ] [a b]) 

([2 1] [3 1] [3 2]) ; _surprise!_ 

Modifica :while-:when per filtrare (= a b) coppie

user=> (for [a (range 1 4) b (range 1 4) :when (not= a b) ] [a b]) 
([1 2] [1 3] [2 1] [2 3] [3 1] [3 2]); _expected_ 

Qualcuno potrebbe spiegare perché (for [ ... :while ..] ...) si comporta come questo?

sto usando Clojure 1.3 su OS X.

Grazie e ci scusiamo per la mancanza di formattazione. Questo è il mio post vergine su StackOverflow.

risposta

9

Diamo un'occhiata ad ogni iterazione.

a = 1 
    b = 1 -> a == b, break because of while 

a = 2 
    b = 1 -> a != b, print [2 1] 
    b = 2 -> a == b, break because of while 

a = 3 
    b = 1 -> a != b, print [3 1] 
    b = 2 -> a != b, print [3 2] 
    b = 3 -> a == b, break because of while 
+0

Grazie, Nikita. Quindi se: mentre si applica solo al ciclo interno, come faccio a farlo applicare anche al ciclo esterno? Il problema che ho è: (per [da [: a: b: c: d: e: f] a [: a: b: c: d: e: f]: let [percorso (find-path ab)] : while path) path); il ciclo dovrebbe fermarsi quando il percorso è zero. – jbear

+0

Mi spiace, intendevo (per [da [: a: b: c: d: e: f] a [: a: b: c: d: e: f]: let [percorso (trova-percorso da a) ]: percorso percorso]) – jbear

+0

@jbear, non so :(Potrebbe essere meglio creare sequenze pigre di percorsi e prendere mentre non sono nulli.Non sono sicuro che 'for' è pigro –

3

La condizione :while in for termina solo il più interno loop. Io uso sempre for, ma :while così raramente che non me ne sono mai reso conto; grazie per la bella domanda! Tristemente penso che il meglio che puoi fare sia racchiudere un take-while attorno allo for, dal momento che vuoi uno stop-counter "globale" sulla sequenza di output, non uno stop-counter su una delle sequenze di input che stai iterando al di sopra di. Per esempio:

(->> (for [a (range 1 4) 
      b (range 1 4)] 
     [a b]) 
    (take-while (fn [[a b]] (not= a b)))) 

() 
+1

Giusto per essere chiari, il test': while' si applica alla sequenza immediatamente precedente, non necessariamente il ciclo più interno –

+0

Grazie per il chiarimento, Alex. Dando il risalto della lingua, Non potei fare a meno di sentirmi un po 'deluso dal fatto che uno dei blocchi fondamentali - la comprensione dell'elenco - dovesse risultare piuttosto limitante, ma spero che con l'apprendimento avrò modo di scoprire altri modi migliori di farlo, con grande aiuto da tutti – jbear

+0

@jbear In realtà è meno limitante di così essere l'altro modo In questo modo, puoi facilmente racchiudere un'espressione 'for' in un" take-while "se vuoi il comportamento" globale ". Ma se il comportamento "globale" fosse ciò che 'for /: while' ha fatto, come avresti recuperato l'attuale comportamento" intermedio "? Dovresti abbandonare 'for' interamente e costruire un nido di mappa di topi, mapcat, filtro, take-while. – amalloy