Tema 2
Tema 2
Introducción a la programación
Algunos de los conceptos que se abordan en esta sección ya fueron introducidos brevemente en el
tema anterior dedicado a fundamentos de la informática. No obstante, se hará un repaso y una
descripción más detallada de los mismos. La Figura 2.1 muestra gráficamente la relación entre los
conceptos problema, algoritmo y programa.
Un problema se define como asunto o conjunto de cuestiones que se plantean para ser re-
sueltas. Los problemas pueden ser de distinta naturaleza, si bien el objetivo de esta asignatura es
abordar problemas cuya solución se pueda calcular utilizando una serie de reglas introducidas en la
62
Figura 2.1: Relación entre los conceptos problema, algoritmo, lenguaje de programación y programa
computadora. Es importante recalcar que para que se pueda resolver correctamente un problema
este tiene que estar bien definido, es decir, hay que saber qué es lo que hay que resolver. Para ello se
deben eliminar posibles ambigüedades que haya en el enunciado, eliminar información irrelevante
y determinar exactamente qué elementos constituyen una solución válida. No obstante, un mismo
problema puede (y suele) resolverse por distintos caminos.
Para dar respuesta a un problema planteado entra en juego el concepto de algoritmo. Un
algoritmo es una secuencia finita y precisa de instrucciones o pasos que permiten resolver un
problema en un tiempo finito. Un algoritmo normalmente permite automatizar una operación como,
por ejemplo, ordenar una lista de números, distribuir los escaños de un parlamento según los votos,
etc. Las propiedades que debe poseer todo buen algoritmo son:
Definitud: toda regla de un algoritmo debe definir perfectamente la acción a desarrollar para
poder aplicarla sin que pueda haber lugar a ambigüedad.
Eficiencia: un algoritmo debe ser eficiente, es decir, el número de operaciones a realizar debe
ser el menor posible para que la solución al problema se obtenga en el menor tiempo posible
y, además, utilizando el menor número de recursos posible. En este sentido, un algoritmo
siempre puede ser potencialmente mejorable y se ha de intentar optimizar.
– Paso 3. Si el resto es igual a 0 entonces ir al Paso 4. En caso contrario, tomar el divisor como
nuevo dividendo y el resto como divisor y volver al Paso 2.
63
– Paso 4. El m.c.d. es el divisor de la última división.
Para poder expresar un algoritmo de forma que pueda ser reconocido y ejecutado en un compu-
tador se necesita un lenguaje de programación. De este modo, se define programa como la
expresión o codificación de un algoritmo en un lenguaje de programación, es decir, un programa es
el resultado de traducir un algoritmo a un lenguaje de programación específico.
Como ya se explicó en el tema anterior, una herramienta a tener en cuenta cuando queremos es-
cribir un programa en un determinado lenguaje es el compilador (si el lenguaje es no interpretado,
por ejemplo C o Java) o el intérprete (si el lenguaje es interpretado, por ejemplo Python o Perl).
Ambas herramientas deben estar instaladas en el computador si queremos que nuestro programa
pueda ejecutarse, ya que son las encargadas de obtener el código máquina a partir de un programa
escrito en un lenguaje de alto nivel. Al código de cualquier programa escrito en un determinado
lenguaje se le conoce como código fuente. La relación entre todos estos conceptos se resume en la
Figura 2.2.
Figura 2.2: Relación entre los conceptos código fuente, ejecutable, compilador e intérprete
1. Compilación, donde el código fuente se transforma (se codifica) a código objeto, que es
una representación cercana al código máquina. Al finalizar esta fase se queda pendiente la
asignación final de direcciones de memoria a los distintos objetos del programa. Si nuestro
programa estuviera escrito en diferentes archivos (módulos), la fase de compilación permite
64
obtener el código objeto correspondiente a cada fragmento, de manera que si el código fuente
de un fragmento no se modifica, no es necesario volver a compilarlo.
2. Enlazado (linking), donde se obtiene el código máquina a partir de la unión de todo código
objeto implicado, es decir, sería la fase posterior a la de compilación en la que se enlazan los
códigos objeto de cada uno de los módulos (fragmentos) a compilar en el caso de que nues-
tro programa estuviera escrito en diferentes archivos, generándose un único código máquina
(fichero ejecutable) resultado de la unión de los códigos objeto de los diferentes módulos.
65
Figura 2.4: Metodología general para la resolución de problemas en programación
– Fase 1: análisis del problema. Este primer paso consiste en definir y comprender el problema
en toda su extensión, analizando todo aquello que es relevante para su resolución.
– Fase 2: diseño del algoritmo. Una vez analizado el problema se debe desarrollar el algo-
ritmo, es decir, determinar los pasos que resolverán el problema. Para ello, existen algunas
herramientas para representar textual o gráficamente el algoritmo (pseudocódigo y organi-
gramas principalmente).
– Fase 3: programación o codificación del algoritmo. Por último, para resolver el pro-
blema mediante computador se necesita codificar el algoritmo utilizando algún lenguaje de
programación, generando así un programa informático. Una vez generado, se deberá ejecutar
el programa y comprobar que soluciona el problema eficazmente.
La fase de análisis tiene como objetivo conseguir que el programador alcance a comprender el
problema en toda su extensión. Esto requiere que el problema esté bien definido, es decir, que se
disponga de una descripción del problema suficientemente detallada en la que quede bien establecido
qué tipo de datos puede aceptar el problema como entrada y qué tipo de resultados se esperan como
solución.
Durante la fase de análisis del problema se deben realizar, a su vez, dos fases obligatorias:
– Fase 1.1: especificación de los datos de entrada del problema. Un dato de entrada puede
definirse como un dato del que depende la salida del problema y que, a su vez, no depende
de ningún otro (no puede calcularse a partir de otros datos de entrada). Un dato de entrada
puede ser variable (si el valor concreto que puede tomar se desconoce a priori) o constante
(si el valor concreto que puede tomar se conoce a priori y se mantiene fijo durante toda la
resolución del problema). Por regla general un dato de tipo variable se solicita al usuario
antes de resolver el problema, mientras que un dato de tipo constante se utiliza durante la
resolución del problema sin necesidad de pedirlo previamente al usuario.
– Fase 1.2: especificación de los datos de salida del problema. Un dato de salida es aquel
66
que proporciona toda o parte de la solución del problema y deberá poder obtenerse a partir
de los datos de entrada especificados.
En esta asignatura será obligatorio establecer un identificador y una descripción para cada
dato de entrada y salida definidos durante la fase de análisis. El identificador deberá ser único y
diferente para cada dato definido y la descripción debe describir brevemente en qué consiste cada
dato identificado. Como convención para esta asignatura, el identificador de los datos de entrada
de tipo constante se escribirá todo en mayúscula, mientras que para los datos de entrada de tipo
variable el identificador se escribirá todo en minúscula. Así, a la hora de codificar el algoritmo
durante la última fase resultará más sencillo diferenciar de forma visual las constantes de las
variables en el programa generado.
Además de establecer el identificador y la descripción, únicamente para los datos de en-
trada de tipo constante habrá que especificar en la descripción el valor que tomará dicho dato
durante la resolución del problema. Por otro lado, si el dato de entrada es de tipo variable
habrá que especificar si tiene o no restricciones, es decir, deberá indicarse, aparte de la descrip-
ción, qué valores estarán permitidos que dicho dato tome como entrada durante la resolución del
problema.
Durante esta primera fase de análisis es muy importante establecer correctamente las restric-
ciones en aquellos datos de entrada que las requieran, ya que estas deberán ser comprobadas por el
programador durante la última fase de codificación del algoritmo para que el programa no permita
la utilización de datos no válidos por parte del usuario.
Por último, además de especificar los datos de entrada y salida, durante esta fase es recomenda-
ble que el programador añada un apartado denominado comentarios, en el que describa cualquier
información que le sea útil para el posterior diseño del algoritmo y codificación como, por ejem-
plo, la utilización de posibles variables auxiliares necesarias para la resolución del problema o la
descripción de aspectos a tener en cuenta a la hora de establecer la solución.
A continuación se muestra un ejemplo del resultado de la fase de análisis para resolver el
problema de “obtener el área y el perímetro de un círculo”:
DATOS DE ENTRADA
Id Descripción Restricciones
radio Radio del círculo en centímetros >0
PI Constante π para calcular área y perímetro. pi = 3, 1416 -
DATOS DE SALIDA
Id Descripción
area Área del círculo en cm2
long Perímetro del círculo en cm
67
Comentarios
Diseñar un algoritmo puede ser una tarea difícil y su aprendizaje no es inmediato, ya que requiere
una buena dosis de experiencia y creatividad. Aprender a diseñar algoritmos correctamente se
considera esencial en la práctica de la programación, más incluso que el conocimiento específico
del lenguaje de programación más reciente. El algoritmo determinará el método de resolución del
problema, y si dicho método se ha desarrollado adecuadamente, su traducción a un lenguaje de
programación u otro es una cuestión menor.
Un algoritmo es independiente tanto del lenguaje de programación que vaya a utilizarse como
del computador donde se ejecute, de ahí su gran importancia. Como ya se ha explicado en la
Sección 2.1, para diseñar algoritmos correctamente hay que cumplir los requisitos relacionados con
las propiedades que debe poseer todo buen algoritmo:
Indicar el orden de realización de cada instrucción o paso definido para resolver el problema.
El algoritmo debe terminar siempre, independientemente de los valores de los datos de entrada
o de las acciones que realice el usuario.
Por ejemplo, se desea realizar un programa informático que muestre por pantalla si un número
introducido por teclado es primo o no. Un posible razonamiento para diseñar el algoritmo que
resuelve dicho problema podría ser: “Del análisis del hecho de que un número N es primo si sólo
puede dividirse por sí mismo y por la unidad, un método que nos puede dar la solución sería dividir
sucesivamente el número por 2, 3, 4..., etc. y, según el resultado, podríamos resolver el problema”.
Para diseñar el algoritmo que representa el método propuesto habría que establecer una serie de
pasos o instrucciones a realizar (que no sean ambiguas) e indicar el orden en el que se deben
ejecutar:
1. Inicio.
3. Poner X igual a 2 (X = 2), donde X será una variable que representará los posibles divisores
de N.
68
5. Si el resultado es entero entonces N no es primo y ya tenemos solución (saltar al paso 10 del
algoritmo). En caso contrario, continuar por el siguiente paso.
7. Si X es menor que N saltar al paso 4. En caso contrario, continuar por el siguiente paso.
10. Fin.
Un aspecto importante en esta fase de diseño del algoritmo es elegir un diseño que sea razona-
blemente aceptable ya que, como se ha comentado con anterioridad, un mismo problema se puede
resolver de muchas maneras, unas más eficientes que otras. Por ejemplo, el algoritmo diseñado para
el problema anterior sería claramente mejorable, ya que si N no es divisible por 2 no tiene mucho
sentido volverse a preguntar si lo es por 4, 6, 8...
Durante el diseño de un algoritmo es recomendable realizar comparaciones entre otros posi-
bles diseños del algoritmo que puedan resolver el mismo problema. La bondad o eficiencia de un
algoritmo puede medirse por dos factores principalmente:
1. El tiempo que necesita para proporcionar la solución, es decir, cómo de rápido es el algoritmo
en resolver el problema. Esta característica suele calcularse en base al tipo y número total de
instrucciones que han sido definidas en el algoritmo.
2. Los recursos que se necesitan para implantarlo. Este aspecto está relacionado con los recur-
sos hardware (memoria principalmente) necesarios para ejecutar el programa diseñado. Como
se verá más adelante, las variables y estructuras de datos utilizadas en los programas ocu-
pan memoria principal, por lo que cuantas más usemos en nuestro programa, más memoria
(recursos) necesitará el mismo para poder ejecutarse.
69
utiliza. Un ejemplo de optimización del algoritmo anterior sería declarar N como primo cuando X
supere a N/2.
Por último, puede haber ocasiones en que se planteen problemas realmente complejos en los que
sea difícil encontrar un método simple de resolución a primera vista. En estos casos es altamente
recomendable descomponer el problema inicial en subproblemas que sean más fáciles de resolver que
el original. Este método de resolución de problemas se le conoce como divide y vencerás y consiste
en convertir un problema complejo en otros más simples que, una vez resueltos, en su conjunto,
nos proporcionen la solución al problema original. Desde el punto de vista de la programación, a la
metodología de diseño de algoritmos que consiste en descomponer un problema en subproblemas
más simples para, a continuación, seguir dividiendo estos subproblemas en otros más simples, se le
denomina diseño descendente.
En esta asignatura, al igual que planteábamos para la fase de análisis, habrá que realizar dos
pasos obligatorios durante la fase de diseño del algoritmo:
– Fase 2.2: descripción del algoritmo, que consistirá en diseñar el algoritmo mediante
alguna de las herramientas que existen para ello (se explican en la siguiente sección). Como
ya se ha comentado, diseñar o describir un algoritmo consiste en establecer las instrucciones
o pasos que componen el método que resuelve el problema planteado.
Existen multitud de herramientas para describir y diseñar algoritmos pero en esta asignatura se
utilizarán principalmente dos: pseudocódigo y organigramas o diagramas de flujo.
El pseudocódigo consiste en una representación del código de un algoritmo en forma de ins-
trucciones en un lenguaje más o menos natural, utilizando construcciones básicas de programación,
asignaciones, instrucciones de entrada y salida, etc. Algunos ejemplos de las expresiones en pseu-
docódigo que se utilizarán en esta asignatura son:
70
leer: se usa para leer un dato del teclado.
si < c > entonces < aSi > si_no < aN o > fin_si: si se cumple la condición c se realiza la
acción aSi. En caso contrario se realiza la acción aNo.
mientras < c > hacer < a > fin_mientras: se repite la acción a mientras se cumpla la
condición c. Antes de repetir la acción a se comprueba primero que se cumple la condición c,
incluso la primera vez.
repetir < a > hasta_que < c >: se repite la acción a hasta que se cumple la condición c.
Primero se ejecuta la acción a y luego se verifica la condición c antes de volver a repetir el
proceso.
repetir < a > mientras < c >: se repite la acción a mientras se cumpla la condición c.
Primero se ejecuta la acción a y luego se verifica la condición c antes de volver a repetir el
proceso.
para < v >←< i > hasta < j > hacer < a > fin_para: se repite la acción a desde i hasta j
veces.
El Algoritmo 2.1 muestra un ejemplo en pseudocódigo del algoritmo que resuelve la suma de los
números pares comprendidos entre 2 y 100:
variables
suma , numero : e n t e r o
inicio
suma ← 2
numero ← 4
repetir
suma ← suma + numero
numero ← numero + 2
m i e n t r a s ( numero <=100)
e s c r i b i r ( suma )
fin
Algoritmo 2.1: Pseudocódigo del algoritmo que suma los números pares entre 2 y 100
71
Por otro lado, los organigramas o diagramas de flujo describen los pasos de un algoritmo
mediante un gráfico de cajas y flechas que representan acciones y flujos de ejecución del programa.
Las construcciones básicas y símbolos que se utilizarán para los organigramas que realicemos en
esta asignatura se muestran en la Figura 2.5. Entre las acciones que se pueden representar en un
organigrama se encuentran las asignaciones, entradas y salidas de datos, llamadas a otros módulos,
bifurcaciones condicionales, etc. La Figura 2.6 muestra el organigrama del algoritmo definido para
resolver la suma de los números pares comprendidos entre 2 y 100.
inicio sí no
E/S acción/es condición
fin
inicio
suma ← 2
numero ← 4
sí
numero<=100
no
escribir suma
fin
Figura 2.6: Organigrama del algoritmo que suma los números pares comprendidos entre 2 y 100
Los diagramas de flujo se utlizan mucho en educación porque tienen una gran carga lógica,
ya que se basan en sentencias verdadero/falso. Están muy relacionados con las matemáticas y la
programación pero para crearlos correctamente también se requiere de una completa comprensión
del problema, incluyendo una parte de abstracción. Es importante repetir que un diagrama de flujo
72
es una representación gráfica de un proceso, y algo que para muchos es más fácil de entender que
una descripción textual. Existen multitud de herramientas digitales para crear diagramas de flujo,
a continuación se exponen algunas:
Draw.io es una aplicación que se ejecuta en el navegador (no requiere instalación de ningún
tipo de software en el equipo), gratuita y compatible con servicios como Dropbox, Google
Drive o OneDrive.
Dia (Diagram Editor) es una opción un poco más compleja que Draw.io y con posibilidades
de software profesional, pero gratuita e instalable.
Una vez que el algoritmo está diseñado y representado, se debe pasar a la última fase de programa-
ción o codificación del mismo mediante el computador. Una vez obtenido el algoritmo, la realización
de esta última fase es prácticamente trivial para cualquier tipo de lenguaje de programación que
se utilice. Esta fase se descompone a su vez en las siguientes tareas:
– Fase 3.1: codificación del algoritmo en un programa. Es la conversión de los pasos del
algoritmo a las instrucciones equivalentes en un determinado lenguaje de programación. Como
ya se ha comentado anteriormente, el algoritmo escrito en un lenguaje de programación se
denomina programa, código fuente o, simplemente, código.
– Fase 3.2: ejecución y comprobación del programa. Esta última tarea consistirá en ejecu-
tar el programa generado y validar los resultados proporcionados por el mismo, comprobando
que se corresponden con los establecidos durante el análisis del problema.
Tras la codificación del programa, este deberá ejecutarse en un computador. El resultado de esta
primera ejecución es incierto, ya que existe una alta posibilidad de que aparezcan errores, bien
en la codificación o bien en el diseño del propio algoritmo. Por tanto, el paso siguiente consiste en
comprobar el correcto funcionamiento del programa y en asegurarse, en la medida de lo posible, de la
validez de los resultados que proporciona. La Figura 2.7 muestra la codificación en lenguaje Python
del algoritmo descrito para resolver el problema de la suma de los números pares comprendidos
entre 2 y 100.
Es importante señalar que durante el proceso de programación que resuelve un problema se debe
separar el diseño del algoritmo de su posterior implementación en un lenguaje de programación
73
Figura 2.7: Codificación en lenguaje Python del algoritmo que suma los números pares comprendidos
entre 2 y 100
específico. Por ello se distingue entre el concepto más general de programación y el más particular
de codificación, que depende del lenguaje de programación utilizado. Al llegar a este punto, se
supone que el programador conoce al menos uno de estos lenguajes y este es el momento en el que
tiene que mostrar sus habilidades para efectuar una codificación lo más correcta y eficiente posible.
Imperativo. Se basa en dar instrucciones al ordenador de cómo hacer algo, es decir, des-
cribe cómo debe realizarse el cálculo, no el por qué. Un cómputo consiste en una serie de
sentencias, ejecutadas según un control de flujo explícito, que modifican el estado del pro-
grama. Las variables son celdas de memoria que contienen datos (o referencias), pueden ser
modificadas, y representan el estado del programa. La sentencia principal es la asignación.
Asociados al paradigma imperativo se encuentran los paradigmas procedural o procedi-
mental, laprogramación modular, la programación estructurada, y la programación
orientada a objetos (POO). El lenguaje más representativo del paradigma imperativo sería
FORTRAN-77, junto con COBOL, BASIC, PASCAL, C y ADA. También lo implementan
74
otros más recientes como Java, C++, Perl o Python, pero estos se consideran sucedaneos de
los primeros.
3. Todo programa se puede implementar utilizando alguna de las siguientes estructuras (o com-
binación de ellas), conocidas como estructuras de control:
75
de ella que otros. En general, no existe ninguna necesidad de utilizarla, ya que cualquier algoritmo
o programa escrito con instrucciones ir_a se puede reescribir de forma que lleve a cabo las mismas
tareas prescindiendo de bifurcaciones incondicionales. Por tanto, para esta asignatura evitaremos el
uso de las bifurcaciones incondicionales porque hacen que los programas sean menos legibles para
el programador.
El objetivo que subyace bajo el paradigma de la programación modular es la idea que ya se introdujo
en la Sección 2.2.2 sobre el diseño de algoritmos: divide y vencerás. La programación modular utiliza
como metodología principal el dividir el problema original en pequeños subproblemas que puedan
resolverse de forma independiente. Por regla general el hecho de conseguir dividir el problema
principal en pequeños subproblemas ayuda a dar con la solución con mayor facilidad, ya que resulta
más factible resolver problemas de menor tamaño. Esta descomposición en pequeños subprogramas
se conoce también como desarrollo descendente o metodología top-down.
El hecho de dividir el problema original en subproblemas permite crear una solución (progra-
ma) para cada subproblema. A esos subprogramas también se les denomina en programación
módulos, y tienen la ventaja de ser independientes unos de otros, es decir, se comportan como
subprogramas independientes que tendrán unos datos de entrada, un procesamiento para realizar
alguna tarea concreta (subalgoritmo) y unos datos de salida. Por tanto, el programar así va a per-
mitir una verificación y validación aislada en cada uno, facilitando así la creación de programas
más complejos. Es por ello que la programación modular tiene las siguientes ventajas:
Aunque se verá con más detalle en el tema dedicado exclusivamente a programación modular,
cualquier módulo o subprograma que se haya creado y esté bien definido se puede invocar (lla-
mar, utilizar) desde el programa principal. Cualquier módulo en programación modular tiene las
siguientes características:
76
Debe tener un identificador representativo, de manera que con el simple hecho de leerlo nos
de una ligera idea de la acción o tarea concreta que realiza.
Cuando termina de ejecutarse siempre debe retornar su control al módulo llamador, que
normalmente será el programa principal, aunque en programación es habitual que un módulo
pueda invocar a su vez otros módulos.
Los módulos deben ser de un tamaño pequeño, es decir, resolver determinadas tareas concre-
tas. No tiene sentido programar módulos que resuelvan muchas tareas o tareas muy complejas
ya que se alejaría del objetivo de la programación modular.
Debe proporcionar cierta abstracción, es decir, ocultar lo que ocurre dentro de él. Desde el
punto de vista del programador un módulo que está bien programado debe verse como una
caja negra a la que le pasamos unos datos de entrada, realiza una acción y sabemos lo que
hace (nos devuelve un resultado, por ejemplo), sin importarnos cómo lo hace.
Los módulos pueden aceptar parámetros o argumentos que sirven para que el módulo invo-
cador (normalmente el programa principal) les proporcione información extra (datos de entrada)
o reciba en ellos información como resultado, además del valor de retorno del módulo (datos de
salida). Los parámetros de un módulo se pueden ver como una lista de variables (con sus corres-
pondientes identificadores y tipo), a la que el módulo llamador asigna unos valores en el momento
de la invocación. Para el propio módulo los parámetros se comportan como variables locales que
toman inicialmente los valores asignados en la invocación. El Algoritmo 2.2 muestra un ejemplo
de un módulo llamado Factorial que calcula el factorial de un valor N (de tipo entero) que recibe
como parámetro desde el programa principal.
f u n c i o n F a c t o r i a l (N : e n t e r o )
fact = 1
para I desde N hasta 2 hacer
fact = fact * N
fin_para
devolver fact
fin_funcion
// programa p r i n c i p a l
variables
valor , x : entero
inicio
77
leer ( valor )
x = Factorial ( valor )
e s c r i b i r ( " El f a c t o r i a l de " , v a l o r , " e s " , x )
fin
Como se estudiará en el tema dedicado a programación modular, un módulo puede ser de dos
tipos: una función o un procedimiento. Conceptualmente, la diferencia entre función y procedimiento
en programación radica en que la función es un módulo que siempre devuelve un valor o resultado al
módulo llamador mediante una instrucción devolver (return) , mientras que el procedimiento realiza
una serie de instrucciones sin devolver directamente ningún valor al módulo llamador, aunque se
pueden implementar procedimientos (y funciones) que devuelvan valores a través de sus parámetros,
como se verá más adelante.
Para terminar de explicar brevemente los conceptos de procedimiento y función imagine que
quiere implementar un módulo que calcule el doble de un número. Si no nos dicen nada, lo más
natural sería definir una función que devolviese el doble del número que reciba como parámetro de
entrada, pero a otro programador se le podría ocurrir implementar un procedimiento que mostrase
por pantalla el doble del número recibido como parámetro, sin devolver nada al módulo llamador.
En este caso tendríamos dos módulos, uno que es una función y otro que es un procedimiento, que
realizan la misma tarea (calcular el doble de un número que reciben como parámetro) pero uno lo
devuelve como resultado (la función) y el otro lo muestra por pantalla (el procedimiento).
78
esto es a lo que se denomina en programación el caso trivial o caso de parada de un algoritmo
recursivo. Por ejemplo, en el caso del cálculo del factorial, el caso trivial sería el factorial de 1, que
es 1. De este modo, podemos definir de forma recursiva el módulo que calcula el factorial, tal y
como se muestra en el Algoritmo 2.3.
f u n c i o n F a c t o r i a l (N)
s i (N=1) e n t o n c e s
d e v o l v e r 1 // c a s o t r i v i a l
si_no
d e v o l v e r (N * F a c t o r i a l (N- 1 ) ) // l l a m a d a r e c u r s i v a
fin_si
fin_funcion
// programa p r i n c i p a l
variables
valor , x : entero
inicio
leer ( valor )
x = Factorial ( valor )
e s c r i b i r ( " El f a c t o r i a l de " , v a l o r , " e s " , x )
fin
Algoritmo 2.3: Pseudocódigo para el módulo que calcula de forma recursiva el factorial de un
número
Para demostrar cómo esta versión recursiva de la función factorial calcula n!, consideremos el
caso sencillo de n = 3. La Figura 2.8 representa gráficamente los pasos sucesivos para obtener dicho
cálculo.
Por último, recordar que la solución recursiva no es la única, ya que siempre será posible utilizar
una estructura repetitiva en lugar del planteamiento recursivo. No obstante, para algunos casos, la
solución recursiva es la solución natural del problema, y por tanto la más clara, como ocurre en el
caso del cálculo del factorial de un número.
79
Figura 2.8: Secuencia para el cálculo del factorial de n de manera recursiva
1. Definición (análisis, especificación de requisitos). Durante esta fase se realiza una descripción
detallada del programa que se va a realizar, qué problema resuelve, qué funciones o tareas
80
Figura 2.9: Ciclo de vida del software lineal o en cascada
debe realizar, qué datos maneja, qué restricciones existen, etc. En esta fase también se indican
los recursos necesarios, costes y el plan de trabajo.
3. Mantenimiento (mejoras y correcciones). Durante la última fase del ciclo de vida del soft-
ware se realizan las correcciones, mejoras y adaptaciones necesarias de la versión original del
programa, es decir, lo que se conoce como mantenimiento.
El inconveniente principal del ciclo de vida lineal o en cascada es que el cliente no llega a ver
una versión del programa en funcionamiento hasta prácticamente el final de las etapas. Por eso,
existe también otro modelo de ciclo de vida del software basado en prototipos, en el que el cliente
interactúa desde el principio con el proceso de desarrollo del programa, ya que se le van haciendo
pequeñas entregas del programa (prototipos) que permiten recibir un feedback continuo del cliente
y así ir mejorando el producto final desde el comienzo de su creación.
81