viernes, 23 de octubre de 2015

MÓDULOS (FUNCIONES)

Un principio ampliamente difundido e importante para resolver problemas es el de “dividir para reinar”, es decir separar el problema en partes, en problemas más pequeños, para así enfrentarlos y resolverlos en un tamaño más accesible y fácil de manejar. Este principio tiene una directa aplicación en la programación de computadoras, para lo cual muchos lenguajes de programación cuentan con una característica adecuada: la modularidad. Estos lenguajes permiten escribir programas compuestos de porciones o partes que se conocen como módulos, muchas veces nombrados también como subprogramas, subrutinas, procedimientos, funciones, entre otros. En todos los casos, los principios generales y elementales que los gobiernan son los mismos, aún cuando algunos tengan características distintas y mayores o menores prestaciones: todos permiten separar un programa en partes más pequeñas y reusar esos trozos de programa o módulos ya escritos para resolver problemas similares.
Un módulo de programa tiene dos partes bien diferenciadas: la definición del módulo y la invocación o llamada al módulo. La definición de un módulo es donde se dice qué es lo que el mismo hará, es decir, cuáles serán sus efectos al ser invocado, es decir cuando se ejecute. Una invocación o llamada ocurre cuando se usa el módulo. En el punto del programa donde se hace la invocación al módulo es donde, en tiempo de ejecución del programa, se ejecutarán las acciones comprendidas en la definición del módulo.
 En un programa, por cada módulo, habrá una única definición, ya que bastará con una vez para decirse qué es lo que el módulo hace. Sin embargo, puede haber más de una invocación al mismo, ya que una solución puede usarse tantas veces como sea necesario.
