Subprogramas
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
...
Definición, funciones.
Un subprograma es un procedimiento (procedure) o una función (function), la funcion
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.
Como vimos anteriormente C tiene como bloque básico la función main() , también
hemos visto la sentencia printf() que es otra función, y de igual forma hay muchas más
funciones predefinidas, pero nosotros mismos también podemos definir nuestras propias
funciones. De hecho, es fundamental hacerlo.
Podemos definir una función cualquiera de la misma manera en que definimos la
función main() . Basta con poner su tipo, su nombre, sus argumentos entre paréntesis y
luego, entre llaves.
Un parámetro es un valor que la función espera recibir cuando sea llamada (invocada), a
fin de ejecutar acciones en base al mismo. Una función puede esperar uno o más
parámetros (que irán separados por una coma) o ninguno.
Parámetros por valor y por referencia
Los parámetros de una función se pueden definir de dos maneras: Por valor o por
referencia.
Paso por valor
Este método copia el valor de los argumentos sobre los parámetros formales, de manera
que los cambios de valor de los parámetros no afectan a las variables utilizadas como
argumentos en la llamada.
Lo importante en el paso por valor es el valor del argumento, por eso es indiferente si este
argumento es una variable, una constante o una expresión.
Paso por referencia en C++.
A diferencia del paso por valor, en el paso por referencia los parámetros no copian el valor
del argumento, sino que comparten su valor. Por lo que cuando cambia el valor del
parámetro también cambia el valor de la variable utilizada como argumento en la llamada.
La forma de indicar un parámetro por referencia es anexar el símbolo & al final del
nombre de la variable de tipo en la lista de parámetros formales, tanto en el prototipo de
la función como en el encabezado de su definición.
Ejemplo:
void obtener_entrada (double &temperatura)
{
cout << “Dame una temperatura en grados centígrados \n”;
cin >> temperatura;
}
La llamada a la función desde el programa principal:
Obtener_entrada (temperatura);
Los parámetros definidos por referencia se pueden utilizar tanto de entrada como de
salida de datos. Este es el otro mecanismo que poseen las funciones, a parte del return,
para devolver valores.
Una consecuencia evidente es que, en la llamada de la función, los argumentos por
referencia han de ser siempre variables.
Funciones que llaman a funciones
Un cuerpo de función puede contener una llamada a otra función. La situación en este
tipo de llamadas es exactamente la misma que si la llamada de función se hubiera
efectuado en la función main del programa; la única restricción es que el prototipo debe
aparecer antes de que se emplee la función.
Recursividad
Una función recursiva es aquella que se llama a sí misma. Esto se traduce en que dentro
del código de un subprograma recursivo hay una sentencia o expresión donde aparece la
llamada a la misma función.
Para no crear secuencias de infinitas llamadas (ya que la función llamada, volverá a
llamarse por ser a su vez recursiva) necesitamos dos elementos:
1. Las funciones recursivas deben tener argumentos, cuyos valores diferencien a una
llamada de otra.
2. Debe existir una condición final o condición de parada, que utiliza la información
de los argumentos para verificar si la recursión debe finalizar, en cuyo caso no se
producen más llamadas.
Evidentemente la secuencia de valores de los argumentos debe ser tal que se garantice
que en algún momento se llegue a la condición final de la recursión. De no ser así, se
producirá un error de Desbordamiento de Pila (Stack Overflow) que indica que las
llamadas recursivas han saturado la memoria de la pila del programa.
La recursividad está directamente relacionada con una técnica de programación llamada
Divide y Vencerás, de tal manera que esta técnica nos puede ayudar a descubrir la forma
que debe tener nuestra función recursiva. Esta técnica se basa en lo siguiente: frente a un
problema, se realizan una serie de tareas básicas que nos llevan a tener el mismo
problema pero de complejidad menor. Si esto lo aplicamos recursivamente, la resolución
del problema se traduce en ejecutar una serie de tareas simples que nos desembocan al
final en el mismo problema pero de una complejidad tan simple que es trivial resolverlo.
Esta resolución se hace en la condición final de la recursión y se denomina Caso Base.