Temas:
De hecho, pocas personas son capaces de manejar la complejidad de un procedimiento de más de 200 líneas. Esto se traduce en que a pesar de lograr escribir el procedimiento, nunca se llega a hacerlo a andar correctamente (no se logra eliminar todos los errores de programación) ¿Significa esto que existe un cota para el tamaño de los programas que puede escribir una persona? Afortunadamente no. En la práctica esta cota existe para el tamaño de los procedimientos, pero no los programas. Se pueden escribir programas de millones de líneas, en base a funciones o procedimientos de 20 a 50 líneas.
La clave para poder escribir programas complejos es descomponer el problema a resolver en subproblemas más pequeños y luego resolverlos en forma independiente. Esto significa que hay que elegir uno de estos subproblemas y programar una solución de él abstrayéndose completamente de los detalles sobre como programar una solución para el resto de los subproblemas. Luego, se elige otro subproblema y se resuelve abstrayéndose de los detalles de implementación del primer subproblema u otros subproblemas aún no resueltos. Así, hasta resolver todos los subproblemas.
Diseño:
El proceso de descomposición de un problema en partes más simples se denomina diseño. El diseño comienza con un problema que puede ser complejo de resolver. Su resultado es un conjunto de subproblemas que constituyen las partes del problema. Estas partes se pueden resolver independientemente unas de otras.
Abstracción:
La abstracción es una estrategia de resolución de problemas en la cual el programador se concentra en resolver una parte del problema ignorando completamente los detalles sobre cómo se resuelven el resto de las partes. En este proceso de abstracción se considera que el resto de las partes ya han sido resueltas y por lo tanto pueden servir de apoyo para resolver la parte que recibe la atención.
Una vez hecha la descomposición en subproblemas, resulta natural resolver cada uno de ellos en una función o un procedimiento. Como los subproblemas son más sencillos que el problema original, su complejidad (dada por su tamaño y número de variables) será inferior a la complejidad del mismo problema resuelto por medio de un solo procedimiento.
La abstracción es la estrategia de programación más importante en computación. Sin abstracción las personas serían incapaces de abordar los problemas complejos. La pericia de un programador no está en ser veloz para escribir líneas de programa, si no que en saber descubrir, en el proceso de diseño, cuáles son las partes del problema, y luego resolver cada una de ellas abstrayéndose de las otras.
Un ejemplo de abstracción es el hecho de que uno pueda conducir un automóvil sin ser un mecánico (lo cual probablemente no era cierto con los primeros vehículos). Al conducir, uno se abstrae de cómo funciona la combustión en el motor. Sólo se requiere saber cómo se maneja el volante y los pedales, y cuales son las reglas del tránsito.
Ejemplo:
Supongamos que se necesita un programa que calcule los impuestos que deben pagar un grupo de personas. El impuesto varía de acuerdo al sueldo que percibe cada persona. Para ello se establecen varios tramos de sueldo. Estos tramos se encuentran en un archivo de nombre impuestos.txt. Su contenido podría ser por ejemplo:
10000 30
2000 20
500 10
0 0
Solución:
Este problema lo podemos resolver con un solo procedimiento, pero el programa resultante será menos complejo si descomponemos el problema en 2 subproblemas y luego resolvemos ambos subproblemas en forma independiente.
1er. problema: dados un arreglo con los límites de sueldo de cada tramo, un arreglo que contiene las tasas de impuesto que corresponde pagar en cada tramo y un sueldo, calcular el monto a pagar en impuestos por ese sueldo.
2do. problema: construir dos arreglos con los valores contenidos en el archivo impuestos.txt y luego establecer un diálogo para pedir sucesivamente varios sueldos y responder cuanto se debe pagar por cada uno de ellos, suponiendo que existe una función que calcula el impuesto a partir de los dos arreglos y el sueldo.
La idea es resolver cada uno de estos problemas en una función o un procedimiento independiente. El primero se puede resolver definiendo una función impuestos que reciba como parámetros los dos arreglos y el sueldo, y entregue como resultado el impuesto a pagar. Esta función tendrá la siguiente forma:
Al resolver este subproblema, el programador debe abstraerse
de que los tramos están en un archivo y que hay que realizar un
diálogo con el usuario, porque estos detalles son parte del
2do problema. Tampoco debe preocuparse de cómo se construyen los
arreglos. Ellos son parámetros del problema.
int impuestos(Array tramos, Array tasas, int sueldo) {
... calcular el impuesto en función de tramos, tasas y sueldo ...
return el impuesto calculado
}
El segundo problema es resuelto en el procedimiento run(). En él se lee el archivo, se construyen los arreglos, se dialoga con el usuario y se invoca la función impuestos. La forma del código será entonces:
Al resolver este subproblema, el programador se abstrae acerca del
detalle de cómo se calcula el impuesto, porque esto es parte
del primer subproblema.
void run() {
- tramos= new Array(...);
- tasas= new Array(...);
- Inicializar ambos arreglos con los valores leídos del archivo
impuestos.txt
- Hacer un ciclo de diálogo con el usuario
- Por cada sueldo ingresado por el usuario
se invoca la función impuestos:
monto= impuestos(tramos, tasas, sueldo);
- desplegar monto.
}
El beneficio que trae la abstracción consiste en que resolver estos dos subproblemas en forma independiente resulta menos complejo que resolver el problema original considerando todos sus detalles en forma simultánea.
Conclusión: resulta conveniente descomponer un problema en subproblemas simples de resolver, porque la suma de las complejidades de las partes es menor que la complejidad del todo. Mientras más grande es el programa final, mayor será la diferencia en complejidad.
En el ejemplo anterior, cada uno de los subproblemas se resolvió por medio de una función o procedimiento. Esta es la forma de resolver los problemas en los lenguajes tradicionales (Pascal, C, Visual Basic y otros). En los lenguajes orientados a objetos (Java, C++, Smalltalk y otros) también existe la posibilidad de resolver cada uno de los subproblemas en clases.
Los típicos problemas que Ud. verá en los controles, tareas y ejercicios serán:
Ejemplo: a lo largo del curso se han usado funciones y procedimientos como sqrt, print, max, sin saber cómo están implementadas.
Ejemplo: escribir la función que calcula el máximo valor en un arreglo.
Ejemplo: desplegar los números primos entre 200 y 300.