jueves, 9 de enero de 2014

Procedimientos y Funciones

Hasta el momento no habíamos visto lo que recibe el nombre de programación modular. La programación modular consiste en escribir los programas en forma de módulos de código, o pequeños programas cuales componen nuestro programa de forma general. Se utilizan para dividir un problema grande en problemas más pequeños, al igual que la filosofía de “divide y vencerás”.La modularización nos proporciona dos grandes ventajas. Primero, evita la programación redundante, o sea, un módulo de programa puede ser definido una vez, y ser llamado desde diferentes puntos del programa, y cada una de estas llamadas, puede computar los datos con entradas diferentes. Lo segundo, y no por ello menos importante, es la claridad lógica resultante de un programa en módulos concisos e individuales, donde cada módulo representa una parte bien definida del problema total. Tales programas son más fáciles de escribir y depurar que los programas a los que falta este tipo de estructura, lo cual es particularmente cierto cuando los programas son largos y complicados. Muchos programas son modularizados incluso cuando puedan no requerir la ejecución repetida de las mismas tareas, por lo que a esta forma de programar, se le considera una buena práctica de programación.

Existen dos tipos de módulos, Procedimientos y Funciones. Son muy similares, aunque su forma de declararlos e invocarlos no es la misma. Ya conocemos 2 procedimientos estándar, Leer e Imprimir, existen un variedad de procedimientos y funciones estándar (que vienen con el lenguaje de programación), pero por cuestiones prácticas no las vamos a estudiar, pues en cada lenguaje de programación, varia su funcionamiento.

Pasemos a estudiar con más detalle a los Procedimientos.


Un procedimiento es una estructura de programa autónoma dentro de un programa, puede ser referenciado, o invocado, escribiendo su nombre seguido de una lista de parámetros opcionales. Los parámetros deben ir encerrados entre paréntesis, y si hay más de uno, separados por coma.

Cuando invocamos un procedimiento, el control del programa, pasará a la primera instrucción ejecutable de su bloque de código, y cuando este termina de ejecutarse, el control se pasa a la sentencia inmediatamente posterior a la invocación del procedimiento. Pongamos un ejemplo.

Ejemplo 1

Procedimiento Mayor (int a, b, c) {
Int max;

Si (a > b) {max = a;} sino {max = b;}
Si (c > max) {max = c;}
Imprimir (“El mayor de los tres números es ”. max);
}

Este procedimiento del ejemplo 1, calcula cual de 3 números cualesquiera pasados por parámetros es el mayor, e imprime su valor en pantalla. Su sintaxis es la siguiente

Ejemplo 2
Procedimiento nombre_de_procedimiento (parámetros _ opcionales) {
Instrucciones ejecutables…
}

Como vemos, les procedimientos constan de una cabecera “Procedimiento”, un nombre “nombre_de_procedimiento” y entre paréntesis, unos “parámetros _ opcionales”, seguido de un inicio de bloque de código “{” y un fin de bloque de código “}”, además de las instrucciones ejecutables que lo componen.

Después de haber declarado nuestro procedimiento, este puede ser invocado en cualquier punto del programa posterior a su declaración.

Volviendo al ejemplo anterior Procedimiento Mayor(int a,b,c), tenemos que los parámetros son 3 variables int. Estas variables deberán tener valores antes de hacer una invocación al procedimiento, pues cuando se invoque dicho procedimiento, no podremos trabajar con sus valores si no están definidos. Estas 3 variables, serán tratadas dentro del bloque de código del procedimiento, para así calcular cual de las 3 es la mayor e imprimirla. Cuando invoquemos el procedimiento, le daremos valores a estas 3 variables, que bien pueden ser propias variables o también pueden ser cualquier valor int. Por ejemplo, seria válido invocar al procedimiento tanto en el ejemplo 3 como en el 4:

Ejemplo 3

