Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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.
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.
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
Ejemplos de funciones:
Función que no hace nada:
void nada(void){ }
int main(){
nada();
}
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
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.
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);
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.
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;
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
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.
PÁGINA: 8
Lenguaje C++
leerEntero operaciones
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;
}
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
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
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++
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 main(){
int n, i=2;
cout << "Ingrese un entero > 0: ";
cin >> 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.
Fuente: https://www.google.com.pe/url
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.
1) Ingresar un entero positivo, imprima todos los primos menores o iguales al mismo, utilice funciones.
PÁGINA: 15
Lenguaje C++
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
PÁGINA: 16