Herramientas de usuario

Herramientas del sitio


curso-cpp:modularizacion-funciones

Diferencias

Muestra las diferencias entre dos versiones de la página.

Enlace a la vista de comparación

Ambos lados, revisión anterior Revisión previa
Próxima revisión
Revisión previa
curso-cpp:modularizacion-funciones [2017/03/19 20:50]
santo [Algunos ejemplos de macros muy útiles]
curso-cpp:modularizacion-funciones [2017/10/29 19:24] (actual)
santo
Línea 15: Línea 15:
 Supongamos que tenemos el siguiente código (que iría dentro de ''​main''​ en un programa): Supongamos que tenemos el siguiente código (que iría dentro de ''​main''​ en un programa):
  
-<​code>​+<​code ​cpp>
 int a,b; int a,b;
 cout << "​Ingrese el rango de numeros a explorar"​ << endl; cout << "​Ingrese el rango de numeros a explorar"​ << endl;
Línea 42: Línea 42:
 Es muy difícil de entender qué hace este código a simple vista, y el motivo principal es que **hace muchas cosas mezcladas todas a la vez**. Veremos que una función permite **separar** una porción de programa definida, y luego **usarla** cuando la necesitemos. Es bueno comparar el tener que leer y entender el programa anterior, con leer algo como lo siguiente: Es muy difícil de entender qué hace este código a simple vista, y el motivo principal es que **hace muchas cosas mezcladas todas a la vez**. Veremos que una función permite **separar** una porción de programa definida, y luego **usarla** cuando la necesitemos. Es bueno comparar el tener que leer y entender el programa anterior, con leer algo como lo siguiente:
  
-<​code>​+<​code ​cpp>
 int a,b; int a,b;
 leerRangoAExplorar(a,​b);​ leerRangoAExplorar(a,​b);​
Línea 50: Línea 50:
 Aquí, ''​leerRangoAExplorar(a,​b);''​ corresponderá al siguiente fragmento del código anterior: Aquí, ''​leerRangoAExplorar(a,​b);''​ corresponderá al siguiente fragmento del código anterior:
  
-<​code>​+<​code ​cpp>
 cout << "​Ingrese el rango de numeros a explorar"​ << endl; cout << "​Ingrese el rango de numeros a explorar"​ << endl;
 cin >> a >> b; cin >> a >> b;
Línea 57: Línea 57:
 Mientras que ''​sumaDePrimosAlCuadradoEnRango(a,​b)''​ representa: Mientras que ''​sumaDePrimosAlCuadradoEnRango(a,​b)''​ representa:
  
-<​code>​+<​code ​cpp>
 int sumaPrimosAlCuadrado = 0; int sumaPrimosAlCuadrado = 0;
 for (int i=a; i<= b; i++) for (int i=a; i<= b; i++)
Línea 73: Línea 73:
 ''​sumaDeMultiplosEspecialesEnRango(a,​b)''​ corresponde al: ''​sumaDeMultiplosEspecialesEnRango(a,​b)''​ corresponde al:
  
-<​code>​+<​code ​cpp>
 int sumaMultiplosEspeciales = 0; int sumaMultiplosEspeciales = 0;
 for (int i=a; i<= b; i++) for (int i=a; i<= b; i++)
Línea 84: Línea 84:
 Y el ''​mostrarResultados''​ corresponde al: Y el ''​mostrarResultados''​ corresponde al:
  
-<​code>​+<​code ​cpp>
 cout << "La primera suma pedida es:" << sumaPrimosAlCuadrado << endl; cout << "La primera suma pedida es:" << sumaPrimosAlCuadrado << endl;
 cout << "La segunda suma pedida es:" << sumaMultiplosEspeciales << endl; cout << "La segunda suma pedida es:" << sumaMultiplosEspeciales << endl;
Línea 93: Línea 93:
 ==== Facilitar un enfoque top-down ==== ==== Facilitar un enfoque top-down ====
  
