Está en la página 1de 16

Lenguaje C++

Funciones 5
05.01 Introducción
Inicialmente los lenguajes de programación permitían que la lógica de los programas salte libremente de una a otra parte, a tal punto que
se le llamó programación espagueti, esto complicaba la lógica. Para superar este problema se desarrolló la programación
estructurada, que solo tiene las 3 primitivas lógicas, que acabamos de aprender:
1) Programación secuencial,
2) Estructuras de decisión,
3) Estructuras de repetición.

Se demuestra que aplicando estas 3 primitivas de programación se puede escribir cualquier programa: GENIAL!!! esto simplifica mucho;
pero aún tenemos efectos colaterales para programar en modo profesional:
• Los anidamientos múltiples son complejos,
• Un programa es visto como una gran unidad lógica total: No se agrupa con precisión bloques de instrucciones que ejecutan tareas
específicas,
• Hay bloques de instrucciones que se utilizan más de una vez, por lo que se reescriben; luego cuando hay cambios se debe cambiar
varias veces y se corre un alto riesgo de no hacer todos los cambios.
• Los programas se hacen extensos;
• No se aprovecha el código desarrollado por otros desarrolladores.

Para resolver estas dificultades aplicamos la estrategia divide y vencerás en dos pasos:
• Un programa se divide en funciones, las cuales aprendimos en matemáticas:
fun: Dominio → Rango
Pero ahora se enfatiza en el proceso que ejecuta la función
• Se integran las funciones a través de: entradas → proceso → retorno
C++ nos suministra muchas funciones nativas: printf( ), scanf( ), etc; que están en las librerías que se incluyen con #include<...>.
Los desarrolladores programan muchas funciones, main( ) es una de ellas.
La programacón modular a nivel de programa divide un programa en funciones o módulos.
La programacón modular a nivel de aplicación divide una aplicación en programas o módulos.

Flujo de compilación y ejecución


Un programa se compila de arriba hacia abajo, de izquierda a derecha, antes de usar una variable o función hay que definirla primero,
por eso, por ahora, definiremos las funciones, al inicio del programa.
Un programa inicia su ejecución en la primera instrucción de la main( ) y termina con la última línea de ella; no importa en que lugar esté
escrita. Las funciones se ejecutan de inicio a fin, cuando son llamadas por la main( ) u otras funciones.

05.02 Definición de Función


Ejemplo de una función para sumar dos valores:
int sumar(int m, int n){ // entran valores para m y n
int k = m+n;
return k; // retorna un valor de tipo int
}

Atento: [ xx ] indica que xx es opcional.


Sintaxis: Una función es un bloque de instrucciones para ejecutar una tarea específica y se define así:
retorno nombre entradas

tipo miFun( [tipo1 var1, tipo2 var2, …] ){ // inicio de bloque


… // bloque de instrucciones que se procesan
return exp; // retorna valor del tipo indicado
} // fin de bloque
donde:
nombre : miFun, suele ser un verbo que indica la tarea a ejecutar.
entradas: lista de 0 ó más tipos y variables a las que se les asignará valores de entrada;
a var1, var2, .. se les llama parámetros; si no hay variables se pone void (vacío).
proceso : bloque de instrucciones que ejecutan la tarea.
retorno : tipo de dato a retornar; si no se retorna nada, se pone void (vacío)

Ejecutar (usar, llamar) una función: se la llama por:


[var = ] miFun([exp1, exp2, ...])
donde:

PÁGINA: 1
Lenguaje C++
exp1, exp2, … : son expresiones de tipo1, tipo2, .., cuyos valores (se les llama argumentos) se asignan a var1, var2, ..
var : es una variable del mismo tipo que del retorno; no es obligatorio as

Ejemplo:
int sumar(int m, int n){ // A las variables m y n se les llama parámetros y se les asinga valores de tipo int: 3 y 7, en este caso.
// Los nombres de las variables son mnemónicos
return m+n; // proceso: m+n; retorna un valor de tipo int
}
int main(){
int m=2, k;
k = sumar(m+1, 7); // llama a sumar y le pasa los valores (argumentos): 3 y 7
cout << m+1 << " + " << 7 << " = " << k << endl; // imprime: 3 + 7 = 10
}
Precisiones:
• Se pasan argumentos (valores), la función llamada los recibe y los asigna a los parámetros (variables).
• Los nombres de las variables de la función llamada son independientes de los nombres en la función que llama, por lo tanto
pueden ser diferentes, entreverados, diferentes parcial o totalmente o iguales, se recomienda esto último para conservar la
nemonía.
• El proceso dentro de la función llamada es totalmente independiente del exterior, está desconectado.
• Lo único que conecta a la función que llama con la función llamada son los valores de entrada y retorno.

Firma de una función


Se pueden escribir funciones con nombres y parámetros muy parecidos, para distinguir una de otra utilizamos la firma.
La firma de una función esta compuesta por los componentes mínimos de una función, que la distinguen de otra parecida, al momento
de llamarla. La firma es:
• El nombre de la función
• La lista ordenada de tipos de parámetros y dimensión. Los nombres de las variables ni el tipo de retorno no intervienen.
Ejemplo base de comparación:
int miFun(int var1, float var2, int var3[ ]) {..} // la fima es todo lo que está en rojo.
Error de compilación:
float miFun(int par1, float par2, int par3[ ]) {..} // porque tiene la misma firma.
Otras funciones parecidas con firmas distintas:
int miFun(int var1, float var2, int var3) {..} // var3 no es arreglo
int miFun(int par1, float xar2, int vvar3[ ], float var4) {..} // tiene un tipo más en la lista

