Logo Studenta

UDIMA _ Grado en Ingeniería de Telecomunicaciones _ Fundamentos de programación _ Temario_Ac

Esta es una vista previa del archivo. Inicie sesión para ver el archivo original

Unidades.rar 
Unidades/Manual/T1/UNIDAD_1_MPROG_c.pdf
www.udima.es 1 – 1 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
PRESENTACIÓN Y OBJETIVOS DE LA UNIDAD 
 1. Concepto de metodología de programación 
 2. La metodología de desarrollo de programas por diseño descendente ("top-down") 
 2.1. Diseño descendente ("top-down"): descomposición de tareas o subtareas 
 2.1.1. Descripción 
 2.1.2. Descomposición de tareas en subtareas: ejemplos 
 2.1.2.1. Ejemplo con un nivel de descomposición 
 2.1.2.2. Ejemplo con varios niveles de descomposición 
 3. Principios y propiedades generales de un diseño descedente 
 3.1. Modularidad de un programa 
 3.2. Acoplamiento entre módulos 
 3.3. Número de aspectos a considerar en cada momento 
 3.4. Cohesión interna de un módulo 
 3.5. Complejidad de un módulo 
 3.5.1. La cantidad de información que recibe 
 3.5.2. Tamaño del módulo 
 3.6. Otros principios 
 3.6.1. Retrasar decisiones 
 3.6.2. Consistencia de las soluciones 
 3.6.3. Localización 
 4. Características básicas de "buen estilo" en programación 
 4.1. Indentación 
 4.2. Nombres de identificadores 
 4.3. Comentarios 
1 
 
UNIDAD 
DIDÁCTICA 
 
METODOLOGÍA BÁSICA DE 
DESARROLLO DE PROGRAMAS 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 2 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
 5. La recursión 
 5.1. Concepto de recursión y función recursiva 
 5.2. Ejemplo de función factorial: implementación iterativa y recursiva 
 5.3. Recursividad bien construida. Funciones parciales y totales 
 5.4. Tipos de recursión: recursión final y no final 
 5.5. Cuándo no utilizar la recursión 
CONCEPTOS BÁSICOS A RETENER 
ACTIVIDADES DE AUTOCOMPROBACIÓN 
EJERCICIOS VOLUNTARIOS 
REFERENCIAS BIBLIOGRÁFICAS 
 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 3 
J. Crespo del Arco Metodología básica de desarrollo de programas 
 
 
 
Presentación 
 
Esta Unidad didáctica se centra en el estudio de una metodología básica para el desarrollo de pro-
gramas. Tras considerar el concepto de metodología de programación, se presenta la metodología es-
tructurada de diseño descendente (top-down) como básica y fundamental. Siguiendo los conocimientos 
previos del alumno se utilizan ejemplos del lenguaje C++ (más concretamente, de C++ como lenguaje 
procedimental), del cual se presuponen en el material de la Unidad didáctica unos conocimientos bási-
cos. No se introducen en esta Unidad didáctica aspectos de la programación orientada a objetos (la 
cual será extensamente tratada en Unidades didácticas subsiguientes). 
 
Se destaca el aspecto fundamental de descomposición de tareas en subtareas de la metodología 
del diseño descendente, y se introduce la metodología mediante unos casos sencillos que permiten 
establecer ciertos principios básicos. En dicha descomposición de tareas en subtareas, los subprogra-
mas (funciones y procedimientos) surgen como los elementos naturales constitutivos de un programa. 
Seguidamente, se estudian los principios generales que deben regir la descomposición de tareas propia 
de la metodología. En la presente Unidad didáctica se tratan, asimismo, las características principales 
de lo que se considera un "buen estilo" a la hora de programar para que el código sea legible y claro, 
como son que se incluyan comentarios adecuados, que se utilicen nombres descriptivos en identifica-
dores, y que el número de líneas de código de las funciones y procedimientos no sea excesiva. 
 
La recursión se introduce como una potente herramienta de programación, y se estudian las dife-
rencias entre la iteración y la recursión, así como las ventajas y desventajas de ambas a la hora de 
abordar la codificación de una función o procedimiento. 
 
 
Objetivos 
 
El alumno conocerá qué es una metodología de desarrollo de programas y será capaz de utilizar la 
metodología de diseño descendente (top-down) para resolver problemas de programación empleando 
una descomposición de tareas en subtareas, la cual deberá seguir ciertos principios generales. Sabrá, 
asimismo, las características de "buen estilo" que debe tener un código. Conocerá la potente herra-
mienta de la recursión y las diferencias cualitativas que ésta presenta frente a la iteración. 
 
 
 PRESENTACIÓN Y OBJETIVOS DE LA UNIDAD 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 4 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
 
1. CONCEPTO DE METODOLOGÍA DE PROGRAMACIÓN 
 
En la resolución de problemas de programación no triviales, se precisa en general una manera sis-
temática de abordar el problema. Esta manera sistemática constituye una metodología de desarrollo de 
programas. No debemos pensar que los computadores resuelven problemas per se, sino que los com-
putadores (incluyendo aquí el software del sistema operativo que los controlan) precisan para ello de 
buenos programas. Esta Unidad didáctica se centra en una metodología básica para diseñar y desarro-
llar adecuadamente programas que resuelvan problemas. Una adecuada metodología es siempre útil, 
aunque es especialmente conveniente, incluso necesaria, en problemas de cierta complejidad y enti-
dad. Si bien la utilización de una buena metodología en programación no siempre garantiza una termi-
nación exitosa de la tarea encomendada, se puede decir que el desorden, cuando no el fracaso, es el 
producto esperable de ignorar una buena metodología y sus principios sistemáticos de diseño. 
Los objetivos de una metodología de diseño y desarrollo de programas son los siguientes: 
 
• El diseño resultante debe ser correcto. Los métodos formales de verificación pueden tratar 
la corrección de programas simples, aunque desgraciadamente no están suficientemente 
desarrollados para programas con cierta entidad (no simples). Una forma de buscar la 
corrección es impulsar la simplicidad en el diseño, sin complejidades añadidas. 
• El diseño resultante deberá ser fácilmente implementable en el lenguaje de programación elegido. 
• Los programas deben poder ser mantenidos eficientemente. El diseño debe ser claro y ajustado 
al problema que se quiere resolver, así como estructurado en subprogramas (funciones y 
procedimientos). Los subprogramas deben poder ser actualizados y mantenidos fácilmente 
tanto por el autor o autores del programa, como por otros programadores diferentes. 
 
Como lenguaje de codificación se empleará en esta Unidad didáctica el lenguaje C++ como len-
guaje procedimental, el cual, entre otros lenguajes posibles, proporciona los elementos necesarios y 
adecuados para llevar a cabo una programación estructurada y una descomposición de tareas en subta-
reas, las cuales pueden ser implementadas en subprogramas (funciones y procedimientos). 
No se tratarán en esta Unidad didáctica la complejidad algorítmica ni las técnicas de diseño de algorit-
mos, importantes temas que serán estudiados en otras asignaturas. No obstante, algunos aspectos
de eficien-
cia algorítmica sí surgirán al considerar ciertos principios de diseño (así como en el estudio de la recursión). 
 
 
2. LA METODOLOGÍA DE DESARROLLO DE PROGRAMAS POR DISEÑO 
DESCENDENTE ("TOP-DOWN") 
 
En esta Unidad didáctica se van a cubrir las fases de diseño y codificación. El énfasis se pondrá 
en la primera, la fase de diseño, ya que la codificación se ha tratado ya en la asignatura previa de 
"Fundamentos de la Programación", donde se han adquirido los conceptos necesarios para programar 
empleando las estructuras de control y técnicas de la programación estructurada. 
La metodología de diseño que aquí se estudia es la metodología descendente (top-down) de divi-
sión de tareas en subtareas, muy ligada a la programación estructurada y al paradigma imperativo. Esta 
metodología también recibe los nombres de metodología funcional, metodología de refinamiento por 
pasos y metodología por aproximaciones sucesivas. 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 5 
J. Crespo del Arco Metodología básica de desarrollo de programas 
2.1. DISEÑO DESCENDENTE ("TOP-DOWN"): DESCOMPOSICIÓN DE TAREAS 
EN SUBTAREAS 
 
2.1.1. Descripción 
 
La metodología descendente considera que un programa está compuesto por subprogramas e in-
terfaces de los subprogramas. Un programa se descompone en partes más pequeñas o módulos utili-
zando la abstracción funcional (o procedimental). La abstracción funcional consiste en centrarse en 
qué tarea debe hacerse sin prestar especial atención a los detalles de cómo la tarea debe llevarse a ca-
bo. En esta Unidad didáctica consideramos que los módulos o partes constitutivas fundamentales de 
un programa son los denominados subprogramas, los cuales pueden ser funciones o procedimientos. 
Se pone el énfasis en la descomposición de las tareas de un programa en subtareas, resultantes de una 
abstracción funcional, y no se considerarán otros aspectos como la abstracción de datos, la cual se 
tratará en otras Unidades didácticas. Asimismo, por simplicidad, se supondrá que los módulos (enten-
diendo funciones y procedimientos) que componen un programa residen en un mismo fichero. 
 