Int a = 5;
Int b = 6;
Int c = 7;
Mayor(a,b,c);

En el ejemplo 3 declaramos e inicializamos 3 variables int, y se las damos o pasamos como parámetros al procedimiento Mayor;

Ejemplo 4

Mayor(5,6,7);

En el ejemplo 4 estamos pasándole por parámetros directamente valores int.

Generalmente usamos el ejemplo 3 en programas reales, aunque el ejemplo 4 no es incorrecto.

También deberíamos saber que las variables que les pasamos por parámetros a los procedimientos, no necesariamente tienen que tener el nombre que especificamos en la declaración del procedimiento, veamos un ejemplo:

Ejemplo 5

Int q = 5;
Int w = 6;
Int e = 7;
Mayor(q,w,e);

En el ejemplo 5 vemos que se declaran e inicializan 3 variables cualesquiera del tipo int y se les pasan por parámetros al procedimiento Mayor. En tiempo de ejecución, el compilador “cogerá” los valores de “q”, “w”, y “e” y se los dará respectivamente a “a”, “b”, y “c” para trabajar con dichos valores dentro del bloque de código de dicho procedimiento.

Ahora pongamos el ejemplo del programa que responde al enunciado:
Realice un programa que dados 3 números, imprima cual de ellos es el mayor.


Programa # 15

Int a, b, c;

Procedimiento Mayor (int a, b, c) {
Int max;
Si (a > b) {max = a;} sino {max = b;}
Si (c > max) {max = c;}
Imprimir (“El mayor de los tres números es ”. max);
}

Imprimir (“Entre el primer número ”);
Leer a;

Imprimir (“Entre el segundo número ”);
Leer b;

Imprimir (“Entre el tercer número ”);
Leer c;

Mayor(a,b,c);

Observe que primero declaramos las variables “a”,”b” y ”c”. Después declaramos el procedimiento con nombre Mayor, mas tarde leemos del teclado los valores de a”,”b” y ”c” y después llamamos al procedimiento Mayor, para que calcule quien de los 3 valores es el mayor. Note que en la declaración del procedimiento, este no se ejecuta. El procedimiento se ejecuta cuando le hacemos la llamada Mayor(a,b,c); .

En este problema, no es necesario utilizar procedimientos, pues su sencillez no lo requiere.

Las funciones, como expresamos anteriormente, son similares a los procedimientos. Sabemos que se declaran de forma diferente a los procedimientos, y además que devuelven un valor del tipo que se les declare. Pongamos un ejemplo de una función que calcule el cuadrado de un número cualquiera.

Ejemplo 1