Ejemplo:
01 #include <iostream>
02 using namespace std;
03 void fun(void){
04 cout << "Un programa se divide en funciones\n";
05 cout << "integradas en: entradas -> proceso -> retorno\n";
06 // tipo de retorno void (vacío); por lo tanto no hay return
07 }
08 int main( ){ fun(); }
Salida:
Un programa se divide en funciones
integradas en: entradas -> proceso -> retorno

Independencia de las funciones: internamente, una función es independiente (encapsulada en su interior) de las otras funciones:
1) Los nombres de las variables de una función son invisibles fuera de ella; por lo tanto inaccecibles desde fuera.
2) No influye en nada que los nombres de las variables de una función sean iguales o diferentes a los de otras funciones:
float dividir(int m, int n){ // la m y n de dividir( ) es independiente de m y n de la main(), simplemente son tocayas.
return float(m)/n;
}
Muchas veces se usa el mismo nombre para variables en diferentes funciones para mantener la mnemonía, por ejemplo m y n
representan numerador y denominador dentro de varias funciones, pero la m y n de una función no tiene ninguna relación con la m
y n de las otras funciones.
3) Las variables globales son vistas desde todas las funciones, si tienen igual nombre que una local, predomina la local.

PÁGINA: 2
Lenguaje C++
Resumen de comunicación (interacción) con otras funciones
Función llamada Función que llama
Entrada Parámetros: son valores, que se asignan a variables Argumentos: son valores transferidos
Salida Retorno: 0 o 1 valor a transferir Recibe el valor retornado

Control de los tipos de datos de las funciones en un programa


Programa Control: siga el flujo de ejecución del programa: 1, 2, 3, 4, 5 y 6.
Entrada Retorno (salida)
#include <iostream>
using namespace std;
float dividir(int m, int n){ 3: los parámetros m y n son int 4: El retorno es de tipo float
return (float)m/n; 5: se retorna float
}
int main(){
int m=2, n=3; 1: m y n son int
printf("%d / %d = %.2f\n", m, n, dividir(m, n)); 2: los argumentos m y n son int 6: dividir es de tipo float
cout << m << " / " << n << " = " <<
dividir(m, n) << endl;
}

Ejemplo: Convertir grados centígrados a Fahrenheit


int n = 4;
float t = 20, x;
Definición de función Llamadas válidas Llamadas no válidas
float cenAFar(float cen){ x = cenAFar(t); x = cenAFar(); // falta argumento
return cen * 1.8 + 32; x = cenAFar(20); x = cenAFar(3, 4); // demasiados argumentos
} x = cenAFar(10+10);
cenAFar(10+10);
cenAFar(2*n);
cout << cenAFar(10) << endl; // otraFun()
retorna float

Ejemplos de funciones:
Función que no hace nada:
void nada(void){ }
int main(){
nada();
}

Función que hace mucho:


int mucho(int i, float f, char c){
cout << "Se hizo muchísimo\n";
return 3+2;
}
int main(){
int n;
n = mucho(1, 3.4. 'a' );
}

Leer un entero positivo:


int leerPositivo(void) {
int n;
do {
cout << "Ingrese un entero positivo: ");
cin >> n;
} while(n<0);
return n;
}

PÁGINA: 3
Lenguaje C++
Su descripción es:
Componente Descripción
función LeerPositivo: Lee un entero > 0
Entradas ninguna
Salida Retorna el valor leído

Entendiendo a las funciones printf() y scanf()


Sintaxis : int printf("format", exp1, exp2, …);
Descripción: Imprime una lista de expresiones.
Entradas: format: formato para escribir las expresiones
exp1, exp2, … : lista de expresiones a imprimir
Proceso: Imprime en el monitor
Salida: retorna el número de caracteres escritos a la función que llama en el programa.
Ejemplo:
int n, var = 12;
n = printf("\nvar = %d", var); // imprime: var = 12; se imprimen 8 caracteres → n = 8.
printf("\nNúmero caracteres impresos = %d", n);
Salida:
var = 12
Número caracteres impresos = 8

Llamando a printf( ) y scanf( ):


int a;
float b;
scanf( "%d %f", &a, &b); // tipee: 12 5.621<enter>
// Cuando estudie apuntadores aprenderá el motivo de: &a y &b
printf("a = %d, b = %.2f\n", a, b); // compare la sintaxis de las dos funciones
Salida:
a = 12, b = 5.62

05.03 Sobrecarga de Funciones


Hemos establecido la regla:
El número, tipo y orden de los argumentos debe ser igual al número, tipo y orden de parámetros respectivamente.
Cumpliremos la regla; pero podemos definir varias funciones con el mismo nombre, pero diferente firma, por ejemplo:
// área de rectángulo y cuadrado
float area(float largo, float ancho){return largo*ancho;} // definición: área de un rectángulo
float area(float largo) {return largo*largo;} // definición: área de un cuadrado

