Bienvenidos sean a este post, continuando con el entorno de Lua hoy hablaremos sobre la declaracion de variables globales pero en realidad estas no necesitan ser declaradas y aunque esto es util para pequeños programas en programas mas grandes un tipo simple puede causar errores que sean dificiles de encontrar.

Anuncios

Sin embargo podemos cambiar esta conducta si nosotros queremos porque Lua mantiene sus variables globales en una tabla regular y nosotros podemos usar metatablas para cambiar su conducta cuando accedes a variables globales, un primer enfoque simplemente detecta cualquier acceso para claves ausentes en tablas globales:

setmetatable(_G, {
	__newindex = function(_, n)
		error("Intentas escribir una variable no declarada " .. n, 2)
	end,
	__index = function(_, n)
		error("Intentas leer una variable no declarada " .. n, 2)	
	end,
})

Despues de este codigo cualquier intento de acceder a variables globales no declaradas (o no existentes) disparara un error:

> print(a)
stdin:1: Intentas leer una variable no declarada a
Nota: Puede suceder que despues del codigo anterior, el interprete salga con el error:
Intentas leer una variable no declarada _PROMPT

Para evitar esto les recomiendo definir a la variable de esta forma:
_PROMPT = {}
Anuncios

Pero como declaramos nuevas variables? Una opcion es usar rawset el cual esquiva al metametodo:

function declarar(nombre, valorini)
	rawset(_G, nombre, valorini or false)
end
Nota: El or con false asegura que la nueva global siempre tenga un valor diferente de nil.

Una manera mas simple es permitir asignaciones a variables globales en el chunk principal asi que declaramos variables de esta forma:

a = 1

Para chequear si la asignacion es en el chunk principal, para ello podemos usar la libreria debug por medio de debug.getinfo(2, “S”) devuelve una tabla cuyo campo what dice si la funcion que llamo al metametodo es un chunk principal, una funcion regular de Lua o una funcion de C, por medio de esta funcion podemos reescribir el metametodo __newindex como se ve a continuacion:

__newindex = function(t, n, v)
	local p = debug.getinfo(2, "S").what
	if p ~= "main" and ~= "C" then
		error("Intentas escribir una variable no declarada " .. n, 2)
	end
	rawset(t, n, v)
end
Anuncios

Esta nueva version tambien acepta asignaciones de codigo C como esta especie de codigo usualmente sabe que esta haciendo para chequear si una variable existe no podemos simplemente compararla a nil porque si es nil el acceso lanzara un error en su lugar usamos a rawget el cual evita el metametodo:

if rawget(_G, var) == nil then
	-- 'var' no esta declarada
	...
end

De esta forma nuestro esquema no permite variables globales con valores nil, por lo que seran automaticamente consideradas como no declaradas pero no es un verdadero problema corregir esto solo necesitamos un tabla auxiliar que mantenga el nombre de las variables declaradas asi cuando un metametodo sea llamado verifica en esta tabla si la variable esta declarada o no, veamos el siguiente codigo:

local nombredeclarados = {}

setmetatable(_G, {
	__newindex = function(t, n, v)
		if not nombredeclarados[n] then
			local p = debug.getinfo(2, "S").what
			if p ~= "main" and p ~= "C" then
				error("Intentas escribir una variable "
				+ "no declarada " .. n, 2)
			end
			nombredeclarados[n] = true
		end
		rawset(t, n, v)
	end,
	__index = function(_, n)
		if not nombredeclarados[n] then
			error("Intentas leer una variable no declarada " .. n, 2)
		else
			return nil
		end
	end,
})

De esta forma inclusive una asignacion como x = nil es suficiente para declarar una variable global, para ambas soluciones la sobrecarga es despreciable, con la primera solucion los metametodos nunca son llamados durante la operacion normal, en el segundo podrian ser llamado pero solo cuando el programa accede una variable construyendo un nil, la distribucion de Lua viene con un modulo llamado strict.lua que implementa un chequeo de las variables globales que usan el codigo que recien vimos y es un buen habito usarlo cuando desarrollas codigo en Lua.

Anuncios

En resumen, hoy hemos visto como son las declaraciones de variables globales, para que se hace, como se utiliza y algunos ejemplos practicos, 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