diciembre 2017


Recursividad en C++

La recursividad es la propiedad que tienen las funciones para llamarse a sí mismas. Es útil para realizar tareas tales como: Clasificar elementos ó calcular el factorial de los números. Por ejemplo, para obtener el factorial de un número (n!) la fórmula matemática sería:
n! = n * (n-1) * (n-2) * (n-3)... * 1   
Más concretamente, 5! (factorial de 5) sería:
5! = 5 * 4 * 3 * 2 * 1 = 120
Y una función recursiva en C++ también podría calcular eso y seria:
Observe cómo en la función factorial incluímos una llamada a sí misma, pero sólo si el argumento pasado era mayor que 1, ya que de lo contrario, la función realizaría un bucle recursivo infinito y una vez que llegara a 0 continuaría multiplicándose por todos los números negativos (lo que probablemente provoque un desbordamiento de la pila en algún punto durante el tiempo de ejecución).


En esta capítulo hemos dado énfasis al tema de funciones y como son declaradas antes de su definición. Todo para poder darle más legibilidad al código; esto y otras cosas usted aprenderá aqui en este site bastante completo. No dejemos de practicar para conseguir nuestros objetivos. Les recomiendo que sigan mis links donde he colocado varios ejemplos básicos y complejos; sí, repítanlos para aprenderlos bien.

Declarando Funciones en C++

En C++, los identificadores solo se pueden usar en expresiones una vez que se han declarado. Por ejemplo, alguna variable x no se puede usar antes de declararse con una instrucción, por ejemplo:
int x;
Lo mismo se aplica a las funciones, las funciones no pueden invocarse antes de declararse. Es por eso que, en todos los ejemplos anteriores de funciones, las funciones siempre se definieron antes que la función main, que es la función desde donde se llamaron a las otras funciones. Si main se definió antes que las otras funciones, esto romperia la regla de que las funciones se declaran antes de ser utilizadas y por lo tanto, no se compilarían.La declaración incluirá todos los tipos implicados (el tipo de retorno y el tipo de sus argumentos), utilizando la misma sintaxis que se utiliza en la definición de la función, pero reemplazando el cuerpo de la función(el bloque de enunciados) con un punto y coma final.
La lista de parámetros no necesita incluir los nombres de los parámetros, sino solo sus tipos. Sin embargo, los nombres de los parámetros se pueden especificar, pero son opcionales y no es necesario que coincidan necesariamente con los de la definición de la función. Por ejemplo, una función llamada protofuncion con dos parámetros int se puede declarar con cualquiera de estas declaraciones:

int protofuncion(int first, int second);
int protofuncion(int, int);
De todos modos, incluir un nombre para cada parámetro siempre mejora la legibilidad de la declaración:Este ejemplo no es un ejemplo de eficiencia. Probablemente usted pueda escribir una version de este programa con la mitad de las líneas de código. De todos modos, este es un ejemplo que ilustra como se pueden declarar las funciones antes de su definición.Las siguientes líneas:
void impar (int a);
void par (int a);
declaran el prototipo de las funciones. Ellas ya contienen todo lo necesario para llamarlos: su nombre, los tipos de argumento y su tipo de devolución (void en este caso). Con estas declaraciones de prototipos en su lugar, se pueden llamar antes de que esté completamente definidas, lo que permite, por ejemplo, colocar la función desde donde se las llama (main) antes de la definición real de estas funciones. Pero declarar funciones antes de definirlas no sólo es útil para reorganizarlas dentro del código. En algunos casos, como este en particular, se requiere al menos de una de las declaraciones, porque "par impar" están siendo llamadas mutuamente; hay una llamada a "par" en "impar" y una llamada a "impar" en "par". Y no hay forma de estructurar el código para que "impar" se defina antes de "par" y "par" antes de "impar".