x = area(2.1, 4); // llama a área de rectángulo
y = area(2.1); // llamada a área de cuadrado

a esto se llama sobrecargar (el nombre de) la función. Al llamar a una función, el compilador determina a cual función llamar según el
procedimiento:
1) Busca la coincidencia exacta entre los tipos y número de valores de argumentos y parámetros; si no, sigue a 2.
2) Se hace la promoción integral de C++ de tipos de datos:
char → int
float → double etc.
se busca la coincidencia; si no, sigue a 3.
3) Se hace la promoción built-in de C++ de asignación a los parámetros:
Si una promoción coincide con dos definiciones ejemplo, si se define:
long area(long n);
double area( double n);
y se llama:
area(2);
la cual coincide con las dos definiciones, se producirá un error debido a la anbiguedad para escoger la función llamada. Si no hay
coincidencia, sigue a 4.
4) Se hace la promoción de tipos definidos por el usuario, esto se verá al definir objetos de clases.

PÁGINA: 4
Lenguaje C++
05.04 Estructura de un Programa Complejo
En la vida real, una actividad compleja suele dividirse en varias actividades más simples, estas en otras más simples y así
sucesivamente forman un árbol. Los programas hacen lo mismo con las funciones (que hacen actividades).

Escritura y compilación
1) Se lee y compila de arriba hacia abajo y de izquierda a derecha.
2) Primero se define una variable o función y luego se utiliza en cualquier parte.
3) El rango de visibilidad de una variable o función inicia en el lugar de su definición y termina al final del bloque que la contiene.

Ejemplo de programa que contiene todas las componentes:


#include<miLibrería> // incluye el archivo miLibrería que contiene funciones a ser usadas
...
#define PI 3.1416 // Define constantes de dos modos, su rango inicia acá y termina al final del programa
const float E = 2.7172;
...
int miGlobal = 4; // define variables globales, cuyo rango de visibilidad es todo el programa
...
tipo fun1(….) { … } // define funciones, su rango inicia acá y termina al final del programa
tipo fun2(….) { … }

int main(){… }

fun1, fun2, … se llaman entre sí, y suelen formar una red como:

El diagrama anterior facilita la compresión de la lógica a los desarrolladores; pero al momento de programar, las funciones se deben
definir de abajo para arriba, para que no hayan errores de compilación:
#include<iostream>
int fun12(...){...}
float funAB(.. ){..... }
char fun21(.. ){...}
void fun1(.. ){...}
int fun2(.. ){...}
int main(){
….
}
Esto dificulta la comprensión del programa; para facilitar se introduce el prototipo de función: firma + tipo de retorno al inicio del
programa:
#include<iostream>
void fun1(int, float, ... ); // prototipo de fun1 : observe el final con ;
int fun2(... ); // prototipo de fun2 : observe el final con ;
int fun12(... ); // prototipo de fun12 : observe el final con ;
float funAB(.. ); // prototipo de funAB : observe el final con ;
char fun21(... ); // prototipo de fun21 : observe el final con ;
int main(){

fun2(... ); // llamando a fun2()

}
void fun1(int n, float x, …) {…} // define a fun1()
int fun2(.. ){…} // define a fun2()
int fun12(.. ){…} // define a fun12()
float funAB(.. ){..... } // define a funAB()
char fun21(.. ){...} // define a fun21()

PÁGINA: 5
Lenguaje C++
Los prototipos pueden incluir los nombres de variables, como referencia para facilitar el entendimiento de la función.
Para la función: int miFun(float x , int m ) {…}
prototipo: int miFun(float [x], int [m]); // [ ] quiere decir opcional
El prototipo se puede escribir de diferente modos:
int miFun(float , int ); // describe los tipos de datos, requisito necesario y suficiente
int miFun(float x, int );
int miFun(float , int m);
int miFun(float a, int b ); // los parámetros a y b se usan para facilitar la descripción de la función
Definición de función Prototipos válidos Prototipos no válidos
int miFun(float x, int m){…} int miFun(float x, int m); // el más usado float miFun(float y, int n);
int miFun(float m, int x); int miFun(int y, int n);
int miFun(float, int); int miFun(int, float);

Finalmente, la correspondencia de tipos de datos dentro de un programa será:


Programa Entradas Salida
Los números 1, 2, 3a, 3b, 4a, 4b y 5 indican el flujo de ejecución
#include<iostream>
using namespace std;
float cenAFar(float cen); // prototipo 3a: el parámetro cen es tipo float 4a: retorno es float
int main(){
float t = 36; 1: t es float
cout << "Grados Fahrenheit = " << cenAFar(t) 2: el argumento t= 36 es float 5: El retorno es float = cenAFar(t)
<< endl; // se imprime con formato %.2f
} 3b: el parámetro cen es float 4b: retorno es float
float cenAFar(float cen){
return cen * 1.8 + 32;
}

Ejecución de un programa
• Un programa inicia su proceso en la main(), sin importar donde esté ubicada: al inicio, al final o al medio.
• El programa termina al finalizar el bloque de la main().
• En tiempo de ejecución: las variables de la main() son alojadas en la Random Acces Memory (RAM: memoria de acceso aleatorio) y
se desalojan al terminar la ejecución; las variables de las otras funciones son alojadas cada vea que son llamadas y se desalojan
inmediatamente al terminar su ejecución, para ahorrar memoria. Por lo tanto la main() se aloja una sola vez, las otras funciones se
pueden alojar y desalojar varias veces, son volátiles.
• Para las funciones recursivas: cada vez que se ejecuta una recursión se aloja memoria para la misma y se forma una pila de
memorias. Cuando termina la ejecución, se desalojan, una por una, las memorias que se apilaron.