Un primer paso de la descomposición de tareas en subtareas es la descripción funcional de qué 
realiza el programa, es decir, la tarea que debe realizar. Tras dicha descripción, no resulta generalmen-
te difícil descomponer dicha tarea en diversas subtareas. Se obtiene de esta forma una primera aproxi-
mación en la que la tarea principal se ha descompuesto en un primer nivel de tareas. La metodología 
de diseño descendente (top-down) consiste en aplicar dicho principio de descomposición de tareas en 
subtareas a cada una de las que han resultado del nivel anterior. En un determinado nivel de la des-
composición, es posible que alguna de las tareas ya obtenidas no se pueda descomponer más (en cuyo 
caso la fase de descomposición de tareas en subtareas finaliza para dicha tarea), mientras que otras 
tareas del mismo nivel, más complejas, son susceptibles de ser divididas en niveles subsiguientes. De 
esta forma se va refinando la descomposición, hasta que el resultado en el último nivel es un conjunto 
de tareas suficientemente sencillas que no se descomponen más. 
 
 
2.1.2. Descomposición de tareas en subtareas: ejemplos 
 
En la descomposición de una tarea en subtareas se deben seguir las estructuras básicas de control 
de la programación estructurada: 
 
• Secuenciación de tareas. 
• Selección de tareas. 
• Iteración de tareas. 
 
Estas estructuras fundamentales de control ya han sido estudiadas en la asignatura de «Fundamen-
tos de la Programación». En lo que sigue, se va a considerar en especial una de ellas, la secuenciación, 
para desarrollar unas primeras ideas básicas y principios de la descomposición de tareas en subtareas y 
plantear ciertos condicionantes de diseño. Posteriormente, en el siguiente epígrafe, se tratarán los prin-
cipios generales de la metodología de descomposición de tareas en subtareas. 
 
Una primera descomposición, dada la descripción de la tarea a realizar por un programa, es la se-
cuencial. Así, una gran parte de los problemas informáticos se pueden expresar, en una primera y su-
perficial aproximación, como la siguiente secuencia de operaciones: 
 
 
1. Obtener datos. 
2. Procesar datos. 
3. Generar resultados. 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 6 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
Dicha secuencia de operaciones suele aparecer en situaciones muy diversas y va en general aso-
ciada a una abstracción funcional de un programa: 
 
 
 
 
Procesamiento/Cálculos
Datos de entrada Salida
 
 
 
 
 
La secuencia anterior ya implica un orden temporal, por lo que claramente una descomposición 
con otro orden sería incorrecta. Si secuenciáramos las tareas de forma que la tarea de generar resulta-
dos fuera previa a la de procesar datos [es decir, si, por ejemplo, la secuencia fuera: 1) obtener datos, 
2) generar resultados, y 3) procesar datos], se estaría cometiendo un evidente error. 
 
 
2.1.2.1. Ejemplo con un nivel de descomposición 
 
Veamos un sencillo ejemplo en el que la secuencia 1) obtener datos, 2) procesar datos y 3) gene-
rar resultados, es adecuada para abordar y resolver un problema. En particular, sea el problema de 
calcular el número total de segundos que hay en una hora del formato horas:minutos:segundos, cuyos 
tres datos componentes se leen como entrada. En este ejemplo, dicha descomposición en tres tareas, 
correctamente secuenciadas, es perfectamente adecuada para abordar y resolver el problema en cues-
tión. Asimismo, en ese primer nivel de descomposición resultan tareas suficientemente simples, por lo 
que no es necesario descomponer a su vez ninguna de esas tres tareas en subtareas en niveles subsi-
guientes. La descomposición en tres tareas se plasma directamente en sendas funciones y procedimien-
tos, como se puede ver examinando el main del programa. El código final es el siguiente: 
 
 
 
/* Programa para convertir horas:minutos:segundos en número de segundos 
total */ 
 
#include <iostream> 
using namespace std; 
 
void obtener_datos (int *horas, int *minutos, int *segundos) 
{ 
 cout << "Introduzca el número de horas: \n"; 
 cin >> *horas; 
 cout << "Introduzca el número de minutos: \n"; 
 cin >> *minutos; 
 cout << "Introduzca el número de segundos: \n"; 
 cin >> *segundos; 
 return; 
} 
 
int calculo_segundos (int horas, int minutos, int segundos) 
{ 
 return (horas*3600 + minutos*60 + segundos); 
} …/… 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 7 
J. Crespo del Arco Metodología básica de desarrollo de programas 
 
void escribir_resultados (int horas, int minutos, int segundos, int 
segundos_total) 
{ 
 cout << "=> El número de segundos en " << horas << ":" 
 << minutos << ":" << segundos << "es: " << segundos_total
<< "\n"; 
 return; 
} 
 
int main (void) 
{ 
 int horas, minutos, segundos, resultado; 
 
 obtener_datos (&horas, &minutos, &segundos); 
 resultado = calculo_segundos (horas, minutos, segundos); 
 escribir_resultados (horas, minutos, segundos, resultado); 
 return 0; 
} 
 
 
 
Podría parecer que la secuencia de 1) obtener datos, 2) procesar datos y 3) generar resultados es 
adecuada para todos los programas que realicen de alguna forma una aplicación de procesamiento de 
datos de entrada, y que, por lo tanto, dicha secuencia de tres tareas sería la primera aproximación en el 
primer nivel de división en tareas de dichos programas. En realidad, no es el caso, y lo veremos segui-
damente en un problema de escasa complejidad. 
 
Consideremos el problema siguiente: dado un fichero de texto, queremos estudiar estadística-
mente el texto y, en particular, calcular el número de apariciones de las vocales. Por simplicidad, se 
supone que no hay tildes en el texto. El siguiente código constituye una primera versión del progra-
ma y sigue la descomposición de tareas previamente comentada: 1) obtener datos, 2) procesar datos 
y 3) generar resultados. Tras presentar el código, se comentará una limitación importante que pre-
senta este diseño. 
 
 
/* Programa Analizar Texto: Primera versión */ 
 
#include <iostream> 
using namespace std; 
#include <cstdio> 
 
#define NVOCALES 5 
#define NCARLINEA 80 
#define INDICE_A 0 
#define INDICE_E 1 
#define INDICE_I 2 
#define INDICE_O 3 
#define INDICE_U 4 
#define NCARMAXFICHERO 10000 
 
int lee_texto (char texto[]) 
{ 
 FILE *fp; 
 char nfichero[NCARLINEA]; /* nombre del fichero a procesar */ 
 
 cout << "Introduzca el nombre del fichero a procesar:\n"; 
 cin >> nfichero; 
 fp = fopen (nfichero, "r"); 
 return (fread (texto, 1, NCARMAXFICHERO, fp)); 
} …/… 
…/… 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 8 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
 
 
void procesa_caracter (char car, int acum_voc[]) 
{ 
 switch(car) { 
 case 'a': case 'A': 
 acum_voc[INDICE_A]++; /* actualización de contador de la vocal 'a'/'A' */ 
 break; 
 case 'e': case 'E': 
 acum_voc[INDICE_E]++; /* actualización de contador de la vocal 'e'/'E' */ 
 break; 
 case 'i': case 'I': 
 acum_voc[INDICE_I]++; /* actualización de contador de la vocal 'i'/'I' */ 
 break; 
 case 'o': case 'O': 
 acum_voc[INDICE_O]++; /* actualización de contador de la vocal 'o'/'O' */ 
 break; 
 case 'u': case 'U': 
 acum_voc[INDICE_U]++; /* actualización de contador de la vocal 'u'/'U' */ 
 break; 
 } /* fin switch */ 
 return; 
} 
 
void procesa_texto (char texto[], int acum_voc[], int ncar) 
{ 
 int i = 0; 
 
 for (i=0; i<ncar; i++) { 
 procesa_caracter (texto[i], acum_voc); 
 } /* fin for */ 
 return; 
} 
 
void escribe_resultados (int acum_voc[], int ncar) 
{ 
 cout << "Número de caracteres totales en el texto: " << ncar << "\n"; 
 cout << "Número de apariciones de las vocales:\n" 
 << " a: " << acum_voc[INDICE_A] << "; e: " << acum_voc[INDICE_E] 
 << "; i: " << acum_voc[INDICE_I] << "; o: " << acum_voc[INDICE_O] 
 << "; u: " << acum_voc[INDICE_U] << "\n"; 
 return; 
} 
 
