Anterior | Superior | Siguiente

Guía de referencia básica de Ada 95

Subprogramas

Definición, declaración y uso.

Un subprograma es un procedimiento (procedure) o una función (function). La diferencia entre un procedimiento y una función es que el primero sólo indica la ejecución de una secuencia de instrucciones, en función de unos parámetros, mientras que la segunda representa un valor que se genera como resultado de su ejecución. Se pueden usar funciones para sobrecargar los operadores del lenguaje, otorgándoles nuevos significados.

Definición de un subprograma.

La definición de un subprograma consta de tres elementos: (1) cabecera, donde se fija su clase y nombre, se describen sus parámetros formales y, si es una función, se especifica el tipo de su resultado, terminando todo ello con la palabra "is", expresando que a continuación se desarrolla el subprograma, (2) declaraciones locales, de cualquier elemento declarable (tipos, variables, constantes, ...), incluyendo la definición anidada de otros subprogramas y (3) el bloque de sentencias ejecutables del subprograma delimitado por las palabras reservadas "begin" y "end" (esta última suele ir acompañada del nombre del subprograma). Si el subprograma es una función, entre las sentencias ejecutables debe incluirse al menos una sentencia de retorno ("return") con una expresión que indique el valor a devolver a quien la llamó; si la ejecución de una función alcanza el final sin encontrar una sentencia de retorno, se produce una excepción "Program_Error",  dado que la sentencia de retorno se utiliza en las funciones para especificar el valor a devolver. En los procedimientos puede omitirse la sentencia de retorno cuando el único punto de retorno se encuentra al final, como en el siguiente ejemplo.

procedure Intercambia(A,B: in out Integer) is
   C: integer;
begin
   C := A;
   A := B;
   B := C;
end Intercambia;

function
Media(A,B: Float) return Float is
begin
   return (A + B) / 2.0;
end Media;

Las declaraciones locales están sujetas a las reglas de ámbito generales de Ada.

Un subprograma puede constituir por sí mismo una unidad de librería o estar anidado dentro de una unidad mayor. Si un subprograma está anidado en una unidad mayor, la definición del subprograma debe escribirse en una sección de declaraciones de esa unidad.

procedure Principal is
    ...
    procedure Intercambia(A,B: in out Integer) is
        ...
    begin
        ...
    end Intercambia;
    ...
begin
    ...
end Principal;

En un mismo ámbito se pueden tener varios subprogramas con el mismo nombre, siempre que se diferencien en los parámetros o en el tipo del resultado (si son funciones). Esto se conoce como sobrecarga de subprogramas. La sobrecarga de operadores es una clase de sobrecarga de subprogramas.

...
procedure Intercambia(A,B: in out Integer) is
    ...
begin
    ...
end Intercambia;
...
procedure Intercambia(A,B: in out Float) is
    ...
begin
    ...
end Intercambia;
...

Declaración de un subprograma.

En determinadas circunstancias (p.e. cuando un subprograma se proporciona para ser usado desde un paquete, cuando se define un subprograma genérico o cuando hay referencias mutuas entre subprogramas) se precisa escribir una declaración de un subprograma separada de su definición. La declaración de un subprograma es como su cabecera, pero terminada en ";" en vez de con la palabra "is", para expresar que lo que se está dando es una vista de un subprograma cuya definición se haya en otro lugar.

procedure Intercambia(A,B: in out Integer);
function Media(A,B: Float) return Float;

Definición separada (separate).

La definición de un subprograma que está anidado en otra unidad puede extraerse de la misma para compilarse por separado, dejándolo indicado en la unidad matriz mediante una declaración con cláusula "separate". A efectos de reglas de ámbito es como si la definición del subprograma estuviera donde está dicha declaración. El subprograma separado debe indicar quién es su unidad matriz.

procedure Ejemplo_Matriz is
    ...
    -- Indicación de que Intercambia se desarrollará aparte
    procedure Intercambia(A,B: in out Integer) is separate;
    ...
begin
    ...
end Ejemplo_Matriz;

-- Lo siguiente posiblemente irá en un fichero diferente
separate(Ejemplo_Matriz)
procedure Intercambia(A,B: in out Integer) is
    ...
begin
    ...
end Intercambia;
...
 

Llamada a un subprograma.

La llamada a un procedimiento constituye una sentencia, mientras que la llamada a una función sólo puede hacerse como parte de una expresión:

Intercambia(X,Y);
Z := Media(L,M);
if Media(L,M) > 5.0 then ...
return Media(L,M);

Parámetros.

Listas de parámetros formales.

El número, tipo y clase de los parámetros formales se declara en una lista de parámetros formales que se pone justo después del nombre del subprograma en la especificación del mismo. La lista de parámetros formales se delimita por paréntesis y se compone de una o varias sublistas de parámetros de la misma clase y tipo separadas por punto y coma (";"). Cada sublista está formada por una secuencia de nombres separados por comas (","), seguida de dos puntos (":") a continuación de los cuales se pone la clase y tipo de los parámetros de la secuencia:

procedure Muchos_Parámetros(A,B: in Integer; C: out Float; L, M: in out Integer);