En esta capítulo hemos dado énfasis al tema de funciones y como son declaradas antes de su definición. Todo para poder darle más legibilidad al código; esto y otras cosas usted aprenderá aqui en este site bastante completo. No dejemos de practicar para conseguir nuestros objetivos. Les recomiendo que sigan mis links donde he colocado varios ejemplos básicos y complejos; sí, repítanlos para aprenderlos bien.

Valores Predeterminados en los Parámetros en C++

En C++, las funciones también pueden tener parámetros opcionales, para las cuales no se requieren argumentos en la llamada, de tal manera que, por ejemplo una función con tres parámetros puede ser llamada con sólo dos. Para eso, la función debe incluir un valor predeterminado para su ultimo parámetro, que será usado por la función cuando se llama con menos argumentos. Por ejemplo:

En este ejemplo, hay dos llamadas a la función dividir. En el primero:
dividir (12)
La llamada solo pasa un argumento a la función, aunque la función tenga dos parámetros. En este caso, la función asume que el segundo parámetro es 2(observe la definición de la función, que declara su segundo parámetro como int b=2). Por lo tanto, el resultado es 6.

En la segunda llamada:
dividir (20,4)
La llamada pasa dos argumentos a la función. Por lo tanto, el valor predeterminado para (int b=2) se ignora y b toma el valor pasado como argumento, es decir 4, produciendo un resultado de 5.

En esta parte como ustedes pueden darse cuenta, estamos aprendiendo a pasar argumentos a traves de las funciones de una manera bastante detallada para que ustedes mis queridos lectores puedan entender y memorizar cada parte de este valioso tutorial. Sólo siempre voy a repetir: No dejemos de practicar para conseguir nuestros objetivos. Les recomiendo que sigan mis links donde he colocado varios ejemplos básicos y complejos; sí, repítanlos para aprenderlos bien.


Funciones inline

Las funciones inline ó en línea son una función de mejora de C++ para aumentar el tiempo de ejecución de un programa. Las funciones pueden ser instruidas al compilador para que estén en línea inline, de modo que el compilador pueda reemplazar esa definición de función donde sea que se llamen. El compilador reemplaza la definición de funciones inline en línea en tiempo de compilación en lugar de referir definición de funciones en tiempo de ejecución.
Para entender mejor: cuando llamamos a una función generalmente se causa una cierta sobrecarga (apilamiento, argumentos, saltos, etc...) y, por lo tanto, para funciones muy cortas; suele ser mucho más eficiente insertar el código de la función exactamente donde se llama a la función en lugar pues de realizar el proceso de llamar a la función de manera formal. 
Para esto pues es que aparece el especificador inline el cual lo que hace es informar al compilador: que la extensión inline es preferible en lugar del mecanismo de llamada de función habitual específica. Esto no cambia para nada el comportamiento de una función, sino simplemente es utilizado para SUGERIR al compilador que el código generado por el cuerpo de  la función debería insertarse en cada punto de la función donde es llamada, en lugar de invocarse con una llamada regular a esta función. Por ejemplo, la función concatenate puede declararse inline como:
inline string concatenate (const string& a, const string& b)
{
return a+b;
}
Esto informa al compilador que cuando se llama a concatenate, el programa prefiere que la función se expanda alineada inline, en lugar de realizar una llamada regular.
Pero tenga en cuenta que la mayoría de los compiladores ya optimizan el código para generar funciones alineadas inline cuando observa una oportunidad que también mejora la eficiencia. Por lo tanto, este especificador simplemente indica al compilador que se prefiere alineadamente inline esta función, aunque el compilador es libre de no alinearlas y optimizarlas al contrario. En C++, la optimización es una tarea delegada al copilador, que es libre de generar cualquier tipo decódigo siempre y cuando el comportamiiento rersultante sea el especificado por el código.


Un tema no menos importante en nuestra caminata, no dejemos de practicar para conseguir nuestros objetivos. Les recomiendo que sigan mis links donde he colocado varios ejemplos básicos y complejos; sí, repítanlos para aprenderlos bien.




Argumentos pasados Por Valor y Por Referencia