Paso de valores al llamar una función y retornar:


Sea el programa:
01 int miFun(int vs, int arrx1[ ], int exp1){
02 vs = 10;
03 arrx[0] = 20;
04 arrx[1] = 30;
05 exp1 = 50+exp1;
06 return 60;
07 }
08 int main(){
09 int vs = 1; // variable simple
10 int arr[3] = {2, 3, 4}; // arreglo
12 ret = miFun(vs, arr, arr[0]); // Tipos de argumento: variable simple, arreglo y expresión: a, b, y c
13 miFun(1, arr, 2); // miFun() retorna 60; pero no es utilizado
14 }

PÁGINA: 6
Lenguaje C++
Paso de argumentos desde una función que llama -ejemplo la main()- a otra función
Tipo de argumento 1) Llamando en main() 2) Función llamada: miFun() 3) Al retornar a main()
a) un solo valor Se pasa el valor 1 de vs, NO la variable vs Recibe el valor 1 en vs1 vs ≡ 1, no cambia su valor
b) arreglo Se pasa la dirección inicial del arreglo, NO Recibe los valores {2, 3, 4} en Los valores del arreglo cambian
el arreglo; pero con esa dirección inicial se arr1 arr = {20, 30, 4}
opera en todo el arreglo: {2, 3, 4}
c) Expresión Se pasa el valor 5=1+4 de la expresión, Recibe el valor 5 en var1 Se pierde el valor de la expresión
NO la expresión vs+4
Retorno En la línea 12: res ≡ 60,
En la línea 13: no se usa el retorno

Ejemplo:
// 05_01: Paso de valores a la función mitad()
#include<iostream>
using namespace std;
void mitad(int a, int b[ ], int c){ // a recibe 2, b recibe {4, 6, 8}, c recibe 4
a /= 2;
b[0] /= 2; b[1] /= 2; b[2] /= 2;
c /= 2;
cout << "2) " << a <<", " << b[0] << “, “ << b[1] << ", " << b[2] << ", " << c << endl;
} // se liberan a, b y c y se pierden sus valores
int main(){
int a=2, b[ ]={4, 6, 8};
cout << " a b c\n";
cout << "2) " << a <<", " << b[0] << “, “ << b[1] << ", " << b[2] << ", " << a+2 << endl;

mitad(a, b, a+2); // se pasa el valor de a; la dirección de b; y a+2=4.


cout << "2) " << a <<", " << b[0] << “, “ << b[1] << ", " << b[2] << endl;
}
Salida:
a b c
1) 2 4 6 8 4
2) 1 2 3 4 2
3) 2 2 3 4 // No cambió el valor de a; los valores de b están a la mitad; No existe c.

ATENCION, gran cambio de mentalidad: inicialmente, el uso de funciones puede parecer artificioso, trabajoso y un poco confuso; sin
embargo nos da orden, independencia, divide el trabajo, facilita el mantenimiento, parametrización (una función se ejecuta para cualquier
valor de los argumentos), y en muchos casos simplifica la lógica dentro de las funciones. De acá en adelante usaremos funciones casi
siempre.
Nota: Se muestra el flujo de programación con números 1) 2) ...
// 05_02.c : Leer dos números m. n > 0 y divídir m/n
#include<iostream>
using namespace std;
// prototipos
int leerNumero(void); // 4) prototipo
float dividir(int m, int n); // 4) prototipo

// funciones
int main(){
int m, n; // 1) Definir variables
m = leerNumero(); // 2) llama a leerNumero: no envía valor, recibe valor int
n = leerNumero(); // 2) llama a leerNumero
cout << "m = " << m << ", " << n = " << n << "m/n = " << dividir(m, n) << endl;
}
int leerNumero(void){ // 3) Definir función: Entrada: no hay; salida: valor int
int n=0;
do {
cout << "Ingrese un entero positivo: ";
cin >> n;
} while(n<=0);

PÁGINA: 7
Lenguaje C++
return n;
}
float dividir(int m, int n){ // 3) Definir función: Entrada, salida
return m/(float)n;
}
Salida:
Ingrese un número positivo: 3
Ingrese un número positivo: 2
m = 3; n = 2; m/n = 1.50

2 Ejemplos del cambio de paradigma de programación a funciones

Ejemplo 1: Lea un entero m >= 2, imprima los primos menores o iguales a m.


