2013-04-15 17 views
7

Ho vari scenari nei cicli in cui vorrei "sbirciare" o "saltare" avanti mentre iterando attraverso gli elementi per l'elaborazione.Groovy 'Peeking' avanti con un iteratore?

Uno scenario è che sto enumerando attraverso le righe di un file e c'è un carattere di "continuazione" alla fine di una riga che indica di combinare la riga successiva con la riga corrente. Se sto semplicemente eseguendo un ciclo che non è troppo difficile, posso semplicemente leggere la riga successiva e sfogliare il mio contatore/indice.

Non è così ovvio lo schema per farlo con il mio iteratore. Voglio effettivamente consumare la prossima linea (s) senza uscire dalla mia chiusura. Ma non sono nemmeno sicuro che sia possibile. Ci sono dei buoni schemi di progettazione per questo schema di iterazione usando una chiusura, quindi non devo ricorrere a un loop meno groovy? È forse una forma di iteratore con qualche stack per spingere/scoppiare gli oggetti per l'elaborazione?

+3

Generalmente uso decoratore intorno all'iteratore simili: https://gist.github.com/ataylor284/5389604 – ataylor

risposta

1

Farei un iteratore che sarebbe responsabile della combinazione delle linee. Per l'esempio di continuazione di riga, l'iteratore può prendere le righe lette dal file nel suo costruttore, quindi leggere il suo metodo next dalle righe, leggere in anticipo quando trova un carattere di continuazione, in modo che i caratteri di continuazione vengano risolti prima del successivo passo nella pipeline. Quindi qualsiasi macchina di stato di cui hai bisogno dovrebbe essere contenuta all'interno dell'iteratore.

1

Ho dovuto implementare qualcosa di simile qualche tempo fa. Avevo un file di grandi dimensioni con dati separati da pipe su ogni riga e i dati potevano continuare nella riga successiva, ma potevo solo sapere se ho "sbirciato" la riga successiva. Ho terminato di utilizzare ArrayList come stack combinato con un metodo peek:

def list = [1, 2, 3, 4, 5, 6, 7] 

list.metaClass.peek = { delegate[-1] } 

assert list.pop() == 7 
assert list.pop() == 6 
assert list.peek() == 5 
assert list.peek() == 5 
assert list.pop() == 5 
assert list.pop() == 4 
assert list.peek() == 3 
0

Interessante domanda.

Il problema chiave qui è che è necessario portare qualche stato attraverso le iterazioni.

Un modo per farlo potrebbe essere quella di utilizzare una variabile esterna (qui sto usando un array di stringhe e List#each invece di un file e File#eachLine, ma dovrebbe essere analogo):

def lines = [ 
    "Single line.", 
    "Long \\", 
    "line \\", 
    "continuation.", 
    "Single line." 
] 

def processLine(line) { println "Processing \"$line\""} 

def continuation = '' 
lines.each { line -> 
    line = continuation + line 
    if (line.endsWith('\\')) { 
    continuation = line.take(line.size() - 1) 
    } 
    else { 
    processLine(line) 
    continuation = '' 
    } 
} 

altro modo è quello di utilizzare un iteratore appositamente progettato per trasportare stato però interations, come Collection#inject:

lines = lines.inject([]) { list, line -> 
    if (list && list[-1].endsWith('\\')) 
    list[-1] = list[-1][0..-2] + line 
    else 
    list << line 
    list 
} 

lines.each { processLine(it) } 

In questo caso stiamo prima che congiunge le linee continue e quindi il loro trattamento.

In entrambi i casi il risultato è:

Processing "Single line." 
Processing "Long line continuation." 
Processing "Single line."