int main (void) 
{ 
 char texto_fichero[NCARMAXFICHERO]; 
 int acum_vocales[NVOCALES] = {0,0,0,0,0}, ncaracteres; 
 
 ncaracteres = lee_texto (texto_fichero); 
 procesa_texto (texto_fichero, acum_vocales, ncaracteres); 
 escribe_resultados (acum_vocales, ncaracteres); 
 return 0; 
} 
 
 
El programa anterior resuelve el problema, aunque presenta una limitación importante que puede 
plantear dificultades prácticas en ciertas situaciones: requiere leer y almacenar todo el texto antes de 
procesarlo. Esta característica puede constituir un serio condicionante si el texto a analizar es muy 
extenso. Esta limitación no existía en el anterior programa del cálculo del número de segundos, ya que 
únicamente tres datos (tres números) constituían la entrada. 
Una mejor solución, en este caso, es abandonar la secuencia de tres tareas antes comentada y 
agrupar las tareas de leer la entrada y procesarla. Así, podemos ir leyendo la entrada carácter a carác-
…/… 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 9 
J. Crespo del Arco Metodología básica de desarrollo de programas 
ter, e ir procesando el carácter leído. De esta forma no es necesario esperar a leer toda la entrada para 
procesarla. El siguiente código sigue dicha descomposición, en la que se han agrupado las tareas de 
obtener datos y procesarlos. Por razones de espacio, no se ha repetido el código de los subprogramas 
que aparecían en la versión previa. 
 
 
/* Programa Analizar Texto: Segunda versión */ 
 
#include <iostream> 
using namespace std; 
#include <cstdio> 
 
#define NVOCALES 5 
#define NCARLINEA 80 
#define INDICE_A 0 
#define INDICE_E 1 
#define INDICE_I 2 
#define INDICE_O 3 
#define INDICE_U 4 
 
void procesa_caracter (char car, int acum_voc[]) 
{ 
 /* Nota: mismo procedimiento que en el código previo. No se repite 
aquí. */ 
} 
 
void lee_procesa_texto (int acum_voc[], int *ncar) 
{ 
 FILE *fp; 
 char nfichero[NCARLINEA]; /* nombre del fichero a procesar */ 
 char car; 
 
 cout << "Introduzca el nombre del fichero a procesar:\n"; 
 cin >> nfichero; 
 fp = fopen (nfichero, "r"); 
 *ncar = 0; /* inicialización */ 
 while ((fscanf (fp, "%c", &car)) != EOF) { 
 procesa_caracter (car, acum_voc); 
 (*ncar)++; 
 } /* fin while */ 
 return; 
} 
 
void escribe_resultados (int acum_voc[], int ncar) 
{ 
 /* Nota: mismo procedimiento que en el código previo. No se repite 
aquí. */ 
} 
 
int main (void) 
{ 
 int acum_vocales[NVOCALES] = {0,0,0,0,0}, ncaracteres; 
 
 lee_procesa_texto (acum_vocales, &ncaracteres); 
 escribe_resultados (acum_vocales, ncaracteres); 
 return 0; 
} 
 
2.1.2.2. Ejemplo con varios niveles de descomposición 
 
