Bienvenidos sean a este post, hoy hablaremos sobre los iteradores pero que es un iterador? Es cualquier construccion que te permite iterar, iterar significa volver a hacer o en nuestro caso volver a trabajar, sobre los elementos de una coleccion, en Lua nosotros representamos tipicamente iteradores con funciones:

Anuncios

Cada vez que llamamos a una funcion, esta nos retorna el proximo elemento de la coleccion

Roberto Lerusalimschy

Cada iterador necesita mantener algun estado entre las sucesivas llamadas, esto es para indicar donde es y como debe proceder desde ahi, y aqui entra en accion los cierres que nos provee un excelente mecanismo para esta tarea.

Nota: Recuerden que un cierre es una funcion que accede una o mas variables locales desde su entorno cerrado

Dichas variables mantienen su valor a traves de las sucesivas llamadas al cierre permitiendo al cierre recordar donde esta a lo largo de una traversal, pero si nosotros necesitamos crear otro cierre debemos crear tambien variables no locales. Una construccion tipica de un cierre incluye a dos funciones: la funcion en si misma y una fabrica o factory, la funcion que crea el cierre, analicemos el siguiente ejemplo:

function valores (f)
	local i = 0
	return function() i = i + 1; return f[i] end
end

Este ejemplo es un iterator bien simple que a diferencia de ipairs no nos devuelve el indice sino solamente el valor, en este caso la fabrica es valores porque cada vez que llamamos a esta fabrica la misma crea un nuevo cierre (el iterador en si mismo), esto provoca que el cierre mantenga su estado en las variables externas f e i, y cada vez que llamamos al iterator devolvera el proximo valor de la lista t, una vez pasado el ultimo elemento el iterator devuelve un valor nil lo cual simboliza el final de la iteracion, ese iterator podriamos usarlo con un bucle while como veremos a continuacion:

t = {10, 20, 30}
iter = valores(t)
while true do
	local elemento = iter()
	if elemento == nil then break end
	print(elemento)
end
Anuncios

Primero crearemos una tabla con tres datos, luego crearemos un variable llamada iter e iniciaremos el iterator valores y le informaremos el valor de t, despues usaremos un bucle while infinito, en este caso crearemos una variable local llamada elemento y le asignamos el iterator iter para ser llamado, despues tendremos un condicional donde verificara si elemento es igual a nil, en caso de ser verdadero ejecutara break para salir del bucle sino mostrara en pantalla el valor de elemento, una forma mas sencilla de hacer el ejemplo anterior es utilizar el for generico porque al fin y al cabo lo tenemos para eso, no?, veamos el codigo:

t = {10, 20, 30}
for elemento in valores(t) do
	print(elemento)
end

Como pueden ver se simplifico bastante nuestro codigo, como vimos en este post este tipo de for trabaja la iteracion de forma interna, por esto no es necesaria la variable iter, tambien la otra propiedad de este for es que mira todos los elementos hasta que encuentra un nil y sale del mismo, esto ocasiona que el condicional ya no es necesario, veamos la salida de este programa:

tinchicus@dbn001dsk:~/lenguaje/lua$ lua5.3 prueba.lua
10
20
30
tinchicus@dbn001dsk:~/lenguaje/lua$

Antes de continuar veamos como quedo nuestro codigo final:

prueba.lua

function valores (f)
        local i = 0
        return function() i = i + 1; return f[i] end
end

t = {10, 20, 30}
for elemento in valores(t) do
        print(elemento)
end

Hasta aca hemos visto un ejemplo simple de iterator pero ahora veremos otro iterator un poco mas complejo que nos permitira atravesar todas las palabras de una archivo de entrada, para ello usaremos el siguiente codigo:

Anuncios

iterator.lua

function palabrasTodas()
        local linea = io.read()
        local pos = 1
        return function()
                while linea do
                        local i,f = string.find(linea, "%w+", pos)
                        if i then
                                pos = f + 1
                                return string.sub(linea, i, f)
                        else
                                linea = io.read()
                                pos = 1
                        end
                end
                return nil
        end
end

for palabra in palabrasTodas() do
        print(palabra)
end

Primero hablemos de la funcion, en este caso primero crearemos dos variables locales llamadas linea y pos, las cuales almacenaran el texto ingresado y la posicion en la linea respectivamente, el texto en linea sera ingresado por medio de io.read y pos iniciara con un valor de 1, como dijimos en otras ocasiones el valor inicial de Lua, esto nos posibilitara siempre generar la siguiente palabra, el siguiente bloque es el iterator en si, veamos al mismo:

        return function()
                while linea do
                        local i,f = string.find(linea, "%w+", pos)
                        if i then
                                pos = f + 1
                                return string.sub(linea, i, f)
                        else
                                linea = io.read()
                                pos = 1
                        end
                end
                return nil
        end
Anuncios

En este caso usaremos un bucle while donde verificara que existe algo en linea, primero creara dos variables locales llamadas i y f (por inicio y final) donde usaremos la funcion find de string, en este primero pasaremos a linea (el cual va a ser el texto donde buscaremos), luego pasaremos el parametro (en este caso usamos un parametro que busca coincidencias de varios caracteres alfanumericos), y por ultimo le informamos la posicion de inicio, si encuentra alguna palabra actualizara los valores de i y f a las posicones donde se encontro la palabra, primer y ultimo caracter respectivamente, despues tendremos un condicional donde si i tiene un valor quera decir que existe una “palabra” donde pasara a actualizar a pos que le asignara el valor de f y le sumara 1, despues utilizara sub para recortar la palabra de linea por medio de los valores de i y f, por ultimo devolviendo de la funcion ese valor, de lo contrario si no recibio nada procedera a solicitar otra linea y reiniciara a pos al valor 1, si salimos del while porque no encontro mas lineas devuelve un nil y sale definitivamente de la funcion, por ultimo usaremos un bucle for generico para crear palabra de las almacenadas en linea, y la mostraremos en pantalla, probemos el programa para ver su salida en el siguiente video

Como pueden ver no importa la cantidad de palabras que ingresemos siempre nos separara las mismas, como pueden ver solamente las palabras y omite los caracteres como espacio, comas o signos de pregunta y/o interrogacion.

Nota: Para salir del bucle en el programa presionen Ctrl+D.
Anuncios

En resumen, hoy hemos visto como crear nuestros propios iteradores, como trabajan con los cierres, como un iterador simple nos da una pequeña ayuda, despues vimos un iterador msa complejo que nos permitio obtener todas las palabras de una linea, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Tambien podes donar

Es para mantenimiento del sitio, gracias!

$1.00