empty - groovy map collect
Groovy ''Peeking'' adelante con un iterador? (3)
Tengo varios escenarios en bucles en los que me gustaría ''mirar'' u ''saltar'' al frente mientras repito los ítems para procesarlos.
Un escenario es que estoy enumerando a través de líneas de un archivo, y hay un carácter ''continuación'' al final de una línea que indica combinar la siguiente línea con la línea actual. Si simplemente estoy bucleando, no es demasiado difícil, puedo leer la siguiente línea y encontrar mi contador / índice.
No es tan obvio el patrón para hacer esto con mi iterador. De hecho, quiero consumir la (s) siguiente (s) línea (s) sin salir de mi cierre. Pero ni siquiera estoy seguro de si eso es posible. ¿Hay algún patrón de diseño bueno para este patrón de iteración utilizando un cierre, por lo que no tengo que recurrir a un ciclo menos groovy? ¿Es quizás una forma de iterador con algo de pila para empujar / abrir elementos para procesar?
Interesante pregunta.
El problema clave aquí es que necesita llevar algún estado a través de iteraciones.
Una forma de hacer esto podría ser usar una variable externa (aquí estoy usando una matriz de cadenas y List#each
lugar de un archivo y File#eachLine
, pero deben ser análogas):
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 = ''''
}
}
Otra forma es usar un iterador especialmente diseñado para llevar estado a través de interacciones, como 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) }
En este caso, primero unimos las líneas continuas y luego las procesamos.
En ambos casos, la salida es:
Processing "Single line."
Processing "Long line continuation."
Processing "Single line."
Tuve que implementar algo similar hace algún tiempo. Tenía un archivo grande con datos separados por tubería en cada línea y los datos podían continuar en la siguiente línea, pero solo podía saber si "miraba" la próxima línea. Terminé usando ArrayList como una pila combinada con un método 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
Haría un iterador que estaría a cargo de combinar líneas. Para el ejemplo de continuación de línea, el iterador puede tomar las líneas leídas desde el archivo en su constructor, y luego leer su next
método de las líneas, leyendo adelante cuando encuentra un carácter de continuación, para que los caracteres de continuación se resuelvan antes del siguiente paso en la tubería. Entonces, cualquier máquina de estado que necesite estará contenida dentro del iterador.