-Esta ventaja está estrechamente relacionada con la anterior. Supongamos que nos dieran la siguiente ​consiga:+Esta ventaja está estrechamente relacionada con la anterior. Supongamos que nos dieran la siguiente ​consigna:
  
 "Crear un programa que lea dos números ''​a''​ y ''​b'',​ que indican un rango de números (inclusive),​ y calcule y muestre en la pantalla dos valores: La suma de los cuadrados de todos los números primos entre a y b, y además, la suma de todos los números entre a y b que son múltiplos de 3 y de 10, pero no de 30." "Crear un programa que lea dos números ''​a''​ y ''​b'',​ que indican un rango de números (inclusive),​ y calcule y muestre en la pantalla dos valores: La suma de los cuadrados de todos los números primos entre a y b, y además, la suma de todos los números entre a y b que son múltiplos de 3 y de 10, pero no de 30."
Línea 112: Línea 112:
 Finalmente, llamaremos ''​mostrarResultados''​ al proceso del paso 4), que **usa los resultados obtenidos en los pasos 2 y 3**. Con estas ideas, podemos planear un ''​esqueleto''​ de nuestro programa, que quedaría más o menos así: Finalmente, llamaremos ''​mostrarResultados''​ al proceso del paso 4), que **usa los resultados obtenidos en los pasos 2 y 3**. Con estas ideas, podemos planear un ''​esqueleto''​ de nuestro programa, que quedaría más o menos así:
  
-<​code>​+<​code ​cpp>
 int a,b; int a,b;
 leerRangoAExplorar(a,​b);​ leerRangoAExplorar(a,​b);​
Línea 134: Línea 134:
 La sintaxis para definir una función es la siguiente: La sintaxis para definir una función es la siguiente:
  
