jueves, 29 de octubre de 2009

Aprende a programar: 5 - Programación Modular

A medida que el tamaño de los programas se hace más grande, incrementa la dificultad de comprenderlos y también se incrementa el número de tareas repetidas que deben ser escritas.
Mediante las funciones podemos escribir el código que se repite varias veces simplemente una vez (crear una función), y después simplemente "decir" queremos usarlo (llamar a una función). Así, a parte de aumentar la legibilidad del programa y reducir el tamaño, nos da la oportunidad de dividir el programa en partes o módulos, de forma que podemos dividir un programa complejo en varias partes más sencillas (¡Divide y vencerás!).

Vamos a ver un ejemplo para comprender mejor lo que es la modularidad. Vamos a construír un algoritmo, no de un programa ni en lenguaje C++, sino de una situación real en un lenguaje inventado por nosotros:
Algoritmo: RevisarCoche
{
    poner Gasolina;
    comprobar Aceite;
    verificar Luces Repuesto;
}

Mediante esta abstracción hemos creado un algoritmo sencillo, formado por otras subtareas.
Ahora definiremos las otras subtareas
Algoritmo: PonerGasolina
{
    marcar Cantidad en Surtidor;
    echar Gasolina;
    pagar;
}

Algoritmo: ComprobarAceite
{
    sacar la varilla;
    mirar la varilla;
    mientras (nivel sea menor que el máximo) {
        poner la varilla;
        añadir aceite;
        sacar la varilla;
        mirar la varilla;
    }
}

Algoritmo: VerificarLucesRepuesto
{
    si hay caja de luces de repuesto
        comprobar juego de luces
        sino
        comprobar juego completo
}

Si tuviéramos que explicarle a alguien como realizar la tarea de revisar el coche, le diríamos: primero pon gasolina, luego comprueba el aceite y luego las luces. Y, luego, podríamos concretarle cualquiera de estos tres puntos. Evidentemente no le diríamos primero marca la cantidad en el surtidor, luego echa gasolina, luego paga, luego saca la varilla, luego mira la varilla, etc...

De esta manera, al hacer este otro algoritmo:
Algoritmo:ComprobarGasolina
{
    si nivelGasolina<=NivelReserva
        PonerGasolina;
}

sólo tenemos que llamar la función PonerGasolina y no volver a describir todo el proceso de marcar cantidad en surtiror, echar gasolina, etc...

Por si aún no lo has entendido te lo describiré de otra forma:
Supongamos que tienes que ir a comprar el pan, luego ir a casa de tu amigo Pepe a pedirle unos apuntes y volver a casa. En tu cabeza estás pensando que tienes que realizar estas tres tareas, y sabes que cada tarea conlleva otras subtareas. Por ejemplo, comprar el pan conlleva ir a la panadería, pedir el pan, calcular las monedas que le das al dependiente, dar monedas, recoger cambio, etc... a su vez, la tarea de ir a la panadería conlleva calcular una ruta dependiendo del sitio donde estés, mirar para cruzar, cruzar en caso de que no vengan coches y un largo etc...
Pues bien, en tu cabeza tienes el subalgoritmo de cruzar la calle, el algoritmo de calcular la ruta más corta, el de dar monedas, etc... de forma que si hicieras una lista de las cosas que tengas que hacer harías:

* Comprar el Pan
* Ir a casa de Pepe
* Volver a casa

Y efectivamente no escribirías:

* Coger llaves
* Abrir puerta de casa
* Cerrar puerta de casa
* Mirar la calle
* Cruzar cuando no venga ningún coche
* etc...

Pues lo mismo pasa en la programación.
¿Entiendes el sentido de la programación modular? Espero que si...

FUNCIONES

Formato de funciones en C++:
TipoDeDatos NombreFunción(parámetro1, parámetro2, ..., parámetroN)
{
    sentencias; //(cuerpo de la funcion)
    return expresion; //Valor devuelto
}

Una función es una operación que recibe uno o más valores llamados argumentos o parámetros, de forma que produce un resultado (valor de retorno).
En C++ los subprogramas se llaman funciones.

