Bienvenidos sean a este post, como su nombre lo implica es un iterador que no mantiene ningun estado por si mismo pero nosotros podemos usarlos en multiples bucles para evitar el costo de crear nuevos cierres, y por cada iteracion el bucle del for llama a su funcion iteradora con dos argumentos: el estado invariable y la variable de control, en cambio el iterador sin estado genera el siguiente elemento por la iteracion solo usando estos dos tipos de valores, un ejemplo tipico de este tipo de iterador es ipairs, el cual “iteractua” sobre todos los elementos de un array:

Anuncios
a = { "uno", "dos", "tres" }
for i, v in ipairs(a) do
	print(i, v)
end

El estado de la iteracion es la tabla siendo atravesado (que es el estado invariable que no cambia durante todo el bucle), ademas esta el indice actual (la variable de control), por esto tanto ipairs (fabrica) como el iterator son bien simples y podriamos escribirla en Lua como se ve a continuacion:

local function iter(a, i)
	i = i + 1
	local v = a[i]
	if v then
		return i, v
	end
end

function ipairs(a)
	return iter, a, 0
end

Cuando Lua llama a ipairs(a) en un bucle for este obtiene tres valores, la funcion iter es el iterador, a es estado invariable y cero es el valor inicial de la variable de control, luego de esto Lua llama a iter(a,0) lo cual resulta en 1,a[1] (salvo que a[1] sea nil), en la segunda iteracion se llama a iter(a,1) lo cual resultara en 2,a[2] y asi hasta obtener el primer elemento nil. La funcion pairs, la cual iteractua sobre todos los elementos de una tabla, es similar con la excepcion de que la funcion iteradora es la funcion next que es una funcion primitiva de Lua:

function pairs(t)
	return next, t, nil
end

La llamada next(t, k), donde k es la clave de la tabla t, devuelve la proxima clave de la tabla en un orden arbitrario ademas del valor asociado a esta clave como un segundo valor de retorno, la llamada next(t, nil) devuelve un primer par pero cuando no existen mas pares next devuelve nil, asi como algunas personas prefieren usar next directamente en lugar de pairs:

Anuncios
for k, v in next, t do
	... instrucciones ...
end

Recuerden que la lista de expresiones de un bucle for esta ajustada a tres resultados que son next, t y nil lo cual es exactamente lo recibido por Lua cuando llamada a pairs(t).

Otro interesante ejemplo de iterador sin estado es un iterador que debe atravesar una lista enlazada, a pesar de haber comentado que una lista enlazada sea frecuente en Lua en algunas oportunidades debemos usarlas, veamos el siguiente caso:

local function getnext(lista, nodo)
	return not nodo and lista or nodo.next
end

function atravesar(lista) return getnext, lista, nil end

El truco aca es usar al nodo principal de lista como estado invariable (el segundo valor regresado por atravesar) y el nodo actual es la variable de control, cuando llamamos por primera vez al iterador getnext, nodo tendra un valor de nil, por esto la funcion retornara lista como el primer nodo, en consecuencia la siguiente llamada nodo ya no sera nil produciendo que el iterador devuelve a nodo.next como se espera, en este caso es trivial usar el iterador, veamos el siguiente codigo:

lista = nil
for linea in io.lines() do
	lista = { val = linea, next = lista }
end

for nodo in atravesar(lista) do
	print(nodo, val)
end
Anuncios

En resumen, hoy hemos visto otro tipo de iterador, como es su sintaxis, su forma de trabajar, su equivalencia, su equivalencia mas primitiva y algunos ejemplos para su uso, 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