En los ejemplos anteriores, ha bastado el primer nivel de descomposición de tareas en subtareas. 
En otros casos más complejos se precisa un refinamiento por pasos en varios niveles. 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 10 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
Por ejemplo, en una aplicación estadística, considérese que se dispone de un fichero de entrada 
con los datos de población y renta de las provincias españolas (líneas con campos: "nombre provin-
cia", "población", "renta"), y de otro fichero con la lista de provincias junto a la información de a qué 
comunidad autónoma pertenece (líneas con campos: "nombre provincia", "nombre comunidad autó-
noma"). Sea entonces el problema de dar como resultado las "n" (parámetro introducido por el usua-
rio) comunidades autónomas con mayor renta per cápita. 
 
En un primer nivel, la descomposición puede ser la ya comentada: 
 
1. Obtener datos. 
2. Procesar datos. 
3. Generar
resultados. 
 
La tarea 1 se puede descomponer a su vez en tres partes diferenciadas: 
 
1.1. Obtener los datos provinciales del fichero de entrada. 
1.2. Obtener los datos de las provincias que componen cada comunidad autónoma. 
1.3. Obtener el parámetro «n» introducido por el usuario. 
 
Asimismo, la tarea 2 de procesar datos es suficientemente compleja como para ser descompuesta 
y dividida en subtareas. Así, podemos dividir la tarea 2 en: 
 
2.1. Calcular la renta per cápita provincial procesando los datos provinciales. 
2.2. Calcular las «n» comunidades con mayor renta per cápita a partir de los cálculos pro-
vinciales per cápita y del dato de las provincias que componen cada comunidad autónoma. 
 
La tarea 2.2 puede ser también descompuesta en: 
 
2.2.1. Calcular las rentas per cápita de las comunidades autónomas integrando los cálculos 
provinciales y el dato de las provincias que componen cada comunidad autónoma. 
2.2.2. Ordenar descendentemente las comunidades según su renta per cápita. 
2.2.3. Seleccionar las "n" primeras comunidades de la lista ordenada. 
 
Por último, cabe recalcar que no se debe olvidar realizar los interfaces entre los subprogramas 
de forma adecuada, de manera que éstos reciban la información necesaria y entreguen los resultados 
correspondientes. 
 
 
 
3. PRINCIPIOS Y PROPIEDADES GENERALES DE UN DISEÑO DESCENDENTE 
 
Después de haber comentado los ejemplos de descomposiciones de tareas en subtareas de la sec-
ción precedente, consideremos los principios y propiedades generales que deben considerarse en las 
descomposiciones. 
 
La descomposición adecuada de tareas en subtareas propia del diseño descendente es una tarea inte-
lectual que, obviamente, mejora con la experiencia y práctica. No obstante, existen ciertos principios ge-
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 11 
J. Crespo del Arco Metodología básica de desarrollo de programas 
nerales que deben seguirse y que facilitan la realización de un buen diseño. Asimismo, en lo que respecta 
a la importancia de la experiencia, no se debe caer en el error de reutilizar ciegamente partes de diseños de 
otros problemas, ya que problemas con pequeñas diferencias entre sí pueden dar lugar a descomposiciones 
y diseños bastante diferentes. Así, todo problema debe analizarse con cuidado y de forma particular. 
 
 
3.1. MODULARIDAD DE UN PROGRAMA 
 
La metodología descendente introduce modularidad en un programa al dividirlo en módulos (en-
tendiendo en esta Unidad didáctica módulos como subprogramas, en la programación procedimental 
que se está considerando). Seguidamente, la modularidad se justifica desde un punto de vista del coste 
para la realización de un proyecto informático. 
 
Sean P1 y P2 dos proyectos de la misma naturaleza. Entonces: 
 
Si Tamaño(P1) ≥ Tamaño(P2), entonces C(P1) ≥ C(P2) 
 
donde "C(P)" denota el coste de realizar el proyecto P. 
 
La implicación anterior pone de manifiesto que, en proyectos y problemas de la misma naturale-
za, es de esperar que el coste de realización se incremente con el tamaño. 
 
Si P es un proyecto de cierta entidad (es decir, no trivial) y se puede dividir en dos partes inde-
pendientes de la misma naturaleza, que se suponen por simplicidad iguales (es decir, dos mitades "P/2" 
y "P/2"), entonces se tiene generalmente que el coste de realizar P es mayor que el de realizar las dos 
partes (mitades) que la componen: 
 
C(P) ≥ C(P/2) + C(P/2) 
 
La desigualdad anterior expresa el conocido principio de "divide y vencerás", y es uno de los prin-
cipios fundamentales de la programación y de la ingeniería del software: el coste de realización de una 
tarea (no trivial) es generalmente mayor que el de las subtareas independientes que la componen. 
 
Se ha supuesto en la desigualdad anterior que las dos partes son independientes. Cuando un pro-
yecto P se descompone en subproyectos no independientes P1 y P2, el análisis de coste se complica en 
cierto modo. En este caso, aparecen dos tareas suplementarias T1 y T2 que reflejan las tareas de in-
teracción de, respectivamente, P1 con P2 y de P2 con P1. En este caso, podemos establecer que: 
 
C(P1 + I1) + C(P2 + I2) ≥ C(P/2) + C(P/2) 
 
No obstante, la relación entre C(P) y C(P1 + I1) + C(P2 + I2) no es trivial. Si los subproyectos P1 
y P2 no son independientes y tienen tareas asociadas de interacción T1 y T2 de cierta entidad, se pue-
de encontrar que la descomposición es perjudicial para el diseño, puesto que el coste de realizar las 
tareas descompuestas puede ser mayor que el de realizar el proyecto original: 
 
C(P1 + I1) + C(P2 + I2) ≥ C(P) (situación desfavorable) 
 
Ahora bien, si la interacción entre P1 y P2 es pequeña, es decir, cuando I1 e I2 son tareas de poca en-
tidad, entonces nos encontramos con la siguiente situación, la cual es la que se debe buscar en la práctica: 
 
C(P) ≥ C(P1 + I1) + C(P2 + I2) ≥ C(P/2) + C(P/2) 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 12 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
3.2. ACOPLAMIENTO ENTRE MÓDULOS 
 
El acoplamiento entre módulos es una propiedad que hace referencia a la independencia de los 
módulos de un programa. Cuanto mayor es la independencia entre las funciones y procedimientos de 
un programa, en el sentido de que interactúan lo menos posible, se dice que menor es su acoplamiento. 
Un módulo con bajo acoplamiento puede ser fácilmente utilizado en otros programas. 
 
Conseguir un bajo acoplamiento es un objetivo deseable en la programación y, en particular, en 
el diseño descendente mediante refinamiento por pasos. Asimismo, cuando las tareas de un progra-
ma tienen un alto grado de independencia entre sí, se facilita en gran medida la descomposición en 
el siguiente nivel de cada tarea en otras subtareas. De hecho, la propiedad de un bajo acoplamiento 
introduce un buen criterio de diseño a la hora de realizar la descomposición de tareas en subtareas, o 
lo que es lo mismo, de descomponer un módulo en submódulos: se debe realizar esta división mien-
tras que los módulos resultantes sean independientes entre sí. Cuando una descomposición produzca 
módulos interdependientes, será recomendable no llevarla a cabo (y parar entonces la descomposi-
ción del módulo), ya que podríamos encontrarnos en la situación antes comentada a evitar en la que: 
 
C(P1 + I1) + C(P2 + I2) ≥ C(P) (situación desfavorable con alto acoplamiento) 
 
 
Utilización de información local al módulo 
 
Con relación a maximizar la independencia de los módulos, los subprogramas deben, en lo posi-
ble, utilizar como información de entrada los parámetros (o argumentos) y, generalmente, no es reco-
mendable, salvo razón justificada, la utilización de variables globales. 
 
De este modo, si realizamos una función que calcule el valor de la hipotenusa de un triángulo 
rectángulo dados sus catetos empleando el teorema de Pitágoras, la información de entrada debe obte-
nerse de los parámetros de entrada en vez de emplear variables globales. De las dos versiones siguien-
tes de la función hipotenusa, la segunda es la que debe implementarse. 
 
 
double hipotenusa (void) /* Versión no recomendable */ 
{ 
 /* cat1 y cat2 son variables globales externas al módulo
*/ 
 return (sqrt (cat1*cat1, cat2*cat2)); 
} 
 
 
 
double hipotenusa (double cat1, double cat2) 
{ 
 /* cat1 y cat2 son parámetros, y por lo tanto locales al módulo */ 
 return (sqrt (cat1*cat1, cat2*cat2)); 
} 
 
 
 
Además de la mayor claridad de la función con parámetros, el código es más independiente y 
reutilizable, al no depender de unas variables globales que se llamen de una determinada manera. 
No obstante, se debe mencionar que el paso de parámetros introduce cierto coste computacional. 
Aunque no es en principio un factor determinante en la inmensa mayoría de los casos, en este 
sentido se debe recordar que construcciones del lenguaje C/C++ como las macros y las funciones 
inline pueden disminuir el coste computacional de la utilización de parámetros. 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 13 
J. Crespo del Arco Metodología básica de desarrollo de programas 
3.3. NÚMERO DE ASPECTOS A CONSIDERAR EN CADA MOMENTO 
 
Este principio tiene su origen en el hecho, como trata la psicología cognitiva, de que el cerebro 
humano tiene grandes dificultades en considerar y prestar atención en un momento dado a más de siete 
aspectos diferentes. 
 
Es por ello que en general se debe evitar la realización de descomposiciones de una tarea en más 
de seis componentes (además de que deben ser independientes, como se ha comentado), y es reco-
mendable que dicho número no sea mayor que 4. En otro caso, sería fácil pasar por alto interrelaciones 
entre los componentes y cometer errores. 
 
 
3.4. COHESIÓN INTERNA DE UN MÓDULO 
 
La cohesión (o compacidad) mide cuán grande es la relación entre los componentes de un módu-
lo. Es deseable que la cohesión sea alta. Un módulo que realice una tarea bien definida, como, por 
ejemplo, el cálculo de la función beta de Euler, es más cohesionado y compacto que otro que haga 
dicho cálculo y, además, inicializaciones varias de variables del programa que no tengan relación con 
el cálculo de la función. 
 
Seguidamente se comentan los diversos tipos de cohesión que pueden encontrarse, junto a la valo-
ración cuantitativa de cada uno de los tipos. Los tres primeros tipos de cohesión se consideran insufi-
cientes y prácticamente sin beneficios (especialmente el primer tipo). Se debe observar que en la me-
todología tratada se ha entendido que los módulos que componen un programa son subprogramas 
(funciones y procedimientos); algunos conceptos y tipos de cohesión que se comentan seguidamente 
tienen sentido especialmente cuando los módulos contienen (como, por ejemplo, en el caso de los ti-
pos de abstractos de datos) varios subprogramas, así como estructuras de datos, como sucederá en 
otras Unidades didácticas subsiguientes. 
 
• Cohesión coincidental (valoración de 0 puntos). Es una "mala" cohesión, que no aporta 
beneficios, en la que en un módulo simplemente se agrupan bloques de código de forma 
arbitraria sin relación relevante entre ellos. 
• Cohesión lógica (valoración de 1 punto). La cohesión lógica sucede cuando un módulo 
agrupa elementos que realizan funciones dentro de una misma categoría lógica, como por 
ejemplo agrupar las funciones de entrada/salida en un módulo. Un problema de un 
módulo que presente este tipo de cohesión es que realizará en principio diversas funcio-
nes y tareas, lo cual no se ajusta a la metodología presentada. 
• Cohesión temporal (valoración de 3 puntos). Se dice que los módulos de un programa 
tienen cohesión temporal cuando agrupan funciones y tareas en función de cuándo se 
ejecutan. Por ejemplo, cuando en un programa existe un módulo de inicialización que 
reúne todas las tareas de inicialización, otro de terminación, etc. 
• Cohesión procedimental (valoración de 5 puntos). Un módulo presenta cohesión procedi-
mental cuando agrupa elementos asociados a una secuencia lógica de pasos (es decir, 
asociados algorítmicamente), como pueden ser, por ejemplo, diversas tareas secuencial-
mente asociadas a la lectura de los datos en una aplicación. 
• Cohesión comunicacional (valoración de 7 puntos). Este tipo de cohesión se presenta cuando 
los elementos de un módulo operan sobre el mismo conjunto de datos, ya sean datos de 
entrada o de salida. 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 14 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
• Cohesión secuencial (valoración de 9 puntos). En la cohesión secuencial los datos de 
salida de un elemento del módulo son la entrada para otro elemento del mismo. Así, en la 
cohesión secuencial se combinan sucesivas operaciones relacionadas cuyas entradas y 
salidas se encadenan y ensamblan. 
• Cohesión funcional (valoración de 10 puntos). La cohesión funcional se alcanza cuando 
todos los componentes de un módulo llevan a cabo una tarea bien definida. Por ejemplo, 
el caso antes mencionado de un subprograma matemático especializado que realiza una 
función precisamente definida, como el cálculo de la función beta de Euler, sin abordar 
otras tareas no necesarias o sin relación, tiene cohesión funcional. 
 Una forma de detectar que un módulo tiene cohesión funcional es por eliminación: que no 
presente los otros tipos de cohesión. 
 
 
3.5. COMPLEJIDAD DE UN MÓDULO 
 
Es deseable una complejidad baja de los módulos de un programa para que el diseño sea claro y sim-
ple, y así facilitar, entre otros aspectos, la consecución de programas correctos. Se considera que los errores 
esperables al desarrollar un programa informático aumentan de forma no lineal con la complejidad. 
 
Algunos factores que influyen en la complejidad de un módulo son: 
 
3.5.1. La cantidad de información que recibe 
 
Como se ha mencionado anteriormente, un módulo debe recibir la información que necesita a 
través de parámetros de entrada y, en lo posible, no emplear variables globales. Los parámetros de 
entrada deben ser solamente los necesarios para que el subprograma realice la tarea en cuestión. El 
número y tamaño de los mismos crece en general con el número de tareas que realiza un subprograma. 
No obstante, se recuerda que es deseable que un subprograma realice sólo una tarea. 
 
3.5.2. Tamaño del módulo 
 
La complejidad de un módulo crece en general con el número de sentencias de las que consta, así 
como con el número de caminos independientes que puede seguir la ejecución del subprograma. El 
número de caminos independientes de, por ejemplo, una instrucción condicional if-then-else es 
2: uno para el bloque asociado al then, y otro para la parte else. Este último aspecto es particular-
mente considerado por la denominada complejidad ciclomática o condicional, y afecta a métodos de 
prueba de código al implicar la comprobación cada camino independiente. 
 
En cuanto al número de líneas de código, se debe decir que, en general, las funciones y procedi-
mientos no deben tener un número de líneas de código muy elevado. No obstante, el número de líneas 
no es en sí una buena métrica ni un criterio completo para delimitar el tamaño de funciones y proce-
dimientos. El criterio básico es que una función o procedimiento debe centrarse en hacer una tarea. Si 
se encuentra que el código hace más de una tarea, es un claro indicio de que, probablemente, deba 
dividirse la función o procedimiento en varios subprogramas.
De todas formas, esto es algo que de-
bería surgir antes de la etapa de codificación propiamente dicha, y ser el resultado de la etapa de dise-
ño siguiendo una descomposición descendente de tareas en subtareas. No obstante lo anterior, habrá 
situaciones y problemas que requieran no desdoblar una función o procedimiento que realiza más de 
una tarea. Un ejemplo de este tipo se comentó en el programa de análisis de apariciones de vocales en 
un texto (ver la sección anterior): las tareas de lectura y procesado están agrupadas en un mismo sub-
programa para no presentar limitaciones cuando el tamaño del texto de entrada sea excesivo. 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 15 
J. Crespo del Arco Metodología básica de desarrollo de programas 
Asimismo, se debe mencionar que la legibilidad de una función o procedimiento disminuye sen-
siblemente al aumentar el número de líneas de código. Es una cuestión que también se puede encua-
drar como perteneciente a las características del "buen estilo" en programación que se comenta en el 
siguiente epígrafe. Una heurística es que el número de líneas de código de una función o procedimien-
to no debería superar 50, salvo casos especiales y justificados. De hecho, en la práctica una gran parte 
de los subprogramas tendrá un número de líneas menor que 25. Programadores experimentados co-
mentan asimismo la "regla" práctica de que un subprograma debe poder visualizarse adecuadamente 
en una página (o una ventana gráfica) para facilitar su inspección. 
 
 
3.6. OTROS PRINCIPIOS 
 
3.6.1. Retrasar decisiones 
 
Este principio de diseño recomienda retrasar decisiones de diseño y de implementación hasta que 
sean necesarias para avanzar en el proceso de descomposición descendente. Esto conlleva generalmente 
no abordar detalles de implementación en las primeras fases de descomposición de tareas en subtareas. 
 
Prestemos atención al caso de los bucles de la programación estructurada, los cuales proporcionan 
un ejemplo muy sencillo de toma de decisiones, en particular de cuándo abordarlas o bien relegarlas. 
Las conclusiones se extienden a otras situaciones más complejas. 
 
El caso de los bucles 
 
El lenguaje C proporciona tres estructuras fundamentales de bucle: while, do y for. Conside-
remos en lo que sigue el caso del bucle while, incluyendo los bloques de instrucciones previas pre-
paratorias al bucle y las instrucciones posteriores al bucle, el cual se puede descomponer en los si-
guientes bloques: 
 
 
1. Instrucciones preparatorias previas al bucle. 
2. while <<condición iteración>> { 
 <<instrucciones repetitivas del bucle>> 
 } /* end while */ 
3. Instrucciones post bucle. 
 
 
 
Las partes 1 y 3 en la secuencia anterior pueden ser prácticamente inexistentes. La mejor ma-
nera de abordar las tres partes anteriores seguiría este orden: primeramente, la condición de la ite-
ración; seguidamente, las instrucciones repetitivas del bucle; y, por último, y en este orden, las 
partes 3 y 1 posteriores y previas, respectivamente. Asimismo, la parte 1 debe inicializar la condi-
ción de la iteración, así como posiblemente otras variables que se utilicen en las instrucciones repe-
titivas del bucle. 
 
Puede sorprender en un primer momento el hecho de que la parte 1 de preparación previa al bucle 
sea realmente la última en detallarse, aunque en realidad sigue una máxima que se cumple frecuente-
mente en el desarrollo descendente: "lo primero debe relegarse para el final". Es por otra parte lógico: 
la fase preparatoria debe saber qué debe preparar antes de determinar los detalles concretos de dicha 
preparación. Estas conclusiones no son sólo válidas para bucles, sino que se extienden a las descom-
posiciones de otras tareas más complejas. 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 16 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
3.6.2. Consistencia de las soluciones 
 
Cuando haya tareas muy semejantes (o, incluso, idénticas), es en principio recomendable que el 
código asociado siga un mismo patrón lógico para no inducir a confusión en el caso de que otro pro-
gramador revise el código y piense que hay alguna razón profunda y relevante para cambios. 
 
 
3.6.3. Localización 
 
La localización hace referencia a la facilidad para encontrar partes de un programa estrechamente 
relacionadas. Así, funciones y procedimientos que realicen tareas similares deben en lo posible agru-
parse en el programa de forma que se facilite la búsqueda y el examen del código. Este principio tiene 
importancia especialmente en el caso de que un programa se componga de diversos ficheros, los cua-
les se pueden agrupar en librerías. En tal caso, una librería debería agrupar funciones y procedimientos 
que guarden ciertas semejanzas y características comunes. 
 
 
 
4. CARACTERÍSTICAS BÁSICAS DE "BUEN ESTILO" EN PROGRAMACIÓN 
 
Lo que constituye un "buen estilo" al programar es un tema bastante subjetivo, al igual que lo es 
en otros ámbitos. No obstante, existen algunas recomendaciones generales cuyo seguimiento es muy 
recomendable, de forma que resulte un código más legible y fácil de comprender, así como de mante-
ner por otros programadores distintos del autor o autores. 
 
 
4.1. INDENTACIÓN 
 
La indentación, o espaciamiento con tabuladores y espacios en blanco, debe ayudar a identificar 
los bloques y estructuras de control de un programa. Aunque en muchos lenguajes, como el lenguaje 
C, la indentación y los espacios en blanco no afectan a la ejecución, sí influyen en gran medida en la 
legibilidad del código. Por ejemplo, los siguientes fragmentos de código proporcionan un ejemplo 
ilustrativo de esta idea: 
 
 
/* estilo de indentación inadecuado */ 
if (x 
 < y) 
{ 
return (x); 
 } else 
{ 
return (y); 
} 
 
 
 
/* estilo de indentación adecuado */ 
if (x < y) { 
 return (x); 
} else { 
 return (y); 
} 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 17 
J. Crespo del Arco Metodología básica de desarrollo de programas 
4.2. NOMBRES DE IDENTIFICADORES 
 
Los nombres utilizados para identificadores de funciones, variables y constantes deben ser des-
criptivos. Esto además redunda en que entonces los comentarios pueden en gran parte obviar dichas 
descripciones y centrarse más en otros aspectos del código. 
 
Por ejemplo, en los siguientes fragmentos de código, un procedimiento que calcula el área de un 
círculo, dado su radio, se implementa siguiendo muy distinto criterio para los nombres de los identifi-
cadores. En el primer caso, los identificadores son inadecuados, y el código precisaría comentarios 
suplementarios. Por el contrario, en el segundo caso los identificadores son descriptivos y los comen-
tarios podrían prácticamente obviarse. Ambas versiones son correctas, pero la legibilidad de la segun-
da versión es mucho mayor. 
 
 
/* Versión con identificadores
inadecuados, no descriptivos */ 
/* Sin comentarios suplementarios, el código es difícilmente legible */ 
 
#define NUMERO 3.1415926 
#define MACRO(x) ((x)*(x)) 
 
void funcion (float x, float *y) 
{ 
 *y = NUMERO * MACRO(x); 
} 
 
 
 
/* Versión con identificadores descriptivos */ 
 
#define PI 3.1415926 
#define CUADRADO(x) ((x)*(x)) 
 
void area_circulo (float radio, float *area) 
{ 
 *area = PI * CUADRADO(radio); 
} 
 
 
 
4.3. COMENTARIOS 
 
Los comentarios ayudan a incrementar la legibilidad de los programas. Una recomendación gene-
ral es que los comentarios no deben dejarse "para el final", es decir, deben ir incorporándose al código 
sin esperar a tener el código acabado. Si se espera al final, probablemente los comentarios no lleguen a 
incluirse de forma completa. 
 
Los comentarios de un programa deben incluir, al menos, la siguiente información: nombre del 
programa, la(s) persona(s) autor(es) del mismo, versión del programa y fecha de realización, una des-
cripción resumida de lo que hace el programa, incluyendo, si es el caso, una descripción de la entrada 
y la salida producida por el programa. 
 
Asimismo, es conveniente comentar las funciones y procedimientos del programa, y de manera 
especial aspectos tan importantes como, en el caso de que existan, las precondiciones (véase epígrafe 
5.3) que afectan a la entrada de los subprogramas. Es importante recordar la importancia de elegir 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 18 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
identificadores adecuados para los subprogramas, variables y constantes, de forma que los comentarios 
se puedan dedicar a tratar otros aspectos más significativos del código. 
 
Los aspectos importantes del código deben ser debidamente comentados. El autor o autores de un 
programa tienen que tener en mente que, aparte de él o ellos mismos, es muy posible que otra(s) per-
sona(s) distinta(s) actualizará(n) y modificará(n) el código en el futuro, por lo que los comentarios 
deben incluir ideas relevantes para ayudar en el mantenimiento del programa. 
 
En el caso particular del lenguaje C, entre otros, es muy conveniente que los finales de los blo-
ques se marquen relevantemente con comentarios para facilitar leer el código y localizar dichos 
finales de bloques. Por ejemplo, en el caso de un bucle while, es recomendable indicar claramente 
el final de su bloque de instrucciones: 
 
 
while { 
 
} /* fin while */ 
 
 
 
 
5. LA RECURSIÓN 
 
5.1. CONCEPTO DE RECURSIÓN Y FUNCIÓN RECURSIVA 
 
La recursividad es una herramienta de programación muy potente, y se introdujo en la informática 
dentro del denominado paradigma funcional, siendo una característica clave y distintiva del lenguaje 
LISP, el cual fue introducido por John McCarthy en 1958, proviniendo su nombre de LISt Processing 
(procesamiento de listas). 
 
Una función (o procedimiento) se dice que es recursiva cuando se llama o invoca a sí misma. 
También se puede considerar una función recursiva cuando llama a otra función que llama a su vez a 
la primera. 
 
 
5.2. EJEMPLO DE FUNCIÓN FACTORIAL: IMPLEMENTACIÓN ITERATIVA Y 
RECURSIVA 
 
La denominada función factorial ofrece un ejemplo sencillo e importante de función que presenta 
una definición iterativa y otra recursiva, las cuales se plasmarán, respectivamente, en dos implementa-
ciones iterativa y recursiva, respectivamente. 
 
 
Definición iterativa. El factorial de un número entero no negativo se define iterativamente de 
la siguiente forma: 
∏
=
=−=
n
k
knnn
1
1*2*3*...*)1(*! 
 
Asimismo, se establece que el factorial de 0 es igual a 1. 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 19 
J. Crespo del Arco Metodología básica de desarrollo de programas 
La anterior definición da lugar a la siguiente implementación iterativa (no recursiva): 
 
 
long int factorial_iterativo (int n) 
{ 
 long int res = 1; /* inicializacion */ 
 
 while (n > 0) { /* iteración para n positivos mayores que 0 */ 
 res *= n; 
 n--; 
 } 
 return (res); 
} 
 
 
La función factorial tiene otra definición matemática que es claramente recursiva: 
 
Definición recursiva 



>−
=
=
0),1(*
0,1
)(
nnfactorialn
nsi
nfactorial 
 
 
Si bien la función factorial puede implementarse de manera iterativa de una manera muy simple 
(como en la función factorial_iterativo), la definición matemática recursiva de la función 
también "invita" a realizar una implementación recursiva, la cual plasma directamente la definición 
matemática recursiva anterior, como se muestra en la siguiente función factorial_recursivo: 
 
 
/* PRE: n debe ser mayor o igual a 0 */ 
long int factorial_recursivo (int n) 
{ 
 if (n==0) { 
 return (1); /* caso básico, sin llamada recursiva */ 
 } else { 
 return (n * factorial_recursivo(n-1)); /* llamada recursiva */ 
 } 
} 
 
Nota. El código también incluye un comentario sobre la “PRE” (precondición), la cual se trata en el epígrafe 4.3. 
Asimismo, se puede observar que la función factorial_recursivo se ha implementado con más de un punto de 
salida (dos return), como ocurre en otras funciones recursivas de la presente Unidad didáctica. 
 
Toda función iterativa se puede expresar de manera recursiva, y viceversa. Ahora bien, en ocasio-
nes una de las dos implementaciones es más directa y fácil de implementar. En el ejemplo de la fun-
ción factorial estudiado en este epígrafe, ambas implementaciones son prácticamente igual de sencillas 
y surgen directamente de la definición matemática de factorial. Sin embargo, no siempre es así. Aun-
que es un tema que se sale del ámbito de la asignatura, cabe mencionar que existen procedimientos 
para abordar la transformación de iteratividad a recursividad, y viceversa, de manera sistemática. 
 
5.3. RECURSIVIDAD BIEN CONSTRUIDA. FUNCIONES PARCIALES Y TOTALES 
 
La recursividad debe estar bien construida, en el sentido de que el número de llamadas debe ser 
finito (es decir, la función debe terminar). Esto implica que las sucesivas llamadas deben "converger" 
en un caso básico. Así, las sucesivas llamadas recursivas invocan a la función con problemas de la 
misma naturaleza de manera que, finalmente, se cumpla una condición de parada que lleva a un caso 
básico (no recursivo) que se resuelve directamente. 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 20 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
Las funciones recursivas definidas al programar en informática son frecuentemente funciones par-
ciales, es decir, no están definidas para todos los valores posibles del tipo de entrada. Las funciones tota-
les están, por el contrario, definidas para todos los valores del tipo de entrada. Es algo que se debe tener 
muy en cuenta a la hora de utilizar una función, y este tipo de información debe en general aparecer en 
los comentarios que acompañan a la
cabecera de una función, como se ha comentado en el epígrafe 4.3. 
Por ejemplo, en el caso de la función factorial, se puede observar fácilmente que la implementa-
ción recursiva (función factorial_recursivo) no está definida para los enteros negativos: la 
recursión cuando la entrada es un entero negativo no acaba nunca al generarse un número no finito de 
llamadas. Así, en la cabecera de la función factorial debe aparecer (como se ha indicado en el código 
de la función factorial_recursivo): 
 
PRE: n debe ser mayor o igual a 0. 
 
La palabra PRE es una abreviatura del término precondición. Una precondición es una condición 
que debe ser cierta antes de la ejecución de la función a la que hace referencia. Por lo tanto, en la fun-
ción factorial, la citada frase denota que el parámetro de entrada «n» debe ser un entero no negativo; la 
función está entonces definida, y calcula el factorial sólo cuando se cumple la precondición que afecta 
al parámetro "n". Una función puede tener varias precondiciones, en cuyo caso todas deben satisfacer-
se. Las precondiciones son particularmente importantes en las funciones recursivas parciales, aunque 
son asimismo aplicables en funciones no recursivas. 
La implementación recursiva de la función factorial (función factorial_recursivo) está 
bien construida. Se puede observar en dicha implementación que (suponiendo, naturalmente, que se 
cumple la precondición) las sucesivas llamadas “confluyen” en el caso básico: así, por ejemplo, si se 
llama a la función factorial_recursivo con “n” igual a 3 (en cuyo caso se cumple la precon-
dición), se genera una llamada con “n” igual a 2, seguida de otra con “n” igual a 1, y, por último, una 
llamada con “n” igual a 0 que desemboca en el caso básico no recursivo que devuelve el valor 1. Cla-
ramente, todas las sucesivas llamadas hasta el caso básico no recursivo cumplen la precondición de la 
función. Se puede mencionar, asimismo, que las funciones recursivas (en comparación con las iterati-
vas) facilitan la formalización y análisis de las condiciones de terminación. 
Una cuestión que surge es qué hacer en el caso de que a una función recursiva parcial se le llame 
con un valor para el cual no esté definida. Por ejemplo, eso ocurre cuando a la función factorial se le 
llama con un entero negativo, caso no cubierto por la definición matemática de factorial. Una solución 
de compromiso que puede ser adecuada en ésta y otras situaciones (aunque no necesariamente en to-
das) es devolver un valor flag (bandera, en inglés) distintivo que indique al usuario que ha habido un 
error. En el caso de la función factorial, un valor flag de –1 es adecuado porque el factorial de un 
número siempre es positivo, por lo que un resultado negativo indicará al usuario de una manera clara y 
distintiva que ha ocurrido un error. La implementación, con un valor flag de –1 quedaría: 
 
 
/* PRE: n debe ser mayor o igual a 0 */ 
/* (Nota: sobre “PRE” (precondición), véase epígrafe 4.3.) */ 
long int factorial_recursivo (int n) 
{ 
 if (n<0) 
 return (-1); /* valor flag a devolver */ 
 else { 
 if (n==0) 
 return (1); /* caso básico, sin llamada recursiva */ 
 else 
 return (n * factorial_recursivo(n-1)); /* llamada recursiva */ 
 } /* end else */ 
} 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 21 
J. Crespo del Arco Metodología básica de desarrollo de programas 
5.4. TIPOS DE RECURSIÓN: RECURSIÓN FINAL Y NO FINAL 
 
La recursión puede tener implicaciones en la eficiencia de un programa. Las llamadas a la función 
producen en general un sobrecoste computacional (overhead) que debe considerarse. Frecuentemente 
(aunque seguidamente se considera un caso en el que esto se puede evitar), los argumentos de cada 
llamada a la función deben guardarse dinámicamente en memoria (concretamente, en la memoria de-
nominada “pila”), cuyo tamaño entonces crece con cada llamada. Esto conlleva ciertas labores de ges-
tión de dicha memoria y del paso de parámetros por parte del sistema operativo que se traduce en que, 
generalmente, las implementaciones recursivas suelen ser menos eficientes que las implementaciones 
iterativas. 
Por ejemplo, la llamada a la función factorial_recursivo con parámetro “n” igual a 4, 
genera las siguientes llamadas y cálculos asociados: 
 
 
factorial_recursivo(4) -> 4*factorial_recursivo(3) -> 
 
4*(3*factorial_recursivo(2)) -> 4*(3*(2*factorial_recursivo(1))) -> 
 
4*(3*(2*(1*factorial_recursivo(0)))) -> 4*(3*(2*(1*1))) = 
 
4*(3*(2*1)) = 4*(3*2) = 4*6 = 24 
 
 
En dicha función, cada llamada hace que se guarden en memoria (concretamente, en la pila) los 
argumentos de la función (en este caso, sólo uno) porque las multiplicaciones no se ejecutan hasta que 
las llamadas recursivas alcanzan el caso básico y “retornan”. Esto hace que la memoria pila crezca con 
cada sucesiva llamada recursiva de la función. 
 
No obstante, existe un tipo de recursión en la que se puede evitar que los argumentos se tengan 
que almacenar en la pila y que ésta crezca con cada llamada, con los consiguientes costes de tiempo y 
memoria. Es la denominada recursión final (tail recursion, en inglés). 
 
Veamos una implementación de factorial que es recursiva final, empleando una función auxiliar 
con un parámetro adicional (en el código, “acum”), el cual se denomina parámetro acumulador: 
 
 
/* PRE: n debe ser mayor o igual a 0. */ 
long int factorial_recursivo_final (int n, long int acum) 
{ 
 if (n==1) 
 return (acum); 
 else 
 return (factorial_recursivo_final(n-1, n*acum)); 
} 
 
/* PRE: n debe ser mayor o igual a 0. */ 
long int factorial (int n) 
{ 
 return (factorial_recursivo_final(n, 1)); /* llamada a función auxiliar */ 
} 
 
 
La función principal factorial, que recibe como parámetro de entrada el número “n” cuyo 
factorial se quiere calcular, llama a la función auxiliar factorial_recursivo_final, la cual 
calcula el factorial de su parámetro “n” cuando el valor del parámetro “acum” es igual a 1. Las llama-
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 22 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
das generadas y cálculos realizados al llamar a factorial_recursivo_final con parámetros 
“n” igual a 4 y “acum” igual a 1 son: 
 
 
factorial_recursivo_final(4,1) -> factorial_recursivo_final(3, 4) -> 
 
factorial_recursivo_final(2,12) -> factorial_recursivo_final(1,24) -> 
 
24 
 
 
El compilador puede evitar incrementar la memoria de pila en las sucesivas llamadas a facto-
rial_recursivo_final ya que no hay operaciones que involucren la llamada recursiva, a dife-
rencia de lo que ocurría en el caso de la función factorial_recursivo no final, en la cual la 
llamada recursiva era un operando de la operación multiplicación. 
 
Veamos otro ejemplo de una operación que se implementa de forma recursiva no final y, segui-
damente, como recursiva final. La operación es la suma de los elementos de un array de enteros desde 
el índice 0 hasta un índice que se pasa por parámetro. Como en el caso de la función factorial, la se-
gunda versión hace uso de una función auxiliar recursiva final con un parámetro adicional: un paráme-
tro acumulador que almacena cálculos intermedios.
/* La función suma_array_rec suma los enteros en array_num desde */ 
/* el índice 0 hasta el índice nrestantes */ 
/* PRE: 0 <= nrestantes <= número elementos de array_num */ 
int suma_array_rec (int array_num[], int nrestantes) 
{ 
 if (nrestantes == 0) { 
 return (0); 
 } 
 else { 
 return (array_num[nrestantes-1] + suma_array_rec (array_num, 
nrestantes-1)); 
 } 
} 
 
 
 
/* Función auxiliar recursiva final */ 
int suma_array_rec_final (int array_num[], int nrestantes, int acum) 
{ 
 if (nrestantes == 0) { 
 return (acum); 
 } 
 else { 
 return (suma_array_aux (array_num, nrestantes-1, 
 acum + array_num[nrestantes-1])); 
 } 
} 
 
/* La función suma_array_rec suma los enteros en array_num desde */ 
/* el índice 0 hasta el índice nrestantes */ 
/* PRE: 0 <= nrestantes <= número elementos de array_num */ 
int suma_array (int array_num[], int nrestantes) 
{ 
 return (suma_array_rec_final (array_num, nrestantes, 0)); 
} 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 23 
J. Crespo del Arco Metodología básica de desarrollo de programas 
5.5. CUÁNDO NO UTILIZAR LA RECURSIÓN 
 
La recursión es una potente herramienta y técnica general para implementar funciones y procedi-
mientos. Toda función que emplee la iteración se puede implementar utilizando la recursión, y vice-
versa. Una cuestión que surge es cuándo no es recomendable emplear la recursión. En general, hay dos 
situaciones en las que debe evaluarse la conveniencia de emplear la recursión y/o el modo de imple-
mentarla: 
 
• Cuando el coste computacional (overhead) ocasionado por las llamadas recursivas sea 
elevado. Más concretamente, esto ocurre cuando la recursión es no final y los parámetros 
que recibe la función o procedimiento recursivo tienen un tamaño considerable, los cuales 
tienen que guardarse en la pila de memoria en cada llamada. 
• Cuando las llamadas recursivas den lugar a llamadas repetidas, una implementación 
directa de la recursión es ineficiente. El estudio de esta situación se sale del marco de la 
presente unidad didáctica, aunque se puede mencionar que en ese caso los problemas 
repetidos se deben almacenar o tabular para evitar que se resuelvan repetidamente. Un 
ejemplo de función recursiva que presenta el problema de llamadas repetidas es la 
función de cálculo de números de Fibonacci: Fib (n) = Fib (n–1) + Fib (n–2), donde 
Fib(0)=0 y Fib(1)=1. Una función que implemente directamente la fórmula anterior, 
aunque correcta, sería ineficiente porque hay casos que se resuelven más de una vez. 
 
 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 24 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
 
CONCEPTOS BÁSICOS A RETENER 
 
 
• El concepto de metodología de desarrollo de programas y los objetivos que persigue. 
En qué consiste la metodología de diseño descendente (top-down) presentada en la 
Unidad didáctica y cómo abordar la descomposición de tareas en subtareas propia de 
esta metodología. 
• Los principios generales que debe seguir la descomposición de tareas en subtareas del 
diseño descendente. Estos principios son de gran utilidad, si no prácticamente impres-
cindibles, en situaciones complejas y en problemas con cierta entidad. 
• Las reglas de "buen estilo" que debe respetar la codificación para facilitar la legibilidad 
del código, así como su mantenibilidad y actualización futuras. 
• La potente herramienta de programación que constituye la recursión, y cómo ésta, en 
problemas de naturaleza inherentemente recursiva, debería ser en principio la primera 
opción (como alternativa a la iteración) a considerar en la implementación. Deben ser 
conocidos ciertos aspectos del coste computacional asociado a la recursión y al paso de 
parámetros de las llamadas recursivas. El tipo de recursión final debe utilizarse en 
situaciones en las que el coste computacional derivado de las llamadas recursivas quiera 
reducirse. 
 
 
 
 
 
 
 
 
 
 
ACTIVIDADES DE AUTOCOMPROBACIÓN 
 
 
A partir del contenido de la presente Unidad didáctica, se propone la realización de las siguientes 
actividades de autocomprobación por parte del alumno, como ejercicio general de repaso y asimilación 
de la información básica proporcionada por el texto. 
Enunciado 1 
 
Indique qué afirmaciones son ciertas: Una metodología de diseño y desarrollo de programas… 
a) Garantiza la corrección del programa final. 
b) Minimiza el número de líneas de código para resolver un problema dado. 
c) Sistematiza el diseño y ayuda a construir programas correctos, aunque no garantiza la 
corrección. 
 
 
 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 25 