TipoDeDatos Es el tipo de datos que devuelve la función, en caso de que la función no devuelva nada, será de tipo void.
NombreFuncion Representa al nombre con el que se va a identificar a la función. Es aconsejale utilizar un verbo en infinitivo que describa la acción del mismo y que empiece por mayúscula para diferenciarlas de las variables.
Parámetros Los parámetros son variables que el programa principal "pasa" a la función para que ésta haga las operaciones necesarias. Una función puede no tener parámetros.

La forma estándar de llamar a una función en caso de que devuelva algún parámetro es:
menor=Menor(a,b); o fecha=DiaDeHoy();
O en caso de que no devuelva valor alguno:
Sumar(a,b); o VerInstrucciones();

Por ejemplo, podríamos tener un programa que borre la pantalla varias veces, podríamos crear una función para hacer el código más sencillo, de forma que el programa quedaría así:
#include <iostream>
using namespace std;

void BorrarPantalla()
{
    for (int i=0;i<24;i++)
    cout << endl;
}

int main()
{
    cout << "Vamos a borrar la pantalla" << endl;
    system("pause");
    BorrarPantalla();
    cout << "Vamos a volver a borrar la pantalla" << endl;
    system("pause");
    BorrarPantalla();
    system("pause");
}

Lo siguiente es un ejemplo de una función con parámetros de entrada (parámetros que el programa principal pasa a la función) y de salida (valor que devuelve la función);
#include < iostream >
using namespace std;

bool EsBisiesto(int anyo)
{
    if(anyo%4==0 && anyo%100!=0 || anyo%400==0)
        return true;
    else
        return false;
}


int main()
{
    int unAnyo;
    bool bisiesto;

    cout << "Introduce un anyo" << endl;
    cin >> unAnyo;

    bisiesto=EsBisiesto(unAnyo);

    if(bisiesto==true)
        cout << "Es bisiesto" << endl;
    else
        cout << "No es bisiesto" << endl;

    system("pause");
}

También sería válida la siguiente forma de usar la función:
if(EsBisiesto(unAnyo))
    cout << "Es bisiesto" << endl;
else
    cout << "No es bisiesto" << endl;

Parámetros por valor y por referencia

Cuando llamas a la función void Funcion(paramFunc,paramFunc2) mediante la forma establecida desde el programa principal (o desde otra función) Funcion(paramMain,paramMain2) lo pasa con los parámetros es que paramFunc y paramFunc2 son una copia de paramMain y paramMain2 respectivamente. De forma que si en la función modificamos los valores de paramFunc y paramFunc2 no estaremos modificando los valores de paramMain y paramMain2. A veces es interesante que si que se modifiquen los parámetros paramMain y paramMain2, así la función puede devolver varios parámetros.
Cuando los parámetros originales no se modifican, se dice que se pasan los parámetros por valor, mientras que si los originales SI que modifican se dice que los parámetros se pasan por referencia.

La definición y sintaxis de uso de parámetros por referencia es igual que la de parámetros por valor exceptuando que en la definición de la cabecera de la función y en sus propios prototipos se deberá anteponer al nombre del parámetro el carácter '&'.

Veamos algunos ejemplos:
#include <iostream>
using namespace std;

void PasarPorValor(int a, int b)
{
    a=a+b;
    b=a;
}
int main()
{
    int numero, numero2;
    numero=10;
    numero2=20;
    PasarPorValor(numero,numero2);
    cout << "numero=" << numero << ", numero2=" << numero2 << endl;
    //numero=10, numero2=20   
    system("pause");
}

Vemos que, aunque en la función modifiquemos los valores de a y b, no cambian los valores de numero1 y numero2. A continuación veremos un caso en el que sí, ya que pasaremos los valores por referencia:
#include <iostream>
using namespace std;

void PasarPorReferencia(int &a, int &b)
{
    a=a+b;
    b=a;
}
int main()
{
    int numero, numero2;
    numero=10;
    numero2=20;
    PasarPorValor(numero,numero2);
    cout << "numero=" << numero << ", numero2=" << numero2 << endl;
    //numero=30, numero2=30   
    system("pause");
}

NOTA IMPORTANTE: La ejecución del código de una función termina cuando se llega a la última línea de esta, o cuando se encuentra el primer return, ya que una función sólo puede devolver un valor, la única forma de "devolver" varios valores es pasando los parámetros por referencia.

No hay comentarios:

Publicar un comentario