-<​code>​+<​code ​cpp>
 tipo_de_la_respuesta nombreDeLaFuncion(lista_de_parametros) tipo_de_la_respuesta nombreDeLaFuncion(lista_de_parametros)
 { {
Línea 148: Línea 148:
 Veamos un ejemplo de función con ''​lista_de_parametros''​ vacía, lo cual es válido en C++: Veamos un ejemplo de función con ''​lista_de_parametros''​ vacía, lo cual es válido en C++:
  
-<​code>​+<​code ​cpp>
 int leerUnNumeroYElevarloAlCuadrado() int leerUnNumeroYElevarloAlCuadrado()
 { {
Línea 165: Línea 165:
 Veamos a continuación un ejemplo de programa completo que usa esa función: Veamos a continuación un ejemplo de programa completo que usa esa función:
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 192: Línea 192:
 Así, si al ejecutar el programa anterior ingresáramos los valores 3, 1, y 10, sería como si en el main se ejecutase lo siguiente: Así, si al ejecutar el programa anterior ingresáramos los valores 3, 1, y 10, sería como si en el main se ejecutase lo siguiente:
  
-<​code>​+<​code ​cpp>
 int main() int main()
 { {
Línea 204: Línea 204:
 Obteniendo el resultado 110. A modo de ejemplo, damos una versión distinta del programa que calcula la suma del **triple** de cada número al cuadrado, para que quede claro que las llamadas a funciones se pueden usar en el medio de expresiones más complejas si así nos conviene (damos solo el main, pues la función es igual que antes): Obteniendo el resultado 110. A modo de ejemplo, damos una versión distinta del programa que calcula la suma del **triple** de cada número al cuadrado, para que quede claro que las llamadas a funciones se pueden usar en el medio de expresiones más complejas si así nos conviene (damos solo el main, pues la función es igual que antes):
  
-<​code>​+<​code ​cpp>
 int main() int main()
 { {
Línea 216: Línea 216:
 Incluso sería válido (aunque es más difícil de leer) escribir el programa original con todas las llamadas en la misma línea: Incluso sería válido (aunque es más difícil de leer) escribir el programa original con todas las llamadas en la misma línea:
  
-<​code>​+<​code ​cpp>
 int main() int main()
 { {
Línea 231: Línea 231:
 ===== Parámetros ===== ===== Parámetros =====
  
-No siempre queremos que una función haga exactamente lo mismo cada vez que usa. A veces, queremos que haga **casi** lo mismo, pero cambiando algún **dato** entre usos. Por ejemplo, podríamos querer una función que eleve un número al cuadrado, es decir, que permita calcular ''​x*x''​ si ya tenemos un entero ''​x''​. Así, cuando usamos la función con 3, queremos que devuelva ''​3*3 == 9'',​ y cuando la usamos con -4 queremos que devuelva ''​(-4)*(-4) == 16''​.+No siempre queremos que una función haga exactamente lo mismo cada vez que se usa. A veces, queremos que haga **casi** lo mismo, pero cambiando algún **dato** entre usos. Por ejemplo, podríamos querer una función que eleve un número al cuadrado, es decir, que permita calcular ''​x*x''​ si ya tenemos un entero ''​x''​. Así, cuando usamos la función con 3, queremos que devuelva ''​3*3 == 9'',​ y cuando la usamos con -4 queremos que devuelva ''​(-4)*(-4) == 16''​.
  
 En el ejemplo anterior la función no hace siempre lo mismo, porque a veces hace 3*3 y a veces (-4)*(-4), pero más allá del número que vamos a elevar, las **operaciones** que hace la función son siempre las mismas, y solo cambia este **dato** inicial. A ese **dato que cambia**, lo llamamos en programación un **parámetro** de la función. Una función puede tener 1 o más parámetros,​ o incluso cero: Las funciones que vimos antes tenían cero parámetros. La función de elevar al cuadrado tendría un único parámetro: El número entero que vamos a querer elevar. En el ejemplo anterior la función no hace siempre lo mismo, porque a veces hace 3*3 y a veces (-4)*(-4), pero más allá del número que vamos a elevar, las **operaciones** que hace la función son siempre las mismas, y solo cambia este **dato** inicial. A ese **dato que cambia**, lo llamamos en programación un **parámetro** de la función. Una función puede tener 1 o más parámetros,​ o incluso cero: Las funciones que vimos antes tenían cero parámetros. La función de elevar al cuadrado tendría un único parámetro: El número entero que vamos a querer elevar.
Línea 239: Línea 239:
 Veamos a continuación el ejemplo de la función para elevar un entero al cuadrado: Veamos a continuación el ejemplo de la función para elevar un entero al cuadrado:
  
-<​code>​+<​code ​cpp>
 int alCuadrado(int x) int alCuadrado(int x)
 { {
Línea 252: Línea 252:
 Veamos un ejemplo completo de programa que usa la función ''​alCuadrado'':​ Veamos un ejemplo completo de programa que usa la función ''​alCuadrado'':​
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 279: Línea 279:
 Una función puede necesitar varios datos para realizar su tarea. De ser así, se trabaja de idéntica manera pero separando los parámetros con comas, y dándolos siempre en el mismo orden. Por ejemplo, a continuación se muestra un ejemplo de código que usa una función que pega dos palabras usando un guión entre ellas: Una función puede necesitar varios datos para realizar su tarea. De ser así, se trabaja de idéntica manera pero separando los parámetros con comas, y dándolos siempre en el mismo orden. Por ejemplo, a continuación se muestra un ejemplo de código que usa una función que pega dos palabras usando un guión entre ellas:
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 310: Línea 310:
 ¿Por qué podríamos querer que una función no devuelva nada? Porque podría importarnos solamente lo que la función **hace**, es decir, las instrucciones que ejecuta, sin necesidad de que nos devuelva un valor final. Un ejemplo sencillo de esto podría ser alguna función que escriba en pantalla: ¿Por qué podríamos querer que una función no devuelva nada? Porque podría importarnos solamente lo que la función **hace**, es decir, las instrucciones que ejecuta, sin necesidad de que nos devuelva un valor final. Un ejemplo sencillo de esto podría ser alguna función que escriba en pantalla:
  
-<​code>​+<​code ​cpp>
 void mostrarConEspacios(string palabra) void mostrarConEspacios(string palabra)
 { {
Línea 332: Línea 332:
 Supongamos que escribimos la siguiente función, con la idea de que aumente en uno la variable indicada: Supongamos que escribimos la siguiente función, con la idea de que aumente en uno la variable indicada:
  
-<​code>​+<​code ​cpp>
 void incrementar(int x) void incrementar(int x)
 { {
Línea 341: Línea 341:
 Con lo que vimos hasta ahora, podría parecer que esta función hará lo que queremos. Podemos intentar utilizarla en un programa: Con lo que vimos hasta ahora, podría parecer que esta función hará lo que queremos. Podemos intentar utilizarla en un programa:
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 367: Línea 367:
 Para ayudar a entender esto agregaremos al programa un par de instrucciones con ''​cout''​ dentro de la función, para ver si se incrementa o no. Para ayudar a entender esto agregaremos al programa un par de instrucciones con ''​cout''​ dentro de la función, para ver si se incrementa o no.
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 413: Línea 413:
 Nuestro ejemplo quedaría entonces (solamente le agregamos un ''&''​ en el parámetro ''​x''​):​ Nuestro ejemplo quedaría entonces (solamente le agregamos un ''&''​ en el parámetro ''​x''​):​
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 463: Línea 463:
 Un ejemplo podría ser una función que recibe un ''​vector<​int>''​ y duplica todos sus elementos: Un ejemplo podría ser una función que recibe un ''​vector<​int>''​ y duplica todos sus elementos:
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
 #include <​vector>​ #include <​vector>​
Línea 510: Línea 510:
 Por ejemplo, el siguiente código: Por ejemplo, el siguiente código:
  
-<​code>​+<​code ​cpp>
 int mivar = 32; int mivar = 32;
  
Línea 534: Línea 534:
  
   * En las funciones se puede llamar (utilizar) otras funciones: esto está totalmente permitido:   * En las funciones se puede llamar (utilizar) otras funciones: esto está totalmente permitido:
-    <​code>​+    <​code ​cpp>
     int multiplicar(int a, int b)     int multiplicar(int a, int b)
     {     {
Línea 550: Línea 550:
   * Variables con el mismo nombre pero definidas en funciones distintas, representan variables **diferentes** (como el ''​x''​ del main y el ''​x''​ de la función en el ejemplo anterior de ''​incrementar''​)   * Variables con el mismo nombre pero definidas en funciones distintas, representan variables **diferentes** (como el ''​x''​ del main y el ''​x''​ de la función en el ejemplo anterior de ''​incrementar''​)
  
 +
 +===== Ejemplos de implementación de funciones =====
 +
 +  * Nuestro primer ejemplo es la función ''​main'',​ que ya venimos usando en todos nuestros programas: Es una función que devuelve un ''​int'',​ que usa la computadora para saber si hubo errores. Por convención,​ se debe devolver cero si todo salió bien, y por eso es buena costumbre terminar todos los programas con ''​return 0''​. La función ''​main''​ es importante porque tiene la característica especial de que allí comienzan a ejecutarse todos nuestros programas, aunque tengan otras funciones.
 +  * Como ejemplo de pensamiento top-down, supongamos que debemos realizar un programa que lea una secuencia de números, y luego calcule y muestre por pantalla la suma, el máximo y el mínimo de todos estos números. Podemos programar primero que anda el main de la siguiente manera:<​code cpp>
 +int main()
 +{
 +    vector<​int>​ v;
 +    v = leerNumeros();​
 +    imprimirResultados(suma(v),​ maximo(v), minimo(v));
 +    return 0;
 +}
 +</​code>​ Donde nos hemos ordenado y hemos logrado **descomponer** el problema entero en tareas más pequeñas. Luego podríamos agregarle las funciones que faltan al programa, para completarlo,​ dejando inalterado el mismo main que ya escribimos: <code cpp>
 +vector<​int>​ leerNumeros()
 +{
 +    // instrucciones...
 +}
 +
 +int suma(vector<​int>​ v)
 +{
 +    // instrucciones...
 +}
 +
 +int maximo(vector<​int>​ v)
 +{
 +    // instrucciones...
 +}
 +
 +int minimo(vector<​int>​ v)
 +{
 +    // instrucciones...
 +}
 +
 +void imprimirResultados(int laSuma,int elMaximo, int elMinimo)
 +{
 +    // instrucciones...
 +}
 +</​code>​
 +
 +  * El siguiente es un ejemplo con funciones que calculan áreas de figuras geométricas,​ que muestra como podemos reutilizar ciertas funciones dentro de otras: <code cpp>
 +int areaParalelogramo(int base, int altura)
 +{
 +    return base * altura;
 +}
 +int areaCuadrado(int lado)
 +{
 +    return areaParalelogramo(lado,​ lado);
 +}
 +int areaTriangulo(int base, int altura) // Trabaja con enteros: Redondea hacia abajo
 +{
 +    return areaParalelogramo(base,​ altura) / 2;
 +}
 +</​code>​
 +  * El siguiente es un ejemplo de función que recibe dos variables enteras, e intercambia sus valores. ¡Notar el uso del ampersand! <code cpp>
 +void intercambiar(int &​variable1,​ int &​variable2)
 +{
 +    int auxiliar = variable1; // Es necesario un auxiliar: ¿Por qué?
 +    variable1 = variable2;
 +    variable2 = auxiliar;
 +} </​code>​ Similarmente,​ el siguiente ejemplo permite "​rotar"​ los valores de tres variables dadas: Es decir, transforma [a,b,c] en [b,c,a]: <code cpp>
 +void rotar3(int &a, int &b, int &c)
 +{
 +   int auxiliar = a; // Nuevamente, ¿Por qué es necesario el auxiliar?
 +   a = b;
 +   b = c;
 +   c = auxiliar;
 +}
 +</​code>​
 +===== Algunas funciones predefinidas =====
 +
 +En C++, existen algunas funciones predefinidas que ya existen, y conocerlas puede simplificarnos la tarea de programar ya que nos ahorramos tener que escribirlas nosotros mismos. Mencionamos algunas a continuación:​
 +
 +  * ''​max'':​ Dados dos números, devuelve el máximo. Por ejemplo ''​max(2,​9) == 9''​ y ''​max(5,​3) == 5''​. Similarmente tenemos ''​min''​ para el mínimo. ​
 +  * ''​swap'':​ Intercambia los valores de las dos variables que se le indica. Por ejemplo si ''​x''​ tiene un ''​3'',​ y ''​q[i]''​ tiene un ''​8'',​ luego de hacer ''​swap(x,​q[i])''​ quedará ''​q[i]''​ con un 3 y ''​x''​ con un 8.
 +  * ''​abs'':​ Devuelve el valor absoluto (módulo) de un entero. Por ejemplo ''​abs(-3) == 3'',​ ''​abs(0) == 0''​ y ''​abs(15) == 15''​.
 +
 +Todas estas funciones requieren utilizar ''#​include <​algorithm>''​ para tenerlas disponibles.
 +
 +===== Algunos errores comunes =====
 +
 +   * Pasar a la función una cantidad de parámetros diferente de las que la función necesita, o con el tipo incorrecto. Por ejemplo si tenemos la función <code cpp>
 +int mayor(int num1, int num2)
 +{
 +    if (num1 > num2)
 +        return num1;
 +    else
 +        return num2;
 +}
 +</​code>​ serían incorrectas las siguientes llamadas: <code cpp>
 +mayor(k,​m,​n) // Pasa 3 parámetros,​ pero la función toma solamente 2
 +mayor(23, "​miliwatt"​) // Pasa 2 parámetros,​ pero el segundo es una cadena y debería ser un int
 +</​code>​
 +   * Diseñar una función con la idea de que **modifique** uno de sus parámetros,​ pero trabajar con una copia por no utilizar el ampersand ''&''​.
 +   * Intentar utilizar un parámetro (con su nombre) **fuera** de una función: Los parámetros solamente están definidos dentro de la función, y no tiene sentido utilizarlos fuera de ella (son variables locales).
 +   * Utilizar una función que todavía no se definió. Se debe programar el código de una función, antes de utilizarla.
 ===== Ejercicios ===== ===== Ejercicios =====
  
Línea 556: Línea 651:
 Por ejemplo, en los ejercicios en los que se hablaba de números primos, se podría escribir una función que toma un ''​int N'',​ y devuelve un ''​bool''​ indicando si es primo. O por ejemplo, escribir una función ''​sumaDeDivisores''​ puede ser útil para escribir de forma más fácil y clara programas que buscan números perfectos. Por ejemplo, en los ejercicios en los que se hablaba de números primos, se podría escribir una función que toma un ''​int N'',​ y devuelve un ''​bool''​ indicando si es primo. O por ejemplo, escribir una función ''​sumaDeDivisores''​ puede ser útil para escribir de forma más fácil y clara programas que buscan números perfectos.
  
 +Otros ejercicios:
 +
 +   * Escribir una función ''​string escribirEnBase(int numero, int base)'',​ que tome un número y una base (Entre 2 y 16 inclusive) y devuelva una cadena con la escritura de ese número en la base indicada.
 +   * Escribir una función ''​int leerNumeroEnBase(string escritura, int base)'',​ que tome la escritura de un cierto número en la base indicada (Entre 2 y 16 inclusive) y devuelva el número en cuestión.
 +
 +Puede ver aquí [[algoritmos-oia:​enteros:​cambio-de-base|cómo realizar cambios de base]].
 ===== Los #define ===== ===== Los #define =====
  
Línea 562: Línea 663:
 Mediante un ''#​define'',​ es posible definir lo que se denomina una //macro//, que es una regla para **reemplazar textualmente un fragmento de código en el programa**. Por ejemplo, supongamos que colocamos en cualquier línea de un programa, el siguiente ''#​define'':​ Mediante un ''#​define'',​ es posible definir lo que se denomina una //macro//, que es una regla para **reemplazar textualmente un fragmento de código en el programa**. Por ejemplo, supongamos que colocamos en cualquier línea de un programa, el siguiente ''#​define'':​
  
-<​code>​+<​code ​cpp>
 #define declaraEInicializaEnUnValor(nombreVariable,​ valor) int nombreVariable = valor #define declaraEInicializaEnUnValor(nombreVariable,​ valor) int nombreVariable = valor
 </​code>​ </​code>​
Línea 569: Línea 670:
 Por ejemplo,el siguiente sería un programa válido: Por ejemplo,el siguiente sería un programa válido:
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 587: Línea 688:
 Que muestra por pantalla 140. Notar que luego de los reemplazos, el programa es absolutamente equivalente a si se hubiera escrito directamente:​ Que muestra por pantalla 140. Notar que luego de los reemplazos, el programa es absolutamente equivalente a si se hubiera escrito directamente:​
  
-<​code>​+<​code ​cpp>
 #include <​iostream>​ #include <​iostream>​
  
Línea 607: Línea 708:
 Especialmente en competencias de programación,​ es muy común tener un fragmento de código como el siguiente: Especialmente en competencias de programación,​ es muy común tener un fragmento de código como el siguiente:
  
-<​code>​+<​code ​cpp>
 for (int numero = 0; numero < valorMaximo;​ numero++) for (int numero = 0; numero < valorMaximo;​ numero++)
     // instrucciones     // instrucciones
Línea 616: Línea 717:
 Otra variante muy similar sería cuando queremos recorrer todos los índices de un cierto vector: Otra variante muy similar sería cuando queremos recorrer todos los índices de un cierto vector:
  
-<​code>​+<​code ​cpp>
 for (int var = 0; var < int(vector.size());​ var++) for (int var = 0; var < int(vector.size());​ var++)
     // instrucciones     // instrucciones
Línea 625: Línea 726:
 Para programar más fácilmente y con menor chances de errores este tipo de for comunes, podemos utilizar un ''#​define''​ para definir una forma compacta de indicar los elementos importantes que cambian de caso en caso, y que el resto se reemplace mecánicamente siempre igual: Para programar más fácilmente y con menor chances de errores este tipo de for comunes, podemos utilizar un ''#​define''​ para definir una forma compacta de indicar los elementos importantes que cambian de caso en caso, y que el resto se reemplace mecánicamente siempre igual:
  
-<​code>​+<​code ​cpp>
 #define forn(i,n) for(int i=0;​i<​int(n);​i++) #define forn(i,n) for(int i=0;​i<​int(n);​i++)
 </​code>​ </​code>​
Línea 633: Línea 734:
 De esta forma, una vez que tenemos este #define el primer for que mostramos nos quedaría simplemente:​ De esta forma, una vez que tenemos este #define el primer for que mostramos nos quedaría simplemente:​
  
-<​code>​+<​code ​cpp>
 forn(numero,​ valorMaximo) forn(numero,​ valorMaximo)
     // instrucciones     // instrucciones
Línea 640: Línea 741:
 Y el segundo quedaría: Y el segundo quedaría:
  
-<​code>​+<​code ​cpp>
 forn(var, vector.size()) forn(var, vector.size())
     // instrucciones     // instrucciones
Línea 647: Línea 748:
 Vemos que ahora solamente hace falta especificar **una vez** el nombre de la variable, y todo lo demás es copiado automáticamente en forma mecánica por el ''#​define''​. Vemos que ahora solamente hace falta especificar **una vez** el nombre de la variable, y todo lo demás es copiado automáticamente en forma mecánica por el ''#​define''​.
  
-Se puede consultar [[cpp-avanzado:​macros |aquí]] otros ejemplos de macros muy útiles para programación competitiva,​ además del ''​forn''​ ya mostrado.+Se puede consultar [[cpp-avanzado:​macros |aquí]] otros ejemplos de macros ​más avanzadas, ​muy útiles para programación competitiva,​ además del ''​forn''​ ya mostrado.
 ==== Por qué es mejor usar funciones ==== ==== Por qué es mejor usar funciones ====
  
Línea 654: Línea 755:
 Supongamos que queremos tener un fragmento de código para elevar un número al cuadrado. La forma más simple de hacerlo es multiplicar al número con sí mismo, ya que multiplicar es una operación atómica disponible. Podemos entonces pensar en hacer un ''#​define''​ para ello: Supongamos que queremos tener un fragmento de código para elevar un número al cuadrado. La forma más simple de hacerlo es multiplicar al número con sí mismo, ya que multiplicar es una operación atómica disponible. Podemos entonces pensar en hacer un ''#​define''​ para ello:
  
-<​code>​+<​code ​cpp>
 #define cuadrado(x) x*x #define cuadrado(x) x*x
 </​code>​ </​code>​
Línea 660: Línea 761:
 De esta forma, cuando escribamos ''​cuadrado(2)'',​ por ejemplo, se reemplaza por ''​2*2'',​ que es el número al cuadrado como queremos. Sin embargo, el ''#​define''​ que acabamos de definir es muy peligroso de utilizar: imaginemos por ejemplo que lo usamos en la siguiente línea: De esta forma, cuando escribamos ''​cuadrado(2)'',​ por ejemplo, se reemplaza por ''​2*2'',​ que es el número al cuadrado como queremos. Sin embargo, el ''#​define''​ que acabamos de definir es muy peligroso de utilizar: imaginemos por ejemplo que lo usamos en la siguiente línea:
  
-<​code>​+<​code ​cpp>
 cout << cuadrado(2+3) << endl; cout << cuadrado(2+3) << endl;
 </​code>​ </​code>​
Línea 666: Línea 767:
 Esperamos obtener 25... pero la salida de este programa produce 11. ¿Por qué ha ocurrido esto? Esperamos obtener 25... pero la salida de este programa produce 11. ¿Por qué ha ocurrido esto?
  
-El motivo es que, como ya hemos mencionado, los ''#​define''​ realizan un reemplazo **mecánico y textual** de los elementos que les indicamos, igual que si hiciéramos copy paste, sin entender el significado de su contenido. Por lo tanto, como el ''#​define''​ indica que debemos cambiar ''​cuadrado(x)''​ por ''​x*x'',​ tenemos que fijarnos ​quien es ''​x'',​ copiarlo dos veces y colocar un asterisco en el medio, textualmente,​ pues eso es lo que hacen los ''#​define''​. En nuestro ejemplo, ''​x''​ es ''​2+3'',​ pues se ha escrito ''​cuadrado(2+3)'':​ Entonces, el fragmento ''​cuadrado(2+3)''​ es reemplazado por ''​2+3*2+3'',​ ya que el ''#​define''​ reemplaza textualmente cada copia de ''​x''​ por el texto indicado. Esta expresión, al no tener paréntesis,​ da por resultado ''​2+6+3=11'',​ pero nosotros queríamos realizar ''​(2+3)*(2+3)=25''​.+El motivo es que, como ya hemos mencionado, los ''#​define''​ realizan un reemplazo **mecánico y textual** de los elementos que les indicamos, igual que si hiciéramos copy paste, sin entender el significado de su contenido. Por lo tanto, como el ''#​define''​ indica que debemos cambiar ''​cuadrado(x)''​ por ''​x*x'',​ tenemos que fijarnos ​quién ​es ''​x'',​ copiarlo dos veces y colocar un asterisco en el medio, textualmente,​ pues eso es lo que hacen los ''#​define''​. En nuestro ejemplo, ''​x''​ es ''​2+3'',​ pues se ha escrito ''​cuadrado(2+3)'':​ Entonces, el fragmento ''​cuadrado(2+3)''​ es reemplazado por ''​2+3*2+3'',​ ya que el ''#​define''​ reemplaza textualmente cada copia de ''​x''​ por el texto indicado. Esta expresión, al no tener paréntesis,​ da por resultado ''​2+6+3=11'',​ pero nosotros queríamos realizar ''​(2+3)*(2+3)=25''​.
  
 Esto podría resolverse si utilizamos **paréntesis en todos lados**: Esto podría resolverse si utilizamos **paréntesis en todos lados**:
  
-<​code>​+<​code ​cpp>
 #define cuadrado(x) ((x)*(x)) #define cuadrado(x) ((x)*(x))
 </​code>​ </​code>​
  
-Este ''#​define''​ funciona correctamente,​ pero ahora quedó bastante más feo y difícil de leer. Una función como ''​int cuadrado(int x) { return x*x; }'' ​nos hubiera evitado todos estos problemas, pues en las funciones **no se hace un reemplazo textual mecánico del código**, sino que se **calcula el valor indicado antes de comenzar a ejecutar la función**. En el ejemplo con la función cuadrado, se calcularía el valor ''​2+3=5''​ antes de iniciar la función, de forma que cuando se comienza a ejecutar la función cuadrado, ya se tiene ''​x=5''​. En este sentido, **las funciones son más inteligentes que el #define**.+Este ''#​define''​ funciona correctamente,​ pero ahora quedó bastante más feo y difícil de leer. Una función como  
 + 
 +<code cpp> 
 +int cuadrado(int x) 
 +{ 
 +    ​return x*x;  
 +} 
 +</​code>​ 
 + 
 +nos hubiera evitado todos estos problemas, pues en las funciones **no se hace un reemplazo textual mecánico del código**, sino que se **calcula el valor indicado antes de comenzar a ejecutar la función**. En el ejemplo con la función cuadrado, se calcularía el valor ''​2+3=5''​ antes de iniciar la función, de forma que cuando se comienza a ejecutar la función cuadrado, ya se tiene ''​x=5''​. En este sentido, **las funciones son más inteligentes que el #define**.
  
 En resumen, utilizaremos ''#​define''​ solamente cuando nos permite hacer algo que con una función no podríamos hacer. Por ejemplo, escribir algún tipo de ''​for''​ común, o resumir alguna instrucción que declare una variable, es algo que no podríamos reemplazar fácilmente por una función. En cambio, un simple cálculo como elevar al cuadrado sí es algo que podríamos hacer fácilmente con una función, y entonces generalmente conviene hacerlo así para evitar posibles errores, y no tener que llenar todo de paréntesis. En resumen, utilizaremos ''#​define''​ solamente cuando nos permite hacer algo que con una función no podríamos hacer. Por ejemplo, escribir algún tipo de ''​for''​ común, o resumir alguna instrucción que declare una variable, es algo que no podríamos reemplazar fácilmente por una función. En cambio, un simple cálculo como elevar al cuadrado sí es algo que podríamos hacer fácilmente con una función, y entonces generalmente conviene hacerlo así para evitar posibles errores, y no tener que llenar todo de paréntesis.
curso-cpp/modularizacion-funciones.1489956613.txt.gz · Última modificación: 2017/03/19 20:50 por santo