J. Crespo del Arco Metodología básica de desarrollo de programas 
Enunciado 2 
 
Indique qué afirmaciones son ciertas: La metodología de diseño descendente (top-down)… 
a) Consiste en ejecutar de forma ordenada los subprogramas según su tamaño de mayor a 
menor. 
b) Consiste en ejecutar de forma ordenada los subprogramas según su complejidad de mayor 
a menor. 
c) Se basa en la descomposición de tareas en subtareas. 
Enunciado 3 
 
Defina cohesión y acoplamiento. 
Enunciado 4 
 
Indique qué afirmaciones son ciertas: 
a) Es deseable que un programa tenga una cohesión y un acoplamiento entre módulos elevados. 
b) Es deseable que un programa tenga una cohesión y un acoplamiento entre módulos bajos. 
c) Ninguna de las afirmaciones anteriores es correcta. 
Enunciado 5 
 
Indique cuáles de las siguientes afirmaciones son ciertas: 
a) La recursión final disminuye el coste computacional que resulta de las llamadas recursivas. 
b) La recursión, cuando es posible aplicarla, produce siempre funciones más eficientes que 
las equivalentes iterativas. 
c) La utilización de parámetros acumuladores está asociada a la recursión no final. 
d) Las llamadas recursivas deben desembocar en un caso no recursivo. 
 
 
 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