En las funciones vistas anteriormente, los argumentos siempre han sido pasados por el valor, esto significa que: al llamar a una función, lo que se pasa a la función son los valores de estos argumentos en el momento de la llamada, que se copian en las variables representadas por los parámetros de la función. Por ejemplo, tomar:
int x=5, y=3, z;
z = addition (x,y);
En este caso, la función addition esta pasando 5 y 3, que son copias del valor de x e y. Estos valores (5 y 3) se usan para inicializar las variables establecidas como parámetros en la definición de la función; pero cualquier modificación que se haga en estas variables dentro de la función no tendrá efecto sobre el valor de las variables y ni fuera de ella, todo esto debido a que no se pasaron a ninguna función en la llamada, tan solo eran copias de sus valores en ese momento.





Sin embargo, en ciertos casos, puede ser útil acceder a una variable externa desde dentro de una función. Para hacer eso, los argumentos se pueden pasar por referencia, en lugar de por su valor. Por ejemplo, la función duplicar en este código, duplica el valor de sus tres argumentos, haciendo que las variables utilizadas como argumentos realmente sean modificadas por la llamada. 
Para obtener acceso a sus argumentos, la función los declara como referencias. En C++, las referencias se indican con un signo de & a continuación del tipo del parámetro, como en los parámetros tomados por duplicado en el ejemplo anterior.
Cuando se pasa una variable por referencia, lo que se pasa ya no es su copia, sino exactamente la variable en sí, la variable identificada por el parámetro de la función, asociándose de algún modo con el argumento pasado a la función y a cualquier modificación en sus correspondientes variables locales dentro de la función, todo esto reflejadas en las variables pasadas como argumentos en la llamada. Veamos un ejemplo básico:





 De hecho, ab y c se convierten en alias de los argumentos pasados en la llamada a la funcion (x,y e z) y cualquier cambio en a dentro de la funcion está realmente modificando la variable "x" fuera de la función. Cualquier cambio en b modifica "y" y cualquier cambio en c modifica "z". Es por eso que cuando, en el ejemplo, la funcion duplicar modifica los valores de las variables a,b y c los valores de xy y z se ven afectados.
Si en lugar de definir duplicar como:
void duplicar (int& a, int& b, int& c)
Se definiese sin los signos ampersand como:
void duplicar (int a, int b, int c)
Entonces las variables. no se pasaran por referencia, sino porvalor, creando en su lugar copias de sus valores. En este caso, la salida del programa habria sido los valores de "x","y" y "z" sin modificarse (es decir 1,3 y 7).

Consideraciones de Eficiencia y referencias const

LLamar a una función con parámetros tomados con valor hace que se hagan copias de los valores. Esta es una operación eficiente para tipos fundamentales como int, pero si el parámetro es de un tipo compuesto grande, puede ocasionar cierta sobrecarga. Por ejemplo, considere la siguiente función:
string concatenate(string a, string b)
{
return a+b;
}
Esta función toma dos cadenas strings como parámetros (por valor) y devuelve el resultado de concatenarlas. Al pasar los argumentos por valor, la función obliga a b a ser copias de los argumentos pasados a la función cuando se la llama. Y si se trata de cadenas strings largas, puede esto significar copiar grandes cantidades de datos solo para la llamada a la función. Pero, esta copia se puede evitar en gran medida si ambos parámetros se pasan como referencias:
string concatenate (string& a, string& b)
{
return a + b;
}
Los argumentos por referencia no requieren de una copia. La función opera directamente (alias de) en la string pasada como argumento, y a lo máximo puede significar la transferencia de determinados punteros para la función. En este sentido, la version de referencia de la función concatenate es más eficiente que la version que toma valores, ya que no necesita copias cadenas voluptuosas para copiar. Por otro lado, las funciones con parametros de referencia generalmente son percibidas como funciones que modifican los argumentos pasados, porque es para eso que los parámetros de referencia son realmente. La solución es para la función de garantizar que sus parámetros de referencia no vayan a ser modificados por esta función. Esto se puede hacer calificando los parámetros como constantes:
string concatenate (const string& a, const string& b)
{
return a+b;
}
Al calificarlos como const, la función está prohibida para modificar los valores de a y de b, pero puede realmente accesar sus valores como referências (alias de los argumentos), sin tener que hacer copias de las strings.
Por lo tanto, las referencias const proporcionan funcionalidad similar a pasar argumentos por valor, pero aumentan la eficiencia para parámetros de tipos grandes. Es por eso que son extremamente populares en C++ para los argumentos de los tipos de compilación. Sin embargo, tenga en cuenta que para la mayoría de los tipos fundamentales, no existe una diferencia notable en eficiencia y en algunos casos las referencias de const pueden ser incluso menos eficientes.