Int Función Cuad(Int x)  {
      Int r;
      r = x*x;
      return r;


Como explicamos, las funciones tienen tipo, y se pueden usar igual que las variables, gracias a esta característica tan peculiar. Vemos que el tipo de esta función es Int, o sea, “devuelve” o “retorna” un valor Int. Su cabecera Función, especifica que estamos en presencia de una función, y su nombre Cuad, lo usaremos para invocarla dentro del programa que se declare. También observamos su único parámetro “x” del tipo Int, que representa el número al que le vamos a calcular su cuadrado. Dentro de la función, declaramos una variable “r” del tipo int, que representa el resultado del calculo del cuadrado de “x” mediante “r = x*x”; Por ultimo, vemos una instrucción nueva, return. Esta nueva instrucción esta encargada de “sacar” o devolver de la función el valor del cuadrado de “x”, que es “r”. Si no se especifica return , la función no devolverá ningún valor. Veamos un ejemplo del programa que responde al enunciado:

Hacer un programa que reciba un número entero por teclado e imprima su cuadrado.

Programa # 16 (versión 1)

Int numero, cuadrado;

Int Función Cuad(Int x)  {
      Int r;
      r = x*x;
      return r;


Imprimir (“Entre un numero por teclado ”);
Leer numero;

cuadrado = Cuad(numero);
Imprimir (“El cuadrado de ” . numero . “ es ” . cuadrado);

En este ejemplo usamos las variables “cuadrado” y “r” intencionalmente, con el objetivo de que usted pueda entender mejor el problema. En este ejemplo que viene a continuación, usamos solo las variables necesarias para resolver este problema

Programa # 16 (versión 1)

Int numero;
Int Función Cuad(Int x)  {
      return x*x;


Imprimir (“Entre un numero por teclado ”);
Leer numero;
Imprimir (“El cuadrado de ” . numero . “ es ” . Cuad(numero));

En este segundo ejemplo, vemos que se declara una variable “numero” para guardar el número entrado por teclado. Dentro de la función, vemos algo interesante, no es necesario guardar el valor devuelto por la expresión “x*x” en ninguna variable, pues el resultado de esta multiplicación se le puede pasar directamente a return para que lo “saque” o devuelva de la función, pues return puede recibir un valor fijo, una variable, o una expresión, cualesquiera de los tres tiene que ser del tipo de la función, en este caso int. Después le pedimos al usuario que entre un número cualquiera, y lo guardamos en la variable “numero”. A continuación sucede algo interesante, para darle el mensaje final al usuario, estamos concatenando un texto con el valor que devuelve una función. Esto es permitido, y a menudo se considera una buena práctica de programación, pues las funciones se direccionan igual que las variables, por su nombre, y esto quiere decir que al llamar a una variable por su nombre, esta devuelve su valor en memoria, y al llamar a una función, esta ejecuta lo que esté en su bloque de código, y devuelve el valor especificado por return. Por tanto, en este momento, no nos interesa lo que haga la función Cuad, lo que nos interesa es que al llamarla por su nombre y pasándole el parámetro adecuado, esta devolverá un valor int equivalente al cuadrado del numero que se le pase por parámetros.

Veamos otro ejemplo.
Realice un programa que calcule el factorial de un número dado por teclado.


Programa # 17

Int num;
Int Función Factorial(int num)
{
  Int f = 1;
  Desde (int i = num;i>1;i--) {
  f= f* i;
  }
return f;
}

Imprimir (“Entre el numero para calcular factorial:”);
Leer num;

Imprimir (num .“!  = ” . Factorial(num));

El programa produciría la siguiente salida.

Entre el número para calcular el factorial :
5
5! = 120


Ahora veremos un ejemplo donde en un mismo programa usamos funciones y procedimientos.

Realice un programa que imprima todos los factoriales en el formato n! = factorialn desde 1 hasta un valor dado.





Programa # 18

Int máximo;

Int Función Factorial(Int num)
{

  Int f = 1;
  Desde (int i = num;i>1;i--) {
  f= f* i;
  }
return f;
}

Procedimiento Calcula_Todos_los_Factoriales_Hasta(int a)
{

  Desde (int i = 1;i<= a; i++) {
  Imprimir (i.” != ” . Factorial(i));
  }

}

Imprimir (“Hasta que numero desea listar los factoriales”);
Leer máximo;

Calcula_Todos_los_Factoriales_Hasta(máximo);

En este ejemplo vemos el uso de una función y un procedimiento. Ya conocemos la función Int Función Factorial(Int num), y sabemos que al pasarle por parámetros a “num”, nos devuelve un int que representa el factorial de “num”. Pero el problema requiere calcular todos los factoriales desde 1 hasta un valor dado por el teclado, para esto usamos el procedimiento Calcula_Todos_los_Factoriales_Hasta(máximo), donde “máximo” es la variable que contiene hasta donde calcularemos el factorial. Dentro de este procedimiento tenemos un ciclo, quien hace una llamada a la función Factorial(Int num) en cada una de sus iteraciones, y utilizando la variable contadora “i”, como un parámetro para la función Factorial(Int num). En cada iteración del ciclo, se imprimen los valores que va devolviendo la función Factorial(Int num).