1 – 26 www.udima.es 
METODOLOGÍA DE LA PROGRAMACIÓN 
Solución 1 
 
c) Sistematiza el diseño y ayuda a construir programas correctos, aunque no garantiza la 
corrección. 
Solución 2 
 
c) Se basa en la descomposición de tareas en subtareas. 
Solución 3 
 
Véase epígrafe 3. 
Solución 4 
 
c) Ninguna de las afirmaciones anteriores es correcta. 
Solución 5 
 
a) La recursión final disminuye el coste computacional que resulta de las llamadas recursivas. 
d)
Las llamadas recursivas deben desembocar en un caso no recursivo. 
 
 
 
 
 
 
 
 
 
 
EJERCICIOS VOLUNTARIOS 
 
 
Tras el estudio de esta Unidad didáctica, el estudiante puede hacer, por su cuenta, una serie de 
ejercicios voluntarios, como los siguientes: 
 
1. Para organizar un referéndum se requiere imprimir las papeletas necesarias en una ciudad, 
y para ello se va a calcular el número de votantes correspondientes a cada distrito elec-
toral. Como datos para realizar la tarea se dispone de un censo de hace cinco años, de la 
lista de fallecidos desde hace cinco años, la lista de antiguos residentes que ya no están en 
la ciudad, y la lista de nuevos residentes con derecho a voto. En todos estos casos la 
información que se dispone correspondiente a cada persona es: nombre y apellidos, DNI, 
año de nacimiento y número de distrito electoral. 
 Utilice la metodología descendente (top-down) para diseñar los módulos de un programa 