Vamos a programar el mismo algoritmo de números primos en 2 contextos. Observe cuidadosamente los cambios:
//Reportar todos los números primos <= m (leído)
//05_03a.c: Sin usar funciones //05_03b.c: Utilizando funciones
#include<iostream> #include<iostream>
using namespace std; using namespace std;
int main(){ int primo(int n);
int m, n, primo, i; int main(){
cout << "Ingrese un número > 1: "; int m, n;
cout >> m; cout << "Ingrese un número > 1: ";
for(n = 2; n<= m; n++){ cin >> m;
primo = 1; // algoritmo para número primo for(n = 2; n<= m; n++)
i = 2;
while(i*i <= n && primo) if(n%i++==0) primo=0;
if(primo) cout << n << " es primo\n";
else cout << n << " no es primo\n"; if(primo(n)) cout << n << " es primo\n";
} else cout << n << " no es primo\n";
} }
int primo(int n){ // algoritmo primo
int i=2; // algoritmo para número primo
while(i*i <= n) if(n%i++==0) return 0;
return 1;
}
El programa es más complejo: Contiene la lógica general y El programa es más simple: Contiene la lógica general y
los algoritmos llama a las funciones (algoritmos)
El algoritmo no es reusable: Definido dentro del programa Una función es reusable: puede ser llamada de otras
general, no puede ser llamado de otras partes. partes.
La comunicación parece más simple La comunicación parece más compleja

Ejemplo 2: El programa para ejecutar las 4 operaciones aritméticas ya fue presentado, a diferentes niveles, en los tres capítulos
anteriores, ahora vamos a completarlo profesionalmente. Escriba un programa que lea dos enteros m > 0 y n > 0, y presente el menú:
Operación que requiere:
1) Sumar: m + n
2) Restar: m – n
3) Multiplicar: m * n
4) Dividir: m/n
5) Salir
Elija su opción: _
Ejecute la opción seleccionada, repita el proceso y salga (termine) con la opción 5.

Requerimientos:
Valide que la opción esté entre 1 y 5 // Use do.
Repita la operación completa hasta que se elija 5. // Use do.
Use funciones // prototipo, definición y llamar
Para ejecutar la operación seleccionada, use switch.

Análisis (Qué hacer): ya lo sabemos


Matriz de casos de prueba: ya la sabemos

PÁGINA: 8
Lenguaje C++

Diseño (Cómo hacer):


Diagrama de funciones
main

leerEntero operaciones

menu suma ...


Definición de funciones
main( ): Ejecuta el control general del programa.
leerEntero( ): lee un número entero y retorna el valor leído.
operaciones( ): controla el flujo de ejecución repetitiva de las 4 operaciones.
menu(): despliega el menú y retorna la opción seleccionada.
suma(): recibe 2 números, los suma e imprime.
Programa:
// 05_04.c : 4 operaciones
#include<iostream>
using namespace std;
// prototipos
int leerEntero(void);
void operaciones(int m, int n);
int menu(void);
void suma(int m, int n);
void resta(int m, int n);
void multiplicacion(int m, int n);
void division(int m, int n);

int main(){ // main() solo ejecuta funciones de control


int m, n;
m = leerEntero();
n = leerEntero();
operaciones(m, n);
}

int leerEntero(void){
int m;
cout << "Ingrese un entero > 0: ";
cin >> m;
return m;
}
void operaciones(int m, int n){
int op; // op = opción
do { // Repetición de operación completa hasta que se elija 5.
op = menu();
switch(op){ // Ejecute de operación seleccionada.
case 1: suma(m,n); break;
case 2: resta(m,n); break;
case 3: multiplicacion(m,n); break;
case 4: division(m,n); break;
default: cout << "Gracias por su visita\n";
}
} while(op!=5);
}
int menu(void){
int op;
cout << "\nOperación que requiere:\n");
cout << "1) Sumar: m + n\n";
cout << "2) Restar: m – n\n";
cout << "3) Multiplicar: m * n\n";
cout << "4) Dividir: m/n\n";
cout << "5) Salir:\n";
PÁGINA: 9
Lenguaje C++
do { // Valida la opción entre 1 y 5
cout << "Elija su opción: "; cin >> op;
} while (op<1 || op > 5);
return op;
}
void suma(int m, int n) {cout << "suma = " << m+n << endl;}
void resta(int m, int n) {cout << "resta = " << m-n << endl;}
void multiplicacion(int m, int n) {cout << "multiplicación = " << m*n << endl;}
void division(int m, int n) {
if(n==0) cout << "No se puede dividir, divisor = 0";
else cout << "división = " << (float)m/n << endl;
}

05.05 Paso de Argumentos de Tipo Función


Esta capacidad da mayor flexibilidad a la parametrización, ejemplo:
// 05_05.c : Paso de argumento de tipo función
#include<cstdio>
using namespace std;
void suma (int m, int n);
void resta(int m, int n);
void por (int m, int n);
void div (int m, int n);
void operaciones(int m, int n, void f(int m, int n));
int main(){
int m= 3, n =2;
operaciones(m, n, suma); // Se pasa una función como argumento
operaciones(m, n, resta);
operaciones(m, n, por);
operaciones(m, n, div);
}
void operaciones(int m, int n, void f(int m, int n)){f(m, n);}
void suma (int m, int n) {cout << m << " + " << n << " = " << m+n << endl;}
void resta (int m, int n) {cout << m << " - " << n << " = " << m-n << endl;}
void resta (int m, int n) {cout << m << " * " << n << " = " << m*n << endl;}
void div (int m, int n) {
if(n==0) cout << "No se puede dividir entre 0\n";
else if(m%n==0) cout << m << " / “ << n << “ = “ << m/n << endl;
else cout << m << " / “ << n << “ = “ << (float)m/n << endl;
}
Salida:
3+2=5
3-2=1
3*2=6
3 / 2 = 1.500000