Clases de parámetros.

En función del sentido en el que se transfiere la información, los parámetos pueden ser:
  1. De entrada (in).
  2. De salida (out).
  3. De entrada/salida (in out).
Si no se indica de qué clase es un parámetro, se considera que es de entrada. Las funciones en Ada sólo admiten parámetros de entrada. Los parámetros de entrada no se pueden modificar en el subprograma, que los considera constantes.

Correspondencia entre parámetros reales y formales.

Cuando se llama a un subprograma, hay que especificar los parámetros reales sobre los que se quiere que actúe. Los parámetros reales ocuparán en la ejecución el lugar de los formales correspondientes. La correspondencia entre parámetros reales y formales se establece normalmente por posición, lo que quiere decir que el primer parámetro real corresponde al primero formal, el segundo al segundo y así sucesivamente. En el siguiente ejemplo se llama al procedimiento "Intercambia" haciendo corresponder el parámetro real X al formal A, y el real Y al formal B:

...
Intercambia(X,Y); -- A, toma el valor de X y B el de Y
...

La correspondencia entre parámetros formales y reales también puede hacerse por nombre: especificando los nombres del parámetro formal y del real, relacionándolos con una flecha ("=>"):

...
Intercambia(A => X,B => Y); -- A, toma el valor de X y B el de Y
...

A un parámetro formal de entrada se le puede hacer corresponder como parámetro real cualquier expresión del tipo adecuado; a los parámetros formales de salida o de entrada/salida sólo les pueden corresponder parámetros reales que sean variables.

Parámetros por omisión.

Es posible especificar un valor por defecto para los parámetros de entrada asignándolo detrás del nombre del tipo.

procedure Defecto(A,B: in Integer := 0; C: in Float:= 0.0);

Si un parámetro formal tiene un valor por defecto no es necesario pasar ningún parámetro real, pero si después hay parámetros no omitidos, habrá que hacerlos corresponder por nombre.

Defecto(X,Y); -- A, toma el valor de X, B el de Y y C toma el valor 0.0
Defecto(X);   -- A, toma el valor de X, B el valor 0 y C el valor 0.0
Defecto(X,C => Z); -- A, toma el valor de X, B el valor 0 y C el de Z

Subprogramas genéricos.

En muchas situaciones un mismo problema se plantea en contextos diferentes, de forma que el algoritmo que lo resuelve es el mismo, salvo por los detalles propios de cada contexto; por ejemplo, si se quiere ordenar un array, las acciones que habrá que realizar son las mismas independientemente del tipo de los elementos del array (siempre que sean ordenables), de su número, e incluso de la operación que se utilice para compararlos. Un subprograma genérico es uno que se escribe dejando todo este tipo de detalles como parámetros formales genéricos a concretar cuando haga falta, de tal manera que se podrá luego utilizar en distintas situaciones sin tener que reescribir cada vez el algoritmo completo. El siguiente ejemplo ilustra la forma de hacerlo.

generic
    type Tipo is private;
    with function "<"(X,Y: Tipo) return Boolean;
    type Indice is (<>);
    type Lista is array(Indice range <>) of Tipo;
procedure Ordenar(L: in out Lista);

procedure
Ordenar(L: in out Lista) is
    Menor: Indice;
    Aux : Tipo;
begin
    for I in L'First..Indice'Pred(L'Last) loop
       Menor := I;
       for J in Indice'Succ(I)..L'Last loop
          if L(J) < L(Menor) then
             Menor := J;
          end if;
       end loop;
       if Menor /= I then
          Aux := L(I);
          L(I) := L(Menor);
          L(Menor) := Aux;
       end if;
    end loop;
    return;
end Ordenar;

Una vez se tiene escrito el subprograma genérico, se pueden declarar instancias del mismo con diferentes parámetros reales genéricos, lo que hará que el compilador cree los subprogramas correspondientes. Para ilustrarlo, se proporciona un procedimiento llamado "Prueba" en cuya cláusula de contexto se incluye la unidad "Ordenar". El procedimiento "Prueba" declara dos instancias del procedimiento "Ordenar", aplicadas a arrays de elementos de tipo Integer y rango Integer (o Docena), que se diferencian sólo en la operación de ordenación. Cuando el compilador elabore estas declaraciones, se dispondrá de dos procedimientos no genéricos ("Ordena_Vector_Ascendente" y "Ordena_Vector_Descendente" ) que podrán ser utilizados, normalmente, igual que si se hubiesen escrito explícitamente.

with Text_Io,Ordenar;
use Text_Io;
procedure Prueba is
    subtype Docena is Integer range 1..12;
    type Vector is array(Integer range <>) of Integer;
    procedure Ordena_Vector_Ascendente is new Ordenar(Integer,"<",Integer,Vector);
    procedure Ordena_Vector_Descendente is new Ordenar(Integer,">",Docena,Vector);
    V: Vector(Docena);
    ...
begin
     ...
     Ordena_Vector_Ascendente(V);
     ...
end Prueba;

© Grupo de Estructuras de Datos y Lingüística Computacional - ULPGC.

Anterior | Superior | Siguiente