que resuelva el problema. 
 
 
 
 
 
 
 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
 
www.udima.es 1 – 27 
J. Crespo del Arco Metodología básica de desarrollo de programas 
2. Implemente el diseño de la aplicación estadística comentada en la epígrafe 2.1.2.2. Puede 
obviar la tarea 2.2.2) de ordenación. 
3. Evalúe el acoplamiento y la cohesión que muestran los módulos finales obtenidos en el 
ejercicio anterior. 
4. El algoritmo de Euclides proporciona un método de cálculo del máximo común divisor 
"mcd" de dos números enteros positivos "a" y "b". Dicho algoritmo se define mediante la 
siguiente expresión matemática recursiva: 
 
 Se pide implementar una función recursiva que calcule el máximo común divisor según la 
expresión anterior. ¿Es la recursión final? 
5. Implemente el algoritmo de Euclides mediante una función no recursiva. 
 
 
 
 
 
 
 
REFERENCIAS BIBLIOGRÁFICAS 
 
 
 
Básica 
 
ALONSO AMO, F.; DE ANTONIO JIMÉNEZ, A.; DIESTE TUBIO, O.; FUENTES CONTE, P.; FUERTES CASTRO, J.L.; 
LÓPEZ GÓMEZ, G.; GONZÁLEZ MARTÍNEZ, Á.L.; MARTÍNEZ NORMAND, L.; MEDINA GARBARINO, T.; 
MONTES GARCÍA, C. y PAZOS SIERRA, J.: Programación sin secretos, Cultural, 1999. 
ANTONAKOS, J.L. y MANSFIELD, K.C.: Programación estructurada en C, Prentice Hall, 2002. 
FINDLAY, W. y WATT, D.A.: Pascal: Programación metódica, Rueda, 1984. 
MARTÍNEZ, L.; SEGOVIA, F.J. y ALONSO, F.: Introducción a la ingeniería del software: Modelo de desarrollo de pro-
gramas, Delta Publicaciones, 2005. 
 
 
Avanzada 
 