05.06 Función en Línea


En tiempo del ejecución, al llamar a una función se consume: memoria RAM, para cargar las funciones; y tiempo: cargándo, pasando
argumentos recibiendo el resultado y descargandola de la RAM; si la función es pequeña y es llamada muchas veces, estos consumos
pueden ser significativos; para mejorar el desempeño se define una función en línea:
inline tipo miFun(…); // prototipo

n = miFun(4); // llamada
...
inline tipo miFun(…) {….} // definición
en este caso miFun() no es procesada como función: en tiempo de compilación, el código de la función es desplegado dentro de la
función que llama. La petición inline es un requerimiento NO un comando, el compilador analiza el caso y decide finalmente si la hace
en línea o no; no la definirá en línea si el código:
• Contiene un bucle, switch o goto, para funciones que retornan valor.
• Contiene variables static.
• Es función recursiva.
PÁGINA: 10
Lenguaje C++
Ejemplo:
inline double cuarta( double a) {return a*a*a*a;} // eleva a la cuarta.

05.07 Función Recursiva


Un programa puede contener varias funciones, las cuales suelen tener una estructura casi jerárquica:

Se podría tener una red muy grande y compleja de funciones que formarían bucles (loops), lo cual sería muy complejo de resolver en
tiempo de ejecución, por lo que debe evitarse, en lo posible. También se puede tener funciones que que se llaman a sí mismas
(recursivas), por ejemplo fun21() es llamada por fun2() y también se auto-llama. Ejemplo:
// 05_06.c : Calcular la suma de los n primeros números enteros de tres formas distintas
#include<iostream>
using namespace std;
Fórmula Repetición Recursividad
int sumar(int n){ int sumar(int n){
suma = 0; if(n==1) return 1; // fin de función, no se auto-llama
for(i=1; i<n; i++) suma = += i; return n + sumar(n-1);
return suma; }
}
int main(){ int main(){
int main(){
int n = 5; int n = 5, suma;
int n = 5;
suma = n*(n+1)/2; suma = sumar(5);
suma = sumar(n);
cout << "La suma de los " << n << " primeros enteros es: " << suma << endl;
}
Salida:
La suma de los 5 primeros enteros es: 15

La función sumar() se ejecuta en dos tiempos:


1 2
n sumar(n) Cierre de sumar()
5 5 + sumar(4) 5 + 10 = 15
4 4 + sumar(3) 4 + 6 = 10
3 3 + sumar(2) 3+3=6
2 2 + sumar(1) 2+1=3
1 1 1

Estrategia para programar funciones recursivas


Observe la función recursiva sumar() anterior:
int sumar(int n){
if(n == 1) return 1; // 1) Poner la condiciones de fin de recursividad al inicio
// La función debe terminar en algún momento: No auto-llamarse más,
// e iniciar el proceso de cierre de auto-llamadas.
return n + sumar(n-1); // 2) Poner las autollamadas al final
}

Una función recursiva sigue el formato general:


... fRec(...){
if(...) return ...; // 1) al inicio debe haber 1 o más if() para finalizar la recursividad
...
... fRec(...); // 2) Poner 1 o más autollamadas al final
PÁGINA: 11
Lenguaje C++
}

/* 05_07.c : Programar dos funciones recursivas con estructuras similares:


1) Hallar el máximo común divisor de dos enteros positivos m y n utilizando el método de Euclides.
2) Hallar el factorial de m.
*/
#include<iostream>
using namespace std;
int mcd(int m, int n);
int fac(int m);
int main(){
int m=22, n=4;
cout << "Ingrese dos números enteros mayores que cero: "; cin >> m >> n;
cout << "El factorial de " << m << " es: " fac(m) << endl; // atento si m es grande se produce un overflow de fac()
cout << "El mcd de " << m << " y " << n << " es " << mcd(m, n) << endl;
}
int mcd(int m, int n){
if(m%n==0) return n; // sale de la función
mcd(n,m%n);
}
int fac(int m){
if(m==1) return 1;
return m*fac(m-1);
}

Observe que la estructura de las dos funciones es similar, ¿Por qué una función recursiva fac( ) tiene return y mcd( ) no?
Sugerencia:
1) Compare las definiciones de mcd y factorial.
2) Compare los ciclos recursivos:
m n r mcd(m, n) Cierre mcd() n fact(n) Cierre de Fact()
22 4 2 4 4 * fact(3) 4* 6= 24
4 2 0 3 3 * fact(2) 3 * 2 = 6
4 2 0 2 2 2 2 *fact(1) 2 * 1 = 2
1 1 1

Cuándo utilizar funciones recursivas:


