Las unidades genéricas en Ada son una característica poderosa que permite la creación de componentes de software reutilizables y flexibles. Las unidades genéricas pueden ser procedimientos, funciones, paquetes o tareas que se parametrizan con tipos, constantes, subprogramas u otras unidades genéricas. Esto permite escribir código que puede adaptarse a diferentes tipos de datos y comportamientos sin duplicar el código.
Conceptos Clave
- Plantillas Genéricas: Definición de una unidad genérica que puede ser instanciada con diferentes parámetros.
- Instanciación: Proceso de crear una versión específica de una unidad genérica con parámetros concretos.
- Parámetros Genéricos: Tipos, constantes, subprogramas u otras unidades genéricas que se pasan a la unidad genérica.
Definición de Unidades Genéricas
Paquetes Genéricos
Un paquete genérico se define utilizando la palabra clave generic
seguida de la lista de parámetros genéricos. Aquí hay un ejemplo de un paquete genérico que define una pila (stack):
generic type Element_Type is private; package Generic_Stack is procedure Push(Item : in Element_Type); procedure Pop(Item : out Element_Type); function Is_Empty return Boolean; end Generic_Stack;
Instanciación de Paquetes Genéricos
Para usar un paquete genérico, se debe instanciar con tipos específicos. Aquí hay un ejemplo de cómo instanciar el paquete Generic_Stack
con un tipo específico:
Procedimientos y Funciones Genéricos
Los procedimientos y funciones también pueden ser genéricos. Aquí hay un ejemplo de una función genérica que encuentra el máximo de dos valores:
generic type T is private; with function "<" (Left, Right : T) return Boolean; function Max (X, Y : T) return T;
Instanciación de Procedimientos y Funciones Genéricos
Para instanciar una función genérica, se debe proporcionar el tipo y la función de comparación:
Ejemplo Práctico
Definición de un Paquete Genérico
Vamos a definir un paquete genérico para una lista enlazada simple:
generic type Element_Type is private; package Generic_Linked_List is type Node; type Node_Ptr is access Node; type Node is record Data : Element_Type; Next : Node_Ptr; end record; procedure Insert_First(List : in out Node_Ptr; Item : in Element_Type); procedure Delete_First(List : in out Node_Ptr; Item : out Element_Type); function Is_Empty(List : Node_Ptr) return Boolean; end Generic_Linked_List;
Implementación del Paquete Genérico
package body Generic_Linked_List is procedure Insert_First(List : in out Node_Ptr; Item : in Element_Type) is New_Node : Node_Ptr := new Node; begin New_Node.Data := Item; New_Node.Next := List; List := New_Node; end Insert_First; procedure Delete_First(List : in out Node_Ptr; Item : out Element_Type) is Temp : Node_Ptr; begin if List /= null then Item := List.Data; Temp := List; List := List.Next; Free(Temp); else raise Constraint_Error with "List is empty"; end if; end Delete_First; function Is_Empty(List : Node_Ptr) return Boolean is begin return List = null; end Is_Empty; end Generic_Linked_List;
Instanciación y Uso del Paquete Genérico
with Generic_Linked_List; procedure Test_Linked_List is package Integer_Linked_List is new Generic_Linked_List(Element_Type => Integer); use Integer_Linked_List; List : Node_Ptr := null; Item : Integer; begin Insert_First(List, 10); Insert_First(List, 20); Insert_First(List, 30); while not Is_Empty(List) loop Delete_First(List, Item); Put_Line(Integer'Image(Item)); end loop; end Test_Linked_List;
Ejercicios Prácticos
Ejercicio 1: Paquete Genérico de Cola
- Definición: Define un paquete genérico para una cola (queue) que permita operaciones de encolar (enqueue) y desencolar (dequeue).
- Instanciación: Instancia el paquete genérico con el tipo
Integer
. - Uso: Escribe un procedimiento que use la cola para almacenar y procesar una serie de números enteros.
Solución del Ejercicio 1
Definición del Paquete Genérico
generic type Element_Type is private; package Generic_Queue is type Node; type Node_Ptr is access Node; type Node is record Data : Element_Type; Next : Node_Ptr; end record; type Queue is record Head : Node_Ptr; Tail : Node_Ptr; end record; procedure Enqueue(Q : in out Queue; Item : in Element_Type); procedure Dequeue(Q : in out Queue; Item : out Element_Type); function Is_Empty(Q : Queue) return Boolean; end Generic_Queue;
Implementación del Paquete Genérico
package body Generic_Queue is procedure Enqueue(Q : in out Queue; Item : in Element_Type) is New_Node : Node_Ptr := new Node; begin New_Node.Data := Item; New_Node.Next := null; if Q.Tail /= null then Q.Tail.Next := New_Node; else Q.Head := New_Node; end if; Q.Tail := New_Node; end Enqueue; procedure Dequeue(Q : in out Queue; Item : out Element_Type) is Temp : Node_Ptr; begin if Q.Head /= null then Item := Q.Head.Data; Temp := Q.Head; Q.Head := Q.Head.Next; if Q.Head = null then Q.Tail := null; end if; Free(Temp); else raise Constraint_Error with "Queue is empty"; end if; end Dequeue; function Is_Empty(Q : Queue) return Boolean is begin return Q.Head = null; end Is_Empty; end Generic_Queue;
Instanciación y Uso del Paquete Genérico
with Generic_Queue; with Ada.Text_IO; use Ada.Text_IO; procedure Test_Queue is package Integer_Queue is new Generic_Queue(Element_Type => Integer); use Integer_Queue; Q : Queue := (Head => null, Tail => null); Item : Integer; begin Enqueue(Q, 10); Enqueue(Q, 20); Enqueue(Q, 30); while not Is_Empty(Q) loop Dequeue(Q, Item); Put_Line(Integer'Image(Item)); end loop; end Test_Queue;
Conclusión
En esta sección, hemos aprendido sobre las unidades genéricas en Ada, cómo definirlas, instanciarlas y usarlas. Las unidades genéricas son una herramienta poderosa para crear componentes de software reutilizables y flexibles. A través de ejemplos prácticos, hemos visto cómo aplicar estos conceptos en la creación de estructuras de datos genéricas como pilas y colas. En el siguiente módulo, exploraremos los paquetes hijos y cómo se pueden utilizar para organizar y modularizar aún más el código en Ada.
Curso de Programación en Ada
Módulo 1: Introducción a Ada
Módulo 2: Conceptos Básicos
- Variables y Tipos de Datos
- Operadores y Expresiones
- Estructuras de Control
- Bucles en Ada
- Subprogramas: Procedimientos y Funciones
Módulo 3: Tipos de Datos Avanzados
Módulo 4: Programación Modular
Módulo 5: Concurrencia y Programación en Tiempo Real
Módulo 6: Temas Avanzados
Módulo 7: Mejores Prácticas y Optimización
- Estilo de Código y Mejores Prácticas
- Depuración y Pruebas
- Optimización del Rendimiento
- Consideraciones de Seguridad