LISCHNER, R.: C++ in a nutshell, O'Reilly, 2003. 
STROUSTRUP, B.: The C++ Programming Language: special edition, Addison-Wesley, 3.ª ed., 2000. 
WIRTH, N.: Algoritmos + Estructura de datos = Programas, ediciones del Castillo, 1980. 
 
 
 
 
 
 
 
"Todos los derechos reservados. Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta Unidad sólo puede ser realizada con la autorización de la Universidad a Distancia 
de Madrid, UDIMA, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta Unidad".
	PRESENTACIÓN Y OBJETIVOS DE LA UNIDAD
<<
 /ASCII85EncodePages false
 /AllowPSXObjects false
 /AllowTransparency false
 /AlwaysEmbed [
 true
 ]
 /AntiAliasColorImages false
 /AntiAliasGrayImages false
 /AntiAliasMonoImages false
 /AutoFilterColorImages true
 /AutoFilterGrayImages true
 /AutoPositionEPSFiles true
 /AutoRotatePages /All
 /Binding /Left
 /CalCMYKProfile (Coated FOGRA27 \050ISO 12647-2:2004\051)
 /CalGrayProfile (Dot Gain 20%)
 /CalRGBProfile (sRGB IEC61966-2.1)
 /CannotEmbedFontPolicy /Warning
 /CheckCompliance [
 /None
 ]
 /ColorACSImageDict <<
 /HSamples [
 1
 1
 1
 1
 ]
 /QFactor 0.15000
 /VSamples [
 1
 1
 1
 1
 ]
 >>
 /ColorConversionStrategy /LeaveColorUnchanged
 /ColorImageAutoFilterStrategy /JPEG
 /ColorImageDepth -1
 /ColorImageDict <<
 /HSamples [
 1
 1
 1
 1
 ]
 /QFactor 0.15000
 /VSamples [
 1
 1
 1
 1
 ]
 >>
 /ColorImageDownsampleThreshold 1.50000
 /ColorImageDownsampleType /None
 /ColorImageFilter /DCTEncode
 /ColorImageMinDownsampleDepth 1
 /ColorImageMinResolution 300
 /ColorImageMinResolutionPolicy /OK
 /ColorImageResolution 300
 /ColorSettingsFile ()
 /CompatibilityLevel 1.4
 /CompressObjects /Tags
 /CompressPages true
 /ConvertImagesToIndexed true
 /CreateJDFFile false
 /CreateJobTicket false
 /CropColorImages false
 /CropGrayImages false
 /CropMonoImages false
 /DSCReportingLevel 0
 /DefaultRenderingIntent /Default
 /Description <<
 /ESP <FEFF005B00420061007300610064006F00200065006E0020002700500045005200460049004C00200043004F0059005600450027005D0020005500740069006C0069006300650020006500730074006100200063006F006E0066006900670075007200610063006900F3006E0020007000610072006100200063007200650061007200200064006F00630075006D0065006E0074006F0073002000640065002000410064006F0062006500200050004400460020007000610072006100200063006F006E00730065006700750069007200200069006D0070007200650073006900F3006E002000640065002000630061006C006900640061006400200065006E00200069006D0070007200650073006F0072006100730020006400650020006500730063007200690074006F00720069006F00200079002000680065007200720061006D00690065006E00740061007300200064006500200063006F00720072006500630063006900F3006E002E002000530065002000700075006500640065006E00200061006200720069007200200064006F00630075006D0065006E0074006F00730020005000440046002000630072006500610064006F007300200063006F006E0020004100630072006F006200610074002C002000410064006F00620065002000520065006100640065007200200035002E003000200079002000760065007200730069006F006E0065007300200070006F00730074006500720069006F007200650073002E>
 >>
 /DetectBlends true
 /DetectCurves 0
 /DoThumbnails false
 /DownsampleColorImages false
 /DownsampleGrayImages false
 /DownsampleMonoImages false
 /EmbedAllFonts true
 /EmbedJobOptions true
 /EmbedOpenType false
 /EmitDSCWarnings false
 /EncodeColorImages false
 /EncodeGrayImages false
 /EncodeMonoImages false
 /EndPage -1
 /GrayACSImageDict <<
 /HSamples [
 1
 1
 1
 1
 ]
 /QFactor 0.15000
 /VSamples [
 1
 1
 1
 1
 ]
 >>
 /GrayImageAutoFilterStrategy /JPEG
 /GrayImageDepth -1
 /GrayImageDict <<
 /HSamples [
 1
 1
 1
 1
 ]
 /QFactor 0.15000
 /VSamples [
 1
 1
 1
 1
 ]
 >>
 /GrayImageDownsampleThreshold 1.50000
 /GrayImageDownsampleType /None
 /GrayImageFilter /DCTEncode
 /GrayImageMinDownsampleDepth 2
 /GrayImageMinResolution 300
 /GrayImageMinResolutionPolicy /OK
 /GrayImageResolution 300
 /ImageMemory 1048576
 /JPEG2000ColorACSImageDict <<
 /Quality 30
 /TileHeight 256
 /TileWidth 256
 >>
 /JPEG2000ColorImageDict <<
 /Quality 30
 /TileHeight 256
 /TileWidth 256
 >>
 /JPEG2000GrayACSImageDict <<
 /Quality 30
 /TileHeight 256
 /TileWidth 256
 >>
 /JPEG2000GrayImageDict <<
 /Quality 30
 /TileHeight 256
 /TileWidth 256
 >>
 /LockDistillerParams false
 /MaxSubsetPct 100
 /MonoImageDepth -1
 /MonoImageDict <<
 /K -1
 >>
 /MonoImageDownsampleThreshold 1.50000
 /MonoImageDownsampleType /None
 /MonoImageFilter /CCITTFaxEncode
 /MonoImageMinResolution 1200
 /MonoImageMinResolutionPolicy /OK
 /MonoImageResolution 1200
 /Namespace [
 (Adobe)
 (Common)
 (1.0)

Continuar navegando