Bienvenidos sean a este post, es una practica comun para las librerias definir sus propios campos en metatablas, hasta ahora todos los metametodos que hemos visto son para el nucleo de Lua, es la maquina virtual quien detecta cuales valores envueltos tienen metatablas y cual de estas metatablas definen metametodos para tal operacion, sin embargo porque las metatablas son tablas regulares, estas pueden ser usadas por cualquiera, un tipico ejemplo es la funcion tostring, como vimos anteriormente esta funcion representa tablas en un formato bastante simple:

Anuncios
> print({})
table: 0x8d2768
>
Nota: La funcion print siempre llama a tostring para dar formato a su salida.

Cuando formateamos cualquier valor, tostring primero verifica si el valor tiene un metatmetodo __tostring, en este caso tostring llama al metametodo para realizar su trabajo, pasando el objeto como su argumento, sea lo que el metametodo devuelva es el resultado de tostring, siguiendo con nuestro ejemplo de conjuntos donde ya hemos definido una funcion para representar a las cadenas, Conjunto.acadena, ahora solo nos falta crear el campo __tostring en la metatabla:

mt.__tostring = Conjunto.acadena

Despues de este cambio siempre que llamemos a print como un conjunto como su argumento, print llama a tostring que a su vez llama Conjunto.acadena, veamos el siguiente ejemplo:

Anuncios
> s1 = Conjunto.nuevo{10, 4, 5}
> print(s1)
{ 4, 5, 10 }
>

Las funciones setmetatable y getmetatable tambien usan un metacampo, en este caso para proteger metatablas, supongamos que queremos proteger nuestros conjuntos para que ningun usuario pueda ver o cambiar sus metatablas, si seteamos un campo __metatable en la metatabla, getmetatable devolvera el valor de este campo, mientras que setmetatable devolvera un error, agreguemos el siguiente codigo a nuestro codigo:

mt.__metatable = "no es de tu incumbencia"

Si lo verificamos de la siguiente manera obtendremos esta salida:

> s1 = Conjunto.nuevo{}
> print(getmetatable(s1))
no es de tu incumbencia
> setmetatable(s1, {})
stdin:1: cannot change a protected metatable

En este caso observamos las dos conductas que mencionamos, la primera al intentar ver la metatabla de s1 nos devolvio el mensaje que le seteamos, en el segundo caso cuando intentamos modificarlo nos informa que la metatabla esta protegida, veamos el nuevo codigo final:

Anuncios

metamtd01.lua

Conjunto = {}
local mt = {}

function Conjunto.nuevo(l)
        local conjunto = {}
        setmetatable(conjunto, mt)
        for _, v in ipairs(l) do conjunto[v] = true end
        return conjunto
end

function Conjunto.union(a, b)
        local res = Conjunto.nuevo{}
        if getmetatable(a)~=mt or getmetatable(b)~=b then
                error("Intentaste 'agregar' un conjunto con un valor no-conjunto", 2)
        end
        for k in pairs(a) do res[k] = true end
        for k in pairs(b) do res[k] = true end
        return res
end

function Conjunto.interseccion(a, b)
        local res = Conjunto.nuevo{}
        for k in pairs(a) do
                res[k] = b[k]
        end
        return res
end

function Conjunto.acadena(set)
        local l = {}
        for e in pairs(set) do
                l[#l + 1] = e
        end
        return "{ " .. table.concat(l,", ") .. " }"
end

function Conjunto.imprimir(c)
        print(Conjunto.acadena(c))
end

mt.__add = Conjunto.union
mt.__mul = Conjunto.interseccion

mt.__le = function(a, b)
        for k in pairs(a) do
                if not b[k] then return false end
        end
        return true
end

mt.__lt = function(a, b)
        return a <= b and not(b <= a)
end

mt.__eq = function(a, b)
        return a <= b and b <= a
end

mt.__tostring = Conjunto.acadena

mt.__metatable = "no es de tu incumbencia"
Anuncios

En resumen, hoy hemos visto algunas de las funciones internas que propias de las metatablas, cuales son, como se utilizan, cuales son los beneficios que obtenemos, 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