Un tema bastante importante en nuestra caminata, no dejemos de practicar para conseguir nuestros objetivos. Les recomiendo que sigan mis links donde he colocado varios ejemplos básicos y complejos; sí, repítanlos para aprenderlos bien.

El Valor "return" de main

Usted puede haber notado que el tipo de retorno de main es int, pero la mayoría de los ejemplos en este capítulo no retornó ningún valor desde main.
Bueno, hay un inconveniente: si la ejecución de main termina normalmente sin encontrar una declaración de retorno return, el compilador deduce que la función termina con una declaración de devolución implícita.
return 0;
Cuando main devuelve cero (explicita o implícitamente), el retorno lo interpreta como que el programa finalizó correctanente. Otros valores pueden ser devueltos por main, y algunos entornos dan acceso a ese valor a la persona que llama de alguna manera aunque este comportamiento no es necesariamente portátil entre las plataformas. Estos valores para main que están organizados para ser interpretados de la misma manera en todas las plataformas son: 
Valor    Descripción
0    
       El programa tuvo Éxito.
EXIT_SUCCESS   
       El programa tuvo Éxito.
  El valor esta definido en el encabezado<cstdlib>.
EXIT_FAILURE    
       El programa falló.
Este valor esta definido en el encabezado<cstdlib>.
Por que el implícito return 0 para la declaración main es una excepción engañosa, algunos autores consideran que es una buena practica escribir explicitamente el enunciado.



Bueno ahora estamos preparados para practicar bien con ejemplos creados por nosotros mismos. Los invito a visitar algunos ejemplos que he creado para iniciantes. Voy a subirlos en breve y por este blog estaré postando; no descuiden su practica para fijar conocimientos bastante importantes como este!
Cuando nos encontramos en una situación donde se requiere que la declaración comience con un tipo, todo perfecto, pero ¿y si la función no tiene parámetros o no devolverá ningún valor? En este caso, la palabra clave a utilizar es void que es un tipo especial para representar la ausencia de valor. Por ejemplo este caso puede presentarse en una función que simplemente imprime un mensaje y esta puede no necesitar devolver ningún valor.
void también puede ser usado en la lista de parámetros de la función para especificar explicitamente lo que la función toma en los parámetros actuales cuando es llamada. Por ejemplo mostrarMensaje se pudo también haber declarado de la siguiente manera:
void mostrarMensaje (void)
{
cout <<"Yo soy una función" << "\n";
}
El uso de void en la lista de parámetros es opcional; un par de paréntesis vacíos es aceptable y preferible. 
Entonces que quede claro que algo que en ningún caso es opcional son los paréntesis que siguen el nombre de la función, ni en su declaración ni al llamarlo. E incluso cuando la función no tome parámetros, al menos un par de paréntesis vacíos se adjuntará al nombre de la función. Vea como se llamó a mostrarMensaje en el ejemplo anterior:
mostrarMensaje();
Los paréntesis son los que diferencian las funciones de otros tipos de declaraciones o argumentos. Lo siguiente, no llamaría a la función:
mostrarMensaje;



Aprendido esto pasamos a la siguiente sesión mis curiosos lectores; si hay algo que no les esta gustando en el site o creen que debería considerar; les agradeceria que me lo hagan saber con un comentário!