Cada vez que se auto-llama una función recursiva se ocupa espacio adicional en la memoria RAM para ella, y se consume tiempo para
alojar los espacios; por eso hay usarla solo cuando sea imprescindible. La recursividad es una forma particular de repetición, en dos
modalidades:
1) Implanta las repeticiones: do, while, for(), ejemplo: sumar los n primeros números enteros:
Suma(1, …, n) = n + Suma(1, …, n-1)
= n + (n-1) + ... + 2 + 1
No se recomienda usar recursividad.
2) La recursividad puede cambiar de sentido en cada repetición, “driblear y hacer zigzag” como el juego del futbolista Lionel Messi,
ejemplo: hallar el Máximo Común Divisor (MCD) de dos entero utilizando el algoritmo de Euclides:
int MCD(int m, int n){
if(m%n==0) return n;
return MCD(n, m%n); // el segundo parámetro se cambia al primer lugar
}
int main(){ cout << MCD(32, 24) << end;}
Este es el principal uso de las funciones recursivas, en muchos casos resuelven problemas de repetición muy complejos en modo
muy sencillo; aunque consuman tiempo y memoria RAM, ejemplo:

PÁGINA: 12
Lenguaje C++
Torres de Hanoi, es un juego que consiste en tres varillas verticales: A, B y C:
A B C

en A están apiladas un número de discos, de diámetros diferentes, ordenados de mayor a menor (el de mayor diámetro abajo). B y C
están vacías. El juego consiste en pasar todos los discos de la varilla A a C, la varilla B es auxiliar.
Reglas:
Sólo se puede mover un disco cada vez.
Un disco de mayor tamaño no se puede colocar encima de uno más pequeño.
Sólo se puede mover el disco que se encuentre en la parte superior de cada varilla.
Escriba un programa que permita ingresar el número de discos y muestre todos los movimientos que se deben realizar; tome los casos
de prueba:
N=1 N=2 N=3
A->C A->B A->C
A->C A->B
B->C C->B
A->C
B->A
B->C
A->C
Notas:
1) La acción “mover disco de A a B”, en el mundo real, es ejecuta en el programa (mundo virtual) por printf(“...”, ‘A’, ’B’): A->B.
Como puede ver, ya estamos cerca de la robótica.
2) No se asuste con este problema, se resuelve muy rápidamente con una función recursiva, siguiendo un algoritmo, tal y
como se hace para encontrar el máximo común divisor de dos números siguiendo el algoritmo de Euclides.
3) En un problema complejo, el programador debe seguir las especificaciones (algoritmo) del diseñador; aunque no entienda la
lógica de la solución (Es una regla para trabajar en equipo); pero es recomendable que la entienda. Para validar el programa
debe usar la matriz de casos de prueba (en este caso para N=1, 2 y 3).

Diseño en 3 pasos:
com='A', aux='B', fin='C';
1) mueve n-1 discos de com a aux utilizando fin como si fuera aux
2) mueve el último disco de com a fin
3) mueve n-1 discos de aux a fin utilizando com como si fuera aux

Programa:
void hanoi(int n, int com, int aux, int fin){
if(n==1) {printf("%c→%c\n", com, fin); return;} // paso 2
hanoi(n-1, com, fin, aux); // paso 1
printf("%c→%c\n" , com, fin); // paso 2
hanoi(n-1, aux, com, fin); // Paso 3
}
int main(){
int N=..;
char com='A', aux='B', fin='C';
hanoi(N, com, aux, fin); // función recursiva que mueve todos los discos
}
En internet se presenta esta solución, inclusive se muestran videos de la dinámica de los movimientos de los discos.
Comparación de tipos de función
Aspecto Función recursiva Funcion no Recursiva
Dificultad mediana compleja
Velocidad veloz Suelen demorarse
Consuma de memoria Muy bajo Alto
Complejidad mediana Alta
Procesos repetivios “lineales” Puede contener procesos repetitivos. Es equivalente a muchos procesos repetivos ”lineales”,
no a todos;
Procesos repetivios complejos No Ejecuta procesos de tipo racimos, fractales.
que se entremezclan Con lógica sencilla; pero se demoran y requieren
No programables con do{}, while() bastante memoria
ni for()
PÁGINA: 13
Lenguaje C++

05.09 Aplicaciones de las funciones


Ambiente No recursivas Recursivas
1 Programa Dividir (estructurar) un programa en módulos para simplificarlo.
2 Algoritmos Usarlo dentro de anidamientos de selección y/o repetición complejos para simplificarlos.
3 Matemáticas Implantar las funciones matemáticas en computación.
4 Procesos Ejecutar tareas
5 Gráficos Gráficos “regulares”, ejemplo: líneas paralelas, Gráficos que tienen forma de racimos, de fractales, se
volúmenes, áreas, volúmenes de contorno sencillo, etc entretuercen, etc.
6 Movimientos Funciones con parámetro tiempo. Movimientos no lineales, ejemplo: Torres de Hanoi
7 Otros

Confirma tu poder:
/* 05_08.c : Verificar si un número n es primo o perfecto (n es perfecto,
si n = suma de sus divisores, ejemplo: 6 = 1 + 2 + 3; 28 también lo es)
*/
#include<iostream>
using namespace std;
int primo(int n){
int i=2;
while(i*i <= n) if(n%i++==0) return 0;
return 1;
}

int perfecto(int n){


int i, suma = 1;
for (i=2;i<=n/2; i++) if(n%i==0) suma += i;
if(suma==n) return 1;
return 0;
}

int main(){
int n, i=2;
cout << "Ingrese un entero > 0: ";
cin >> n;

if(primo(n)) cout << n << " es primo\n";


else cout << n << " no es primo\n";
if(perfecto(n)) cout << n << " es perfecto\n";
else cout << n << " no es perfecto\n"
}

