Bienvenidos sean a este post, asi como las metatablas no solo permiten utilizar operadores arimeticos sino tambien los relacionales, a traves de los siguientes metametodos:
- __eq, igual
- __lt, menos que
- __le, menos que o igual
Estos son los unicos y no hay mas operadores para los otros operadores relacionales, Lua los traduce de esta forma:
a~=b equivale a not(a==b)
a > b equivale a b < a
a >=b equivale a b <= a
Hasta LUA 4.0 todos los operadores de orden eran traducidos a uno solo, traduciendo a<=b a not(b<a), sin embargo esta traduccion es incorrecta cuando tenemos una orden parcial, asi es cuando no todos los elementos de nuestro tipo son apropiadamente ordenadas, un ejemplo son los numeros de punto flotante que no son completamente ordenados en la mayoria de las maquinas porque los valores NaN (Not a Number) de acuerdo con la IEEE 754 standard actualmente adoptada por virtualmente todos los hardwares de punto flotante, NaN representa valores indefinidos tal como un resultado de 0/0, el estandard especifica que cualquier comparacion que implica un NaN deberia devolver un false, esto nos genera que NaN <= x es siempre false pero x < NaN tambien es false, y esto tambien nos producira que la traduccion a <= b a not(b < a) no es valido en este caso.
Tomemos el ejemplo del post anterior con los conjuntos, tenemos un problema similar porque un obvio (y util) significado para <= conjuntos es la contencion de conjuntos porque a <= b implica que a es un subconjunto de la b, con este significado otra vez es posible que ambos a<=b y b < a son falsas, por lo tanto necesitamos implementaciones separadas para __le (menos que o igual) y __lt (menos que), agreguemos el siguiente bloque al codigo del post anterior:
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
Agregamos las dos funciones para __lt y __le, las cuales se encargaran de verificar sus respectivos operadores, en el caso de __le usamos un bucle for donde asignaremos cada elemento del conjunto a en k, y en el bloque usaremos un condicional donde en caso de que no exista esta posicion en el conjunto b devuelve un false, si no ocurrio ninguna injerencia devuelve un true, en el caso de __lt chequea si a es menor que b y la negacion de b es menor que a, si se cumplen estas dos condiciones devuelve un true de lo contrario un false, y por ultimo agregamos a __eq donde verifica si a es menor o igual a b y b es menor o igual que a, en caso de cumplirse las dos condiciones devuelve un true de lo contrario devolvera un false, veamos un ejemplo:
> s1 = Conjunto.nuevo{2, 4}
> s2 = Conjunto.nuevo{4,10,2}
> print(s1 <= s2)
true
> print(s1 < s2)
true
> print(s1 >= s1)
true
> print(s1 > s1)
false
> print(s1 == s2 * s1)
true
>
A diferencia de los metametodos arimeticos, los metametodos relacionales no pueden ser aplicados a tipos mezclados, su conducta para tipos mezclados imita la conducta comun de estos operadores en Lua, si intentamos comparar una cadena con un numero para el pedido nos devolvera un error, similarmente si intentas comparar dos objetos con diferentes metametodos para el pedido, Lua nos devolvera un error.
Una comparacion de igualdad nunca devuelve un error pero si dos objetos tienen distintos metametodos la operacion de igualdad resulta en false, sin llamar a cualquiera de los metametodos, al igual que dijimos antes esta conducta imita la conducta comun de Lua, la cual siempre clasifica a cadenas distinta de los numeros, al margen de sus valores Lua llama a un metametodo de igualdad solo cuando los dos objetos comparados comparten ese metametodo.
Antes de finalizar veamos nuestro nuevo codigo:
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
En resumen, hoy hemos vistos los metametodos relacionales, que son, para que sirven, como se definen, como se implementan y como son los estados que devuelven, 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.50