2.1- TIPOS DE FUNCIONES: INTERNAS Y CREADAS POR EL USUARIO
Funciones internas
El intérprete de Python cuenta con funciones internas que siempre se hallan disponibles. Se muestran a continuación, en orden alfabético.
__import__ (name[, globals[, locals[, fromlist]]])
Esta función es llamada por la sentencia import. Existe con el propósito principal de reemplazarla con otra función compatible en interfaz, para hacer posible cambiar la semántica de la sentencia import. Consultar los módulos de biblioteca estándar ihooks y rexec si se desea ver por qué y cómo hacer esto. Consultar también el módulo interno imp, que define ciertas operaciones útiles para construir una función __import__() propia.
Por ejemplo, la sentencia `import spam' causa la siguiente llamada: __import__('spam', globals(), locals(), []); la sentencia from spam.jamon import huevos resulta en __import__('spam.jamon', globals(), locals(), ['huevos']). Hay que ver que, a pesar de que se pasan locals() y['huevos'] como argumentos, la función __import__() no da valor a la variable local huevos; esto se realiza con el código adicional generado por la sentencia import (de hecho, la implementación estándar no utiliza el argumento locals en absoluto y usa globals sólo para determinar el contexto del paquete de la sentencia import).
Cuando la variable name tiene la forma package.module, normalmente, se devuelve el paquete de nivel más elevado (el nombre hasta el primer punto, no el módulo denotado por name. Sin embargo,. cuando se proporciona un argumento fromlist no vacío, se devuelve el módulo denominado name. Esto se hace de esta manera por compatibilidad con el bytecode generado para las diferentes clases de sentencias import. Cuando se utiliza "import spam.jamon.huevos", el paquete de nivel superior spam debe colocarse en el espacio nominal que ha llamado a import, pero cuando se usa "from spam.jamon import huevos", se ha de utilizar el subpaquete spam.jamon para encontrar la variable huevos. Como remedio a este comportamiento, se puede utilizar getattr() para extraer los componentes deseados. Por ejemplo, se podría definir la siguiente función de ayuda:
import string
def my_import(name):
    mod = __import__(name)
    components = string.split(name, '.')
    for comp in components[1:]:
        mod = getattr(mod, comp)
    return mod
abs (x)
Devuelve el valor absoluto de un número. El argumento puede ser un entero normal o largo o un número de coma flotante. Si el argumento es un número complejo, se devuelve su módulo.
apply (function, args[, keywords])
El argumento function debe ser un objeto invocable (una función o método definido por el usuario o interna) y args una secuencia (si no es una tupla, primero se convierte la secuencia a tupla). Se llama a la función function con args como lista de argumentos. El número de argumentos es la longitud de la tupla (se diferencia de llamar sin más a func(args), pues en este caso siempre hay exactamente un argumento). Si se proporciona el argumento opcional keywords, debe ser un diccionario cuyas claves sean cadenas. Especifica argumentos clave a añadir al final de la lista de argumentosd.
buffer (object[, offset[, size]])
EL argumento object debe ser un objeto con interfaz compatible con buffer (cadenas, matrices y buffers). Se creará un nuevo objeto buffer referente a object. El objeto buffer será un corte desde el principio de object (o desde el desplazamiento inicial offset especificado). El corte se extenderá hasta el final de object (o tendrá una longitud especificada por el argumento size).
 Funciones definidas por el usuario
Las funciones de usuario pueden ser escritas para realizar tareas repetitivas y para reducir el tamaño de un programa.
Las funciones definidas por el usuario son aquellas que crea usted mismo para usarlas en aplicaciones, a diferencia de las funciones en las clases incorporadas, que realizan funciones predefinidas. Deberá asignar nombre a las funciones usted mismo y añadir sentencias en el bloque de función. Las secciones anteriores tratan la escritura de funciones como, por ejemplo, las funciones con nombre, anónimas y callback.
Puede utilizar una ruta de destino para llamar a una función en cualquier línea de tiempo y desde cualquier línea de tiempo, inclusive desde la línea de tiempo de un archivo SWF cargado. Para llamar a una función, escriba la ruta de destino del nombre de la función, si es necesario, y pase los parámetros necesarios entre paréntesis. Existen diversas formas de sintaxis para las funciones definidas por el usuario. En el código siguiente se utiliza una ruta para llamar a la función initialize(), que se ha definido en la línea de tiempo actual y que no requiere ningún parámetro:
this.initialize();

En el ejemplo siguiente se utiliza una ruta relativa para llamar a la función list() que se ha definido en el clip de película functionsClip:
this._parent.functionsClip.list(6);

También puede definir sus propias funciones con nombre. Por ejemplo, la siguiente función con nombre helloWorld() ha sido definida por el usuario:
function helloWorld() {
    trace("Hello world!");
};
Las funciones se declaran asociadas a un tipo de valor. Este valor será el que devolverá la función, por ejemplo ‘int’ se utilizará cuando la función devuelva un dato numérico de tipo entero. Si la función no devuelve ningún valor entonces se colocará delante la palabra “void”, que significa “función vacía”



2.2  COMPONENTES
Los componentes son unos elementos genéricos con una funcionalidad muy concreta, cuya única finalidad es la reutilización. Cada uno de ellos está destinado a realizar una tarea típica en una aplicación.
Un componente de la VCL es una clase que caracteriza a un control de Windows agregando propiedades, métodos y gestores de eventos a cada control.
La filosofía de los componentes en C++ Builder es exactamente la misma que en Visual Basic. Esto es tanto así que algunos componentes pueden utilizarse en ambos entornos (los componentes ActiveX).
2.2.1   Declaracion
Una declaración es una sentencia que introduce un nombre en una unidad de compilación dándole existencia semántica. Esto de la "existencia semántica" es una forma elegante de decir que a partir de ahí el compilador sabe que "cosa" es (representa) ese nombre. La forma de darle existencia semántica a las entidades es declararlos (algo así como "presentarlos" formalmente en el código). Por ejemplo, si declaramos una variable x o una funciónfunc, a partir de ahí el compilador sabe que x es una variable de tal tipo, y que func es una función de características cuales.
Simplemente asocia un identificador con un tipo (existencia semántica). La declaración de una función se denomina prototipo. La gramática C++ exige que la declaración de una entidad se indique primero su tipo y después el identificador con el que se la conocerá en adelante.
Ejemplos:
extern int x;
class C;
int func(int x, char c);   // prototipo
Observe que la gramática C++ permite realizar varias declaraciones en una sola sentencia separando con comas los identificadores:
int x, y, z;
C c1, c2, c3;
Después de la declaración es poco lo que puede hacer el compilador con una etiqueta, ya que solo conoce el "tipo" de objeto que representa. Sin embargo, son posibles aquellos usos para los que basta con esta información. Ejemplo:
struct E1;        // declara que E1 es tipo struct
struct E2* pe2;   // declara que pe2 es tipo E2* (puntero-a-struct-E2)
E1* ep1;          // declara que pe1 es tipo E1* (puntero-a-struct-E1)
Este tipo de declaraciones se denominan adelantadas (en el sentido que no están
los tipos incompletos no pueden ser utilizados en la definición de otros tipos, aunque sí en su declaración -también incompleta-. Ejemplo:
extern C* cptr;      // puntero a tipo incompleto
typedef int UNDA[];  // matriz incompleta
UNDA* aptr;          // puntero a tipo incompleto
class D {
   UNDA** apptr;     // Error: puntero a tipo incompleto
   ...               // en definición
};
     2.2.2   ARGUMENTOS
En programación, los argumentos son los valores realmente suministrados a un procedimiento cuando éste es invocado o llamado. En ocasiones se considera sinónimo de parámetro, pero técnicamente no significan lo mismo.
Los argumentos pueden (y a menudo es así) variar de llamado en llamado (a diferencia de los parámetros, que forman parte de la definición estática de un procedimiento).
Un argumento o parámetro es una variable utilizada para recibir valores de entrada en una rutina o subrutina. Dichos valores, que serán enviados desde la rutina invocante, son llamados argumentos. La subrutina usa los valores asignados a sus parámetros para alterar su comportamiento en tiempo de ejecución. La mayor parte de los lenguajes de programación pueden definir subrutinas que aceptan cero o más argumentos.
Existen cinco formas de pasar un argumento a una función (subrutina) o procedimiento: por valor, por referencia, por resultado, por valor-resultado y por nombre.
Diferencia entre parámetro y argumento en programación
En general las palabras argumento y parámetro son tomadas como sinónimos; en realidad hay diferencia: los parámetros aparecen en la definición del procedimiento, los argumentos aparecen en los llamados a procedimientos.

Un parámetro es una propiedad intrínseca de un procedimiento, dado que está incluido en su definición. En tanto, los argumentos son más bien los valores actuales asignados a los parámetros variables cuando la subrutina es llamada. En la práctica no se suele distinguir tajantemente entre ambos términos.
2.2.3  PARAMETROS
En la informática, más precisamente en programación, un parámetro representa un dato que se ofrece una función con un fin específico. Por ejemplo, una rutina que tenga como finalidad devolver el número más alto de una lista, esperará que le pasemos dicho conjunto de valores como parámetro para realizar su tarea.
En algunos lenguajes, es posible especificar que ciertos parámetros sean opcionales, y asignarles un valor por defecto en caso de que el programador escoja no pasarlos. En programación de gráficos, por ejemplo, suele ser común que las funciones o métodos que gestionan temas relacionados con el color, tengan un parámetro para la transparencia que no sea obligatorio especificar, ya que comúnmente las imágenes de una aplicación multimedial son opacas.
Esto repercute principalmente en la practicidad a la hora de programar, ya que existen funciones que esperan 10 parámetros, 6 de los cuales son optativos, y verse forzado a completar esos espacios con valores nulos o falsos cada vez que se desee prescindir de ellos representa una pérdida de tiempo.
 2.3  INVOCACIÓN Y RETORNO DEL VALOR DE LA FUNCIÓN
FUNCIONES QUE RETORNAN VALOR
Analicemos por medio de un ejemplo dichas funciones :
#include <stdio.h>
#include <conio.h>
#define FALSO 0
#define CIERTO 1
int finalizar(void);
int lea_char(void) ;
main()
  {
  int i = 0;
  int fin = FALSO;
  printf("Ejemplo de Funciones que retornan valor\n");
  while (fin == FALSO) {
    i++;
    printf("i == %d\n", i);
    fin = finalizar();
    }
  printf("\n\nFIN DEL PROGRAMA........");
  return 0;
  }
int finalizar(void)
  {
  int c;
  printf("Otro número ? (s/n) ");
  do {
    c = lea_char() ;
    } while ((c != 'n') && (c != 's'));
  return (c == 'n');
  }
int lea_char(void)
  {
  int j ;
  if( (j = getch()) >>= 'A' && j <<= 'Z' )
    return( j + ( 'a' - 'A') ) ;
  else
    return j ;
  }
Analizando el programa anterior; las dos primeras líneas incluirán, en el programa los prototipos de las funciones de librería usadas, ( en este caso printf() y getch() . En las dos siguientes damos nombres simbólicos a dos constantes que usaremos en las condiciones lógicas y posteriormente damos los prototipos de dos funciones que hemos creado.
Podrían haberse obviado, en este caso particular, estas dos últimas declaraciones, ya que ambas retornan un int (default), sin embargo el hecho de incluirlas hará que el programa sea más comprensible en el futuro.
Comienza luego la función main(), inicializando dos variables, i y fin, donde la primera nos servirá de contador y la segunda de indicador lógico. Luego de imprimir el rótulo del programa, entramos en un loop en el que permaneceremos todo el tiempo en que fin sea FALSO.
Dentro de este loop, incrementamos el contador, lo imprimimos, y asignamos a fin un valor que es el retorno de la función finalizar() 
2.4   ÁMBITO O ALCANCE DE LAS VARIABLES
El ámbito es el contexto que tiene un nombre dentro de un programa. El ámbito determina en qué partes del programa una entidad puede ser usada.
Esto sirve para que se pueda volver a definir una variable con un mismo nombre en diferentes partes del programa sin que haya conflictos entre ellos.
Si una variable es declarada dentro de un bloque (método/función/procedimiento), ésta será válida solo dentro de ese bloque y se destruirá al terminar el bloque. Adicionalmente, la variable no podrá verse ni usarse fuera del bloque (en el exterior del bloque). La variable dentro del bloque es una variable local y solo tiene alcance dentro del bloque que se creó y sus bloques hijos, pero no en bloques hermanos ni padres, una variable definida fuera de cualquier bloque es una variable global y cualquier bloque puede acceder a ella y modificarla.
En el caso de programación orientada a objetos (POO), una variable global dentro de una clase es llamada variable de instancia, y cada objeto creado con esa clase tiene una. Adicionalmente existen variables globales que son comunes a un todos los objetos creados con una clase y son llamadas variables de clase.
Hay dos tipos de alcances, el estático que también es llamado lexicográfico, donde el alcance se determina en tiempo de compilación, mientras que las variables de alcance dinámico se verificara en el hilo de ejecución.
Ejemplo en Java
public class A {
    public Integer numeroEntero = new Integer(); /* Variable Global a todos los Métodos */
    public Integer metodo() {
        int num = 1; // Variable Local a método. Puede accederse dentro de este método en cualquier parte, pero no fuera del mismo.
        for (int i = 0;i<numeroEntero.intValue();i++) { // i es local al bucle for, sólo puede ser accedida dentro del mismo.
            num *= i;
        }
        // i = 2; Esta línea provocaría error al no haber declarado la variable i. i fue definida localmente al bucle for.
        return Integer.valueOf(num);
    }
       public void otroMetodo() {
        int num = 1; // Variable local a otroMetodo. num aquí es una variable distinta a la variable num de método
        System.out.println("Variable local num: " + num);
    }
}
 2.5  PASO DE ARGUMENTOS POR VALOR
Es un tipo poco usado en los lenguajes de programación actuales. Se basa en que dentro de la función se trabaja como si los argumentos hubieran sido pasados por valor pero al acabar la función los valores que tengan los argumentos serán copiados a las variables que pertenecían.
Este tipo puede ser simulado en cualquier lenguaje que permita el paso de valores por referencia de la siguiente forma:
void EjemploValorRes(int a1, int a2, int a3) {
    int aux1 = a1, aux2 = a2, aux3 = a3;
    // código trabajando con aux1, aux2 y aux3
    a1 = aux1; a2 = aux2; a3 = aux3; // Dependiendo del compilador la copia se realiza en un sentido o en el otro
}
Tal y como indica el ejemplo de simulación de valor-resultado, el orden de copia depende del compilador, lo que implica que la misma función pueda dar resultados diferentes según el compilador usado.


 2.6  PASO DE ARGUMENTOS POR REFERENCIA
El paso de parámetros por referencia consiste en proporcionar a la subrutina a la que se le quiere pasar el argumento la dirección de memoria del dato. En este caso se tiene un único valor referenciado (o apuntado) desde dos puntos diferentes, el programa principal y la subrutina a la que se le pasa el argumento, por lo que cualquier acción sobre el parámetro se realiza sobre la misma posición de memoria.
El paso por referencia se hace utilizando apuntadores. Se envía la dirección de memoria de la variable, por lo tanto los cambios que haga la función si afectan el valor de la variable. Ejemplo:
/*
 * por_referencia.c
 *
 * Julio César Brizuela <brizuelaalvarado@gmail.com> 2009
 *
 * para el wikilibro "Programación en C"
 * bajo licencia FDL, adaptado del Dominio Público
 */
 #include <stdio.h>
 void sumar_referencia(int *numero); /* prototipo de la función */
 int main(void)
{
               int numero = 57; /* definimos numero con valor de 57*/

               sumar_referencia(&numero); /* enviamos numero a la función */

               printf("\nValor de numero dentro de main() es: %d ", numero);
               /* podemos notar que el valor de numero se modifica
                * y que ahora dentro de main() también se ha modificado
                * aunque la función no haya retornado ningún valor.
                */
                return 0;
}
 void sumar_referencia(int *numero)
{
               *numero += 1; /* le sumamos 1 al numero */
                /* el valor de numero recibido se aumenta en 1
                * y se modifica dentro de la función
                */
               printf("\nValor de numero dentro sumar_referencia() es: %d", *numero);
                return;
}


a)- Ejemplo donde se analiza las funciones internas, asociándola con la definición matemática de función.

Hemos hablado ya sobre las expresiones aritméticas y cómo podemos traducir fórmulas matemáticas usando diferentes clases de operadores aritméticos. Sin embargo, ¿qué pasa si en mi algoritmo necesito calcular una raíz cuadrada? ¿o si necesito calcular alguna función trigonométrica? ¿y qué tal si hay que redondear un valor numérico? Pues para esos casos existen las llamadas Funciones Internas, incorporadas o estándar, que forman parte de prácticamente todos los lenguajes de programación. 

La idea de estas funciones es ahorrarnos trabajo puesto que no tenemos que hacer más que llamarlas por su nombre (o "invocarlas") y pasarles algunos parámetros para que nos regresen el resultado que buscamos. Por ejemplo, la función raiz2 nos permite calcular la raíz cuadrada de un número y lo único que tenemos que hacer es pasarle como parámetro el número cuya raíz cuadrada queremos calcular. Algo asi:
 x ← raiz2(25)    
Después de esta instrucción, x tendría el valor de 5.
En esta tabla encontramos las funciones internas más frecuentes así como su descripción y un ejemplo de uso para comprender mejor su funcionamiento.

Veamos ahora cómo usar estas funciones para traducir a expresiones algorítmicas una de las fórmulas matemáticas más conocidas: la fórmula general para resolver ecuaciones de segundo grado (sí recuerdan sus matemáticas de secundaria, ¿verdad?):



O lo que es lo mismo:






Estas fórmulas, traducidas a expresiones algorítmicas quedarían así:

x1 = (-b + raiz2(cuadrado(b) - 4 * a * c)) / (2 * a)
x1 = (-b - raiz2(cuadrado(b) - 4 * a * c)) / (2 * a)


b)  ACTIVIDADES
EJERCICIO DE PROGRAMACIÓN
DIFERENCIA ENTRE EL PASO POR VALOR Y PASO POR REFERENCIA.
PASO DE PARÁMETROS POR VALOR:

 Al realizar la definición de la función colocamos los parámetros que ésta recibe especificando el tipo de dato de los mismos.

Luego, cuando hacemos el llamado y especificamos los argumentos, estos se evalúan y se le pasa a la función una copia de los mismos. A esto es lo que llamamos paso por valor ya que la función trabajará con los valores de esas copias. Si pasáramos variables como argumento, éstas no se modificarían por este sistema de copias.

Veamos esto con un ejemplo simple en que tenemos la siguiente función void que intercambia los valores de dos enteros x e y:


Declaramos dos variables de tipo int en el main, mostramos sus valores, llamamos a la función y volvemos a mostrarlos de la siguiente manera:


Y veríamos en pantalla:

Donde vemos que, en efecto no se modificaron los valores. Pero agreguemos un printf al final de la función para ver si se habían modificado o no los valores:



Y obtenemos:


Donde vemos que, en efecto se modificaron los valores de x e y dentro de la función pero que no se modificaron las variables originales ya que al hacer el pasaje por valor se trabaja con la copia de las mismas.
PASO DE PARÁMETROS POR REFERENCIA
En el pasaje por referencia se pasa a la función las direcciones de memoria de las variables en cuestión en lugar de su valor. A diferencia del paso por valor, aquí no se realizan copias de las variables sino que se trabaja sobre la dirección de memoria que pasamos, lo que nos permite modificar el valor de la variable en cuestión.
Veamos esto con el mismo ejemplo que usamos antes, pero usando punteros:

Luego hacemos el llamado a la función de la misma manera, pero pasando las direcciones de memoria de x e y:
Obteniendo lo siguiente al compilar el programa y ejecutarlo:

 Donde vemos que esta vez sí se modificó el contenido de las variables.