Requisitos:Conocimientos basicos de Lenguaje C, punteros, estructuras de control y de repetecion
Objetivo:Desmenuzar conceptualmente un problema simple, hasta lograr una correcta implementacion.
Problema a resolver:
|
|
Vamos a comprobar si los datos ingresados estan correctamente cargados en la memoria. Mostramos los valores ingresados por teclado |
|
Vamos a realizar la sumatoria de las ventas de las sucursales y vamos a mostrar la sumatoria (esto podria haberse hecho mientras se ingresaban las ventas, pero simplemente vamos a hacerlo ahora por una cuestion de simplicidad). Para ellos vamos a utilizar un variable de tipo acumulador (acumulador = acumulador + valor a acumular o bien acumulador+=valor a acumular), llamado total en donde acumularemos cada elemento del arreglo vtas[]. Para ello tambien utilizaremos otro ciclo o bucle de tipo "para" (for), identico a los anteriores. |
|
Vamos a realizar el promedio de ventas, tomando total y dividiendo por 10. Mostramos el promedio de ventas. Observe que el divisor es 10.0 y no 10. |
|
Con esta ultima version del programa, podemos decir que resolvimos el problema. Como siempre, cabe preguntarnos: ¿Resolvimos correctamente este problema? ¿Que criticas podemos hacerle? ¿Hay codigo repetido? ¿Como podriamos evitarlo? ¿Que cosas podrian cambiar con el tiempo? Este tipo de cuestiones no son faciles de contestar cuando recien comenzamos a programar, no obstante, debemos estar atentos e ir aprendiendo o considerando estas cuestiones a medida que aprendemos. Les propongo ir avanzando en mejorar este codigo de tipo espagueti... Definitivamente no resolvimos correctamente este codigo, es demasiado especifico, muy "a medida" (por ende, muy particular, digamos muy poco generico y eso es muy malo). ¿Que puede cambiar? obviamente el numero de sucursales, ¿Que podemos hacer? Analizar el codigo y ver en que lugares deberiamos cambiarlo en caso de que cambie el numero de sucursales. Utilizar el pre-procesador de C para definir una macro llamada NUMERO_DE_SUCURSALES que ahora, casualmente vale 10, pero que en el futuro podria tomar otro valor:
|
|
Hasta incluso queda mas legible (entendible) el codigo. Observe el calculo del promedio, el cual fue modificado para aprovechar mejor la macro creada. Pruebe de modificar el valor de NUMERO_DE_SUCURSALES, recompilar el codigo, ejecutarlo nuevamente y ver si el mismo reacciona correctamente. Este codigo es mas legible, porque es mas ABSTRACTO, se aleja de la maquina y se acerca al hombre. Debemos hacer codigo "lo mas humano posible", pues ello sera entonces "lo mas entendible y lo mas modificable". Esta podria ser una solucion a la cuestion del cambio de la sucursal, no obstante, requiere recompilar el codigo. Puede solucionarse este problema, incluso, sin recompilar el codigo, entonces, deberiamos informarle de alguna forma al programa aquello que puede "variar", en este caso, el numero de sucursales. Es decir, desde el exterior debemos informar al programa, hay varias formas de hacer eso, una posible y facil es utilizar la linea de comandos, supongamos que el programa se llama suc6, entonces lo ejecutamos haciendo: $ ./suc6 podriamos cambiar eso y hacer que el programa se ejecute de la siguiente manera: $ ./suc6 10 en donde, 10 es el numero de sucursales (en este caso). Deberiamos controlar que el usuario efectivamente lo ejecute de esta forma, avisandole y evitando errores. Esto puede hacerse utilizando el arreglo de apuntadores a char * que el sistema operativo le pasa a la funcion main(), en el primer elemento del arreglo queda el nombre del programa, en el segundo elemento queda el primer valor que le pasamos al programa y asi sucesivamente, en nuestro caso, debemos utilizar el primer valor que le pasamos al programa: |
|
Volviendo a las preguntas anteriores, deberiamos tambien pregutarnos: "Conceptualmente, ¿Que Hace este programa?" Basicamente realiza operaciones (ingresar, mostrar, sumar, promediar) sobre un arreglo de tipo double. Y ¿Que otras operaciones podrian hacerse sobre un arreglo de tipo double? Ordenar (ascendente o descendente), obtener el numero mas grande, obtener el numero mas pequeño, etc.. Entonces, generalizar, abstraer este programa es pensarlo en terminos de una libreria (y pensar en libreria es pensar en reuso de codigo): podria diseñarse una libreria para manejo de arreglos de tipo double, que implemente funciones que hagan las operaciones antes descriptas y este programa seria un cliente o usuario de esta libreria!. Libreria que bien podria ser util en muchos otros problemas aparte de las sucursales.
Si pretendemos hacer estas operaciones sobre un arreglo, arreglo que es externo a la operacion misma, es decir, el arreglo podria existir dentro de main() y alli se puede invocar a la funcion sumar_dbl(), pero el arreglo esta afuera de sumar_dbl(), no podemos declarar un arreglo dentro de sumar_dbl() porque no sabemos a priori sobre que arreglo debemos trabajar ni cuantos elementos tiene. Incluso el arreglo podria ser bien grande con lo cual tampoco podemos pensar en copiar el arreglo dentro de cada funcion para luego hacer la operacion, seria muy ineficiente. Conclusion: la unica forma de implementar estas funciones es a traves de punteros, las funciones deberian recibir -al menos- dos datos desde el exterior: puntero al comienzo del arreglo y cantidad de elementos del arreglo. Entonces podemos ir deduciendo los prototipos de estas funciones. Vamos con las mas faciles. Comenzamos con sumar_dbl(), sumar implica recorrer el arreglo e ir acumulando sus valores para producir un nuevo dato: la sumatoria, por lo tanto, esta funcion podria retornar un double que seria la sumatoria, va a necesitar un puntero al comienzo del arreglo (double *) y la cantidad de elementos a sumar (int), entonces el prototipo seria: double sumar_dbl(double *,int). Idem con el promedio: double promediar_dbl(double *,int). Veamos el codigo de estas dos funciones: |
|
Ambas funciones son muy similares, p es el puntero al comienzo del arreglo, n es la cantidad de elementos; la estructura "para" (for) hace "variar" a i desde 0 hasta n - 1 (dentro del for), terminando i con el valor n (fuera del for), por lo tanto, el for da tantas vueltas como elementos a sumar, por cada vuelta se incrementa en 1 el valor de i y se incrementa el puntero para que apunte al siguiente elemento del arreglo. A medida que se hace eso, voy acumulando en una variable double local (suma) la sumatoria "de lo apuntado por p" (*p). Podemos probar el funcionamiento de estas dos funciones incorporandolo al codigo del programa de la siguiente forma y comparando los resultados que obtenemos: |
|
Ejecute el programa. Los valores de totales y promedios ambos deben dar igual. Una buena estrategia es implementar funciones, probarlas y luego refinarlas. ¿Como pueden refinarse estas funciones? Habiamos observado que el codigo es bastante similar, es mas, es casi redundante... por lo tanto, algo parece andar mal (en terminos conceptuales), tal vez ya se dio cuenta... , lo que esta mal es la funcion promediar_dbl(), pues un promedio es, una abstraccion mayor a una sumatoria, es algo que esta por encima de una sumatoria, es una sumatoria y una division. Y aqui radica la diferencia entre entender y comprender, en terminos de C. Si Ud. realmente comprende, deberia poder expresar la funcion promediar_dbl() en terminos de sumar_dbl() y todo el codigo se reduce a ... A una sola linea de codigo C !! |
|
Si se le ocurrio el codigo de la funcion promediar_dbl() tal como esta aqui, lo felicito!, Ud. comprendio. Si no se le ocurrio el codigo de la funcion promediar_dbl() tal como esta aqui, no se desanime!, verifique nuevamente todo el proceso, relea este documento, piense, analice el codigo, modifiquelo, pruebelo y siga trabajando, esto recien comienza!. |
|
Atte. Lic. Guillermo Cherencio |