Tipos de Datos en Erlang (III): Conjuntos

Siguiendo con el repaso de los tipos de datos que se pueden representar en Erlang a través del uso de los módulos que integra su librería estándar (stdlib) llega el turno de los conjuntos. Los conjuntos son una agrupación de elementos con ciertas propiedades, la que más cabe destacar es que en un conjunto no pueden existir dos elementos idénticos, es decir no existe repetición.

Los conjuntos se pueden emplear cuando necesitamos tener un grupo de elementos con un factor común que nos permita acciones como la unión a otro grupo de elementos, su intersección, realizar restas o saber si un conjunto es subconjunto de otro distinto, por ejemplo.

Podemos encontrar tres módulos que nos proporcionan la funcionalidad de los conjuntos: sets, que es el básico; ordsets, igual que el anterior pero mantiene los elementos del conjunto ordenados; y gb_sets, que es una implementación a través de árboles balanceados. Los tres módulos mantienen una interfaz común aunque gb_sets que será la que veremos en este artículo.

Creando un conjunto

La creación del conjunto la realizamos mediante el uso de la función new/0. Para cualquiera de los tres módulos nos retorna un conjunto vacío de elementos para poder emplearlo con sus funciones correspondientes:

%% conjunto con sets
Set = sets:new().
 
%% conjunto con ordsets
OrdSet = ordsets:new().
 
%% conjunto con gb_sets
GBSet = gb_sets:new().

IMPORTANTE: aunque las funciones serán las mismas de uno a otro no es posible intercambiar el uso de los módulos. Si un conjunto se ha creado con el módulo sets deberá de modificarse o tratarse con las funciones de ese módulo, si se intercambia por otras funciones originaría un error.

Manejando datos

Podemos agregar cualquier tipo de elemento al conjunto. A diferencia de los arrays y los diccionarios en los conjuntos no hay identificador. Cada elemento se identifica a sí mismo por su contenido íntegro.

Para insertar elementos emplearemos la función add_element/2 mientras que eliminarlos lo haremos con del_element/2. Podemos saber si un elemento está dentro del conjunto mediante is_element/2. Un ejemplo:

%% Nuevo conjunto
Set0 = sets:new().
 
%% Insertando elementos
Set1 = sets:add_element(rojo, Set0).
Set2 = sets:add_element(verde, Set1).
Set3 = sets:add_element(azul, Set2).
Set4 = sets:add_element(otro, Set3).
 
%% Eliminamos "otro"
SetN = sets:del_element(otro, Set4).

Conversiones

Las operaciones más comunes para cargar el conjunto y mostrar su contenido son las funciones de conversión a y desde una lista. Esto se hace a través de las funciones to_list/1 y from_list/1. El ejemplo de inicialización anterior se puede realizar de una forma mucho más fácil así:

%% Inicializa un conjunto
Set = sets:from_list([ rojo, verde, azul ]).
 
%% Lo llevamos de nuevo a lista
Lista = sets:to_list(Set).

Operaciones de Conjuntos

Las operaciones de los conjuntos son la unión, intersección y sustracción. Para estas operaciones necesitaremos tener varios conjuntos (dos al menos) y realizar las operaciones correspondientes:

%% creamos dos conjuntos
SetA = sets:from_list([ rojo, verde, azul ]).
SetB = sets:from_list([ verde, amarillo, marron ]).
 
%% union (la suma de ambos)
sets:union(SetA, SetB).
sets:union([SetA, SetB]).
% [ verde,azul,marron,rojo,amarillo ]
 
%% intersección (la parte común a ambos)
sets:intersection(SetA, SetB).
sets:intersection([SetA, SetB]).
% [ verde ]
 
%% sustracción (los elementos del primer conjunto que no estén en el segundo)
sets:subtract(SetA, SetB).
% [ azul, rojo ]
 
%% una diferencia simétrica la podemos conseguir así
SetAm = sets:subtract(SetA, SetB).
SetBm = sets:subtract(SetB, SetA).
SetAll = sets:union(SetAm, SetBm).
% [ azul,marron,rojo,amarillo ]
 
%% averiguar subconjuntos
sets:is_subset(SetBm, SetAll).
% true
sets:is_subset(SetB, SetAll). 
% false

Conclusiones

Las virtudes que proponen estos módulos sobre el uso de las listas se remarcan en la página de ayuda de sets que son principalmente el gestionar una lista de elementos únicos, es decir, que si tienes que insertar 1000 elementos únicos en un contenedor, este es tu gestor.

El empleo de este módulo es bastante simple como se puede ver en lo escrito. Una adición y eliminación básica y las operaciones de conjuntos es lo principal que se realiza con estos módulos y con este tipo de datos.