Matriz de Pruebas
Descripción Entrada Salida Chequeo
1 Valor inicial 1 1 es primo 1 es perfecto √
2 Valor inicial 2 2 es primo 2 no es perfecto √
3 Valor medio 3 3 es primo 3 no es perfecto √
4 Valor medio 5 5 es primo 5 no es perfecto √
5 Valor significativo 6 6 no es primo 6 es perfecto √
6 Valor significativo 28 28 no es primo 28 es perfecto √
7 Valor mayor 29 29 es primo 29 no es perfecto √
8 Valor mayor 30 30 no es primo 30 no es perfecto √

PÁGINA: 14
Lenguaje C++
¡Perfecto, Admírate!:
¡Qué fácil! : Fíjate en los return de las funciones.
¡Qué óptimo!: Fíjate el uso de las repeticiones:
Para primo() : while(i<n && i*i <= n) if(n%i++==0) return 0;
Para perfecto(): for (i=2;i<=n/2; i++) if(n%i==0) suma += i;
Aunque la máquina es rapídisima y no protesta; en ambos casos, no hicimos ni un cálculo demás.
¡Qué claro! : Fíjate en la main(), solo ordena; y las funciones solo ejecutan, y no hay chismes: Lo que está en la main(), se queda
en la main; lo que está en una función se queda en la función; solo se comunica lo indispensable.
¡Qué ingenioso!: Fíjate como se manejaron las estructuras repetitivas. Pronto serás diestro en eso.
¡Qué bien probado!: mmm; eso no es verdad; está verificado para 8 casos; pero eso no significa que esté probado para todos los
casos; pero se verificaron los casos críticos y eso es un buen indicador.

Las reglas aprendidas en este mundo virtual, también se aplican en el mundo real, por ejemplo, haga la sinonimia:
Mundo Virtual Mundo Real
Funciones Personas
Programas, sistemas Actividades, tareas

Ya conoce con precisión el fundamento, para dividir y vencer en el mundo virtual; lo mismo funciona en el mundo real, además debe
añadir el sentimiento y la acción de las personas.

Ubícate: Ya no eres un minino en computación:

Fuente: https://www.google.com.pe/url

¡Felicitaciones! Ya no dices miau y se te ve muy bien.


A tu pinta se le suele dar más valor del que debiera, y lo sabes bien;
por eso no se te suben los humos,
ni aunque en el espejo apareciera Einstein: Tú eres tú.

Atent@: Tu mamá le contará a todo el mundo: ¡Mi niñ@ es tod@ un@ profesional de la computación!
Espero que tengas a la mamá; ella tiene algo de razón. Hemos avanzado bastante en la teoría, luego manejaremos tipos de datos más
complejos y útiles.

05.09 Preguntas de Teoría


1) ¿Qué es una función y cuál es su sintaxis?
2) ¿Qué se pasa como argumentos al llamar a una función, y en qué los aloja al recibirlos?
3) ¿Cómo contribuyen las funciones a simplificar y a gestionar un programa complejo?, ¿qué dificultad genera el uso de funciones?
4) ¿Qué hacer para superar la dificultad de que una función solo puede retornar 0 o 1 valor?
5) Esbozar un programa que tiene una función, con y sin prototipo.
6) ¿Cuál es el flujo de un programa en tiempo de compilación y de ejecución?
7) ¿Cómo se carga en la RAM la main() y las otras funciones en tiempo de ejecución?
8) Al recibir y al retornar valores de una función se debe mantener el mismo tipo de los valores, ¿cuál es el flujo que garantiza dicho
mantenimiento?
9) En el código:
int m=2, mm[] = {1, 2, 3}, n;
n = miFun(m, mm);

int miFun(int m, int mm[ ]) {return 2+2;}
¿qué se pasa a miFun() en el primer y segundo argumento; y que retorna?
10) En tiempo de ejecución, ¿Cuántas veces se carga en la RAM una función recursiva, y cuántas veces la main()?

05.10 Ejercicios de Programación


Escriba, compile y ejecute los programas.

1) Ingresar un entero positivo, imprima todos los primos menores o iguales al mismo, utilice funciones.

PÁGINA: 15
Lenguaje C++

2) Escriba 4 funciones, cuyos prototipos son:


int suma(int n, int m); // retorna n + m
int resta(int n, int m), // retorna n - m
int multiplica(int n, int m), // retorna n * m
float divide(int n, int m), // retorna n / m si m no es cero
Desde main(), llámelas e imprima los resultados.

3) Responda a las preguntas y argumente su respuesta:


a. ¿Puede una función llamar a main()? // Sí
b. ¿Es posible que una función a() llame a una función b(), esta a una función c(); y esta llame nuevamente a a()? // Sí
c. ¿Es posible que una función se llame a si misma? // Sí

4) Calcule el factorial de n fac(n) utilizando una función recursiva; pruebe con fac(4) y fac(40).

5) Utilice una función recursiva para calcular la función de Ackermann A(m,n), donde m >=0, n>=0 y:
n +1, Si m = 0;
A(m, n) = A(m-1,1), Si m > 0 y n = 0;
A(m-1, A(m, n-1)), Si m > 0 y n > 0

6) Utilice una función recursiva para hallar el mínimo de un arreglo.

PÁGINA: 16

También podría gustarte