Está en la página 1de 37

Tema 3

Elementos básicos de la Programación


Imperativa. Estructuras de Control

3.1 Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

3.2 Elementos básicos de un programa . . . . . . . . . . . . . . . . . . . . . . 83

3.2.1 Identificadores, constantes y variables . . . . . . . . . . . . . . . . . . . . . 84

3.2.2 Instrucciones y tipos de instrucciones . . . . . . . . . . . . . . . . . . . . . 88

3.2.3 Otros elementos de un programa . . . . . . . . . . . . . . . . . . . . . . . . 92

3.3 Tipos simples de dato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94

3.4 El tipo cadena . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

3.5 Estructuras de control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

3.5.1 Estructura secuencial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97

3.5.2 Estructuras selectivas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100

3.5.3 Estructuras iterativas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105

3.6 Escritura de algoritmos y programas en pseudocódigo . . . . . . . . . . 116

3.6.1 Depuración y control de errores . . . . . . . . . . . . . . . . . . . . . . . . . 118

En este capítulo se describe la estructura general de un programa, cuáles son sus partes consti-
tutivas, qué tipos de datos se pueden manejar, qué tipos de instrucciones existen y, en general,
los elementos básicos de cualquier programa. Para finalizar la primera parte, se explica cómo debe
realizarse la escritura de algoritmos y programas en general. En la segunda parte del tema se pre-
sentan las estructuras de control básicas del paradigma de programación estructurada: estructuras
secuenciales, de selección e iterativas. Se explicarán cuáles son sus funciones en los programas, cómo
se utilizan y cómo se representan tanto en pseudocódigo como en organigramas.

82
3.1 Introducción

En temas anteriores ya se ha introducido el concepto de programa de computador que, aplicado


al ámbito de la programación, puede definirse como una lista de instrucciones u órdenes elementales
que producirán la ejecución de una determinada tarea. El proceso de programación es, por consi-
guiente, un proceso de solución de problemas y el desarrollo de un programa requiere las siguientes
fases:

1. Definición y análisis del problema.

2. Diseño del algoritmo (pseudocódigo, organigrama...).

3. Codificación del programa.

4. Depuración y verificación del programa.

5. Documentación y mantenimiento.

Conceptualmente un programa se considera una caja negra (ver Figura 3.1) en la que se trans-
forman los datos de entrada del programa en salidas o resultados a través de una serie de códigos e
instrucciones. Al proceso de introducir la información de entrada (datos) en la memoria del compu-
tador se le denomina entrada de datos, operación de lectura o acción de leer. Las salidas de datos
o resultados de un programa se deben presentar en dispositivos periféricos de salida (normalmente
la pantalla). A la operación de salida de datos se le conoce también como escritura o acción de
escribir.

Entrada PROGRAMA Salida


(algoritmo de resolución)

Figura 3.1: Bloques de un programa

3.2 Elementos básicos de un programa

Ya se ha recalcado la importancia que tiene en programación separar el diseño del algoritmo de


su implementación en un determinado lenguaje. Por ello, se debe distinguir claramente entre los
conceptos de programación y el medio en que ellos se implementan en un lenguaje específico. Sin
embargo, una vez que se comprendan los conceptos de programación, cómo utilizarlos, el aprendizaje
de un nuevo lenguaje de programación es relativamente fácil.

83
Los lenguajes de programación (como cualquier otro tipo de lenguaje) tienen elementos bá-
sicos que se utilizan como bloques constructivos, así como reglas establecidas mediante las que
esos elementos se pueden combinar. Al conjunto de esas reglas se le denomina sintaxis del len-
guaje. Cualquier computador puede interpretar únicamente aquellas instrucciones de un lenguaje
que sean sintácticamente correctas, es decir, que estén bien construidas conforme a las reglas del
propio lenguaje. De esta forma, los programas que contienen errores de sintaxis son rechazados
automáticamente por el computador debido a que no consiguen entender lo que se ha programado
por no haber respetado las reglas del lenguaje.
Los elementos básicos constitutivos de un programa o algoritmo son los siguientes:

Palabras reservadas (inicio, fin, si, entonces, ..., for, if, ...).

Identificadores (se utilizan para identificar objetos en el código del programa).

Constantes.

Variables.

Instrucciones (expresiones aritméticas, lógicas, ...).

Además de estos elementos básicos existen otros elementos que forman parte de los programas,
cuya comprensión y funcionamiento será vital para el correcto diseño de un algoritmo y naturalmen-
te la codificación del programa. Algunos de estos elementos son: bucles, contadores, acumuladores,
interruptores o banderas, etc. Todos ellos se explican con más detalle a continuación.

3.2.1 Identificadores, constantes y variables

En programación un identificador es un nombre que se le da a cualquier objeto de nuestro algo-


ritmo o programa. Cualquier identificador en programación debe cumplir habitualmente dos reglas
básicas en la mayoría de lenguajes:

1. Debe ser alfanumérico, es decir, que esté formado por letras y dígitos exclusivamente. El que
las letras sean mayúsculas o minúsculas es indiferente para que el identificador sea válido.

2. Debe comenzar siempre por una letra.

Según estas reglas, ejemplos de identificadores válidos serían: nombre, DIV, año2002, Asignaturas-
Matriculadas, ... Por el contrario, ejemplos de identificadores no válidos serían: 4curso, ?comprobado,
2002, Dia del Mes, ...
En programación los identificadores escogidos deben ser significativos, es decir, los nom-
bres que elijamos para determinados objetos de nuestro programa deben hacer referencia al dato
que representan. Con ello facilitaremos la lectura y comprensión de los algoritmos y programas que

84
diseñemos. Ejemplos de buenos identificadores podrían ser edad, para almacenar la edad de una
persona, o IVA, para almacenar el porcentaje de IVA. El simple hecho de utilizar identificadores
significativos facilitaría la compresión del programa puesto que nos podemos hacer una ligera idea
de lo que contienen los objetos al leer su identificador.
Una constante o literal en programación es un caso especial de identificador cuyo valor no
varía durante la ejecución del programa. La definición de una constante consiste, por tanto, en
asociar un identificador con un determinado valor, valor que permanecerá inalterable durante toda
la ejecución del programa. Desde el punto de vista de la eficiencia de los programas, la utilización
de constantes no ocupa espacio en memoria, ya que el propio compilador o intérprete sustituye
cada aparición del identificador de la constante por su valor correspondiente durante el proceso de
traducción o interpretación. En programación para definir los identificadores de constantes se suele
seguir la regla de utilizar identificadores con todas las letras en mayúscula.
La definición o declaración de constantes durante el diseño de algoritmos mediante pseudo-
código se realiza siempre antes del comienzo del programa, comenzando con la palabra reservada
constantes y, a continuación, asociando cada identificador de constante con su valor separados por
el signo igual, tal y como se muestra en el extracto del Algoritmo 3.1.

constantes
< id_const_1 > = < valor_const_1 >
< id_const_2 > = < valor_const_2 >
...

Algoritmo 3.1: Declaración de constantes en pseudocódigo

Por ejemplo, si en nuestro algoritmo queremos definir una constante con identificador PI y valor
3,1416, en pseudocódigo se haría tal y como se representa en el extracto del Algoritmo 3.2.

85
constantes
PI = 3 , 1 4 1 6
variables
valor , x : entero
inicio
leer ( valor )
x ← valor + P I
...
fin

Algoritmo 3.2: Declaración de la constante PI en pseudocódigo

Una de las ventajas que proporciona el uso de constantes en nuestros programas es la fácil
modificación del código del programa, ya que si es necesario modificar un dato considerado como
constante en el programa, la definición de una constante permite hacerlo en un solo paso en lugar
de tener que explorar todo el código en busca de literales que hagan referencia a ese dato. Por
ejemplo, si el IVA variase, bastaría con modificar el valor asociado a la constante en la definición
para que dicha modificación actuara sobre todas las referencias incluidas en el programa.

Por último, el concepto de variable está directamente asociado a la memoria del computador.
Una variable es un caso especial de identificador cuyo valor puede variar, es decir, una va-
riable representa un valor almacenado en memoria que se puede modificar durante la ejecución del
programa y/o conservar para ser usado tantas veces como se desee. Cabe aclarar que el concepto
de variable en el ámbito de la programación es distinto al del ámbito de las Matemáticas, ya que en
programación las variables no representan incógnitas sino datos almacenados en memoria principal.

En la mayoría de lenguajes de programación compilados (C y Java, por ejemplo) es obligato-


rio declarar una variable antes de utilizarla en el programa. Declarar una variable consiste
en definir el identificador de la variable y su tipo, es decir, qué conjunto o tipo de valores podrá
almacenar durante la ejecución del programa. El proceso de declaración de variables servirá al com-
pilador para reservar en memoria el espacio adecuado para los tipos de valor que puede almacenar
la variable.

La declaración de variables durante el diseño de algoritmos mediante pseudocódigo se rea-


liza posteriormente a la declaración de constantes y siempre antes del comienzo del programa,
comenzando con la palabra reservada variables y, a continuación, asociando cada identificador de
variable con su tipo separados por el signo dos puntos (:), tal y como se representa en el extracto
del Algoritmo 3.3.

86
variables
< id_variable_1 > : < tipo_variable_1 >
< id_variable_2 > : < tipo_variable_2 >
...

Algoritmo 3.3: Declaración de variables en pseudocódigo

Como se explica más adelante en el apartado dedicado a los tipos simples de dato, para expresar
los tipos de las variables en pseudocódigo se utilizarán palabras reservadas como entero, real,
caracter... El extracto del Algoritmo 3.4 muestra un ejemplo de la declaración de dos variables de
tipo entero con identificadores suma y numero. Como se puede observar, está permitido declarar
más de una variable del mismo tipo en la misma línea.

variables
suma , numero : e n t e r o
inicio
l e e r ( numero )
suma ← numero + 10
...
fin

Algoritmo 3.4: Ejemplo de declaración de variables en pseudocódigo

Como norma en esta asignatura para el diseño de algoritmos y la codificación de programas,


todas las variables deberán declararse antes de ser utilizadas, permitiendo que varias
variables de un mismo tipo puedan ser declaradas en la misma instrucción de declaración (tal y como
se muestra en el ejemplo anterior). Además, es una buena práctica de programación inicializar
las variables antes de usarlas en el programa. La inicialización de una variable significa darle
un cierto valor por primera vez antes de usarla, y suele hacerse al comenzar el programa antes
de pedir los datos de entrada al usuario. Esta práctica es obligatoria hacerla cuando una variable
aparece como operando en la parte derecha de una expresión de asignación, siempre que no se haya
utilizado antes.

87
3.2.2 Instrucciones y tipos de instrucciones

El proceso de diseño del algoritmo y, posteriormente, de codificación del programa consiste en


definir las acciones o instrucciones que resolverán el problema. Las instrucciones disponibles en un
lenguaje de programación dependen del tipo de lenguaje, pero existen una serie de instrucciones
(acciones) básicas que se pueden implementar de modo general en un algoritmo y que esencialmente
soportan todos los lenguajes. Estas instrucciones básicas son:

Instrucciones de inicio/fin. Se utilizan para indicar el comienzo y el final del algoritmo.


Como ya se ha explicado con anterioridad, en pseudocódigo y en organigrama se representan
con las palabras inicio y fin.

Instrucciones de asignación. La instrucción de asignación se utiliza fundamentalmente


para dar un valor a una variable. Es importante recalcar que en pseudocódigo la instrucción
de asignación se representa mediante el operador ← (y no mediante el símbolo =). El formato
general de una operación de asignación en pseudocódigo es:

id_variable ← expresion

Aunque en la mayoría de lenguajes el operador de asignación es el símbolo =, en esta asig-


natura se utilizará el operador ← en la descripción del algoritmo para evitar ambigüedades,
dejando el uso del símbolo = exclusivamente para el operador de comparación es igual a, que
habitualmente se utiliza en instrucciones de decisión. Por tanto, la operación de asignación
v ← 5 significa que a la variable v se le ha asignado el valor 5. Conviene recordar que la acción
de asignar es destructiva, es decir, el valor que tuviera la variable antes de la asignación se
pierde y se reemplaza por el nuevo valor. Según sea el tipo de expresión que aparece en la
parte derecha, las acciones de asignación se clasifican en:

• Asignación aritmética. Las expresiones en las operaciones de asignación son aritméticas.


Por ejemplo: v ← 4 + (3,5 ∗ 2). Tras evaluar la operación, v tomará el valor 11.

• Asignación lógica. La expresión que se evalúa en la operación de asignación es lógica.


Por ejemplo: x ← 8 < 5. Tras evaluar la operación, x tomará el valor falso.

• Asignación de caracteres. La expresión que se evalúa es de tipo cadena de caracteres.


Por ejemplo: y ← ’12 de octubre’. Tras evaluar la operación, se asigna la cadena de
caracteres 12 de octubre a la variable y.

Un caso especial de asignación será aquel en el que aparece la misma variable tanto en la parte
izquierda como en la parte derecha de la sentencia. En cualquier caso, todos los lenguajes
funcionan de idéntica manera: la parte derecha siempre es evaluada en primer lugar

88
y, una vez hallado su valor, este se asigna a la posición de memoria que representa la variable
de la parte izquierda. Por ejemplo, si tenemos la asignación NumDias ← NumDias+1, el valor
final de la variable NumDias será el que tuviera la variable antes de llegar a esa instrucción
más 1.

Otro aspecto a tener muy en cuenta en las asignaciones y en las expresiones que se utilizan en
las instrucciones de asignación es la compatibilidad de los tipos de datos que aparecen
en ellas, de manera que, de forma general, sólo pueden ser operadas entre sí variables o
expresiones pertenecientes a un mismo tipo de dato.

Instrucciones de lectura/escritura. Las instrucciones de lectura permiten leer determina-


dos valores desde dispositivos de entrada (teclado normalmente) y asignarlos a determinadas
variables, mientras que las instrucciones de escritura permiten escribir cualquier valor en un
dispositivo de salida (pantalla normalmente). A la hora de describir algoritmos mediante pseu-
docódigo, ya se explicó que las acciones de lectura y escritura se representan de la siguiente
manera:

leer (id_var1, id_var2, ...)


escribir (expr1, expr2, ...)

Así, por ejemplo, la instrucción leer (a, b, c) representa la lectura de tres valores de entrada
desde teclado que se asignan a las variables a, b y c. Por otro lado, la instrucción escribir
(’hola que tal’) visualizaría en pantalla el mensaje hola que tal.

Instrucciones de selección o decisión. Cuando el programador desea especificar dos o más


caminos alternativos en un algoritmo o programa se deben utilizar instrucciones de decisión
o selección. Una instrucción de selección evalúa una condición y en función del resultado de
esa condición el flujo de ejecución del algoritmo seguirá un camino u otro. En la Sección 3.5
se estudiarán con mayor profundidad los tipos de estructuras de selección que son básicas en
programación.

3.2.2.1 Expresiones aritméticas

Las expresiones aritméticas son análogas a las fórmulas matemáticas, es decir, están formadas
principalmente por una combinación de operandos y operadores. En una expresión aritmética las
variables y constantes implicadas (operandos) son numéricas (de tipo real o entero) y los operadores
son los aritméticos. En esta asignatura se utilizarán en pseudocódigo los operadores aritméticos que
se muestran en la Tabla 3.1:
Los operadores se utilizan de igual forma que en matemáticas pero, sin embargo, no todos los
operadores aritméticos existen en todos los lenguajes de programación. Por ejemplo, en FORTRAN

89
+ suma
- resta
* multiplicación
/ división real
div división entera
** exponenciación
% módulo (resto)

Tabla 3.1: Operadores aritméticos en pseudocódigo

no existe div ni el operador módulo %, y el operador exponenciación es diferente según el lenguaje


(en FORTRAN y Python es **, mientras que en C es ˆ).
Por regla general en todos los lenguajes los cálculos que implican operandos de tipo real o entero
suelen dar normalmente resultados del mismo tipo, siempre que todos los operandos de la expresión
sean del mismo tipo. Por ejemplo, el producto de operandos reales produce como resultado un real.
También es posible que en una expresión aritmética concurran varios operadores. En ese caso, el
orden de aplicación de los mismos puede influir en el resultado final. Es lo que se conoce como
orden de precedencia o reglas de prioridad de los operadores. Por ejemplo, en la expresión
aritmética 6 + 4/2 el resultado será 5 u 8 dependiendo del orden en que se apliquen los operadores
+ y /. Por lo tanto, es necesario establecer unas reglas para determinar qué operadores se aplican
primero en estos casos. El orden de precedencia de operadores que normalmente suelen aplicar
todos los lenguajes de programación es el siguiente:

1. Operador exponencial.

2. Operadores de multiplicación y división real.

3. Operadores de división entera y módulo.

4. Operadores de suma y resta.

En caso de coincidir varios operadores de igual prioridad en una expresión, el orden de prioridad
que se aplica es de izquierda a derecha, excepto para los operadores unarios que será de derecha
a izquierda. Por ejemplo, el resultado de 4 + 8/ − 2 ∗ 6 sería -20. Así mismo, los paréntesis tienen
prioridad sobre el resto de operaciones, de forma que las expresiones encerradas entre paréntesis
serán las primeras que se evalúen. Si existen paréntesis anidados (interiores unos a otros), las
operaciones internas se evalúan primero. Por ejemplo, el resultado de (4 ∗ (1 + 8))/(−2 ∗ 6) sería -3.

3.2.2.2 Expresiones lógicas

Una expresión lógica es un tipo de expresión cuyo valor o resultado es siempre verdadero o falso.
También se les conoce como expresiones booleanas en honor al matemático británico George
Boole, que desarrolló el Álgebra lógica de Boole. Las expresiones lógicas se forman combinando

90
cualquier tipo de dato (ya sea constante o variable), operadores lógicos y operadores relacionales o
de comparación. La Tabla 3.2 y la Tabla 3.3 muestran los operadores lógicos y relacionales respecti-
vamente que se utilizarán en esta asignatura para las descripciones de algoritmos en pseudocódigo.

< menor que


> mayor que
y operador lógico Y (AND)
= igual que
o operador lógico O (OR)
<= menor o igual que
no operador de negación (NOT )
>= mayor o igual que
Tabla 3.2: Operadores lógicos utilizados != distinto de
en pseudocódigo
Tabla 3.3: Operadores relacionales utilizados en pseudocó-
digo

Con los operadores relacionales se pueden realizar comparaciones de valores de tipo numérico o
carácter, permitiendo así poder expresar cualquier condición o decisión que haya que controlar en
nuestro algoritmo. El formato general para representar una comparación es:

<expresion1> operador_relacional <expresion2>

y el resultado de dicha operación siempre será verdadero o falso. Así, por ejemplo, si x = 4 e
y = 3, entonces la expresión lógica x > y será verdad, mientras que la expresión aritmético-lógica
(x − 2) < (y − 4) será falsa.
Los operadores de relación se pueden aplicar a cualquiera de los cuatro tipos de dato básicos
(entero, real, lógico o caracter). Para realizar comparaciones de datos de tipo caracter se requiere
una secuencia de ordenación de los caracteres similar al orden creciente o decreciente. Esta or-
denación suele ser alfabética, tanto para los caracteres en mayúscula como para los caracteres en
minúscula. Cuando se consideran caracteres mixtos (letras y números, por ejemplo) se debe recurrir
a un código normalizado como es el código ASCII para hacer la comparación. Así, el resultado de
la comparación será el que resulte de comparar los valores ASCII asociados a los caracteres impli-
cados. Por ejemplo, la expresión lógica ’A’ <= ’B’ dará como resultado verdad ya que el código
ASCII del carácter A (65) es menor que el del carácter B (66), mientras que la expresión lógica
’X’ = ’Z’ dará como resultado falso, ya que el código ASCII del carácter X (88) es distinto del
código ASCII del carácter Z (90).
Cuando en una expresión lógica interviene algún operador lógico (y, o, no) el resultado final de
dicha expresión se ha de calcular según unas tablas propuestas por el Álgebra de Boole conocidas
como tablas de verdad (ver Tabla 3.4). Conocer de memoria las tablas de verdad es prácticamente
una obligación para toda persona que quiera diseñar cualquier algoritmo y, por ende, cualquier
programa informático.
Según se muestra en las tablas de verdad del Álgebra de Boole, la expresión lógica A y B será
verdad si y solo si A y B son verdad, mientras que la expresión lógica A o B será verdad cuando

91
A B AyB AoB
A no A verdad verdad verdad verdad
verdad falso verdad falso falso verdad
falso verdad falso verdad falso verdad
falso falso falso falso

Tabla 3.4: Tablas de verdad del Álgebra de Boole

alguno de los operandos (o A o B) sean verdad. En las expresiones lógicas se pueden mezclar
operadores relacionales y operadores lógicos. Así, por ejemplo, la expresión lógica (1 < 5) y (5 < 10)
será verdad, al igual que la expresión (5 > 10) o (’A’ < ’B’), ya que el operando de la derecha
representa una expresión lógica cierta (el carácter A es menor alfabéticamente que B según el
código ASCII).
De forma análoga a los operadores aritméticos, los operadores lógicos y los operadores relacio-
nales también tienen un orden de precedencia o reglas de prioridad:

Entre operadores lógicos, el de mayor precedencia es el no, luego el y y por último el o. Si


aparecen dos o más operadores lógicos iguales en una expresión, los operadores de negación
(no) se aplicarán de derecha a izquierda y los operadores y (AND) y o (OR) de izquierda a
derecha. Por ejemplo, si X = 4 e Y = 3, la expresión lógica (X > 3) o (Y > 5) y no(X = 4)
será verdad.

Entre operadores relacionales el orden de precedencia es el siguiente:

1. <, <=, >, >=

2. =, !=

3.2.3 Otros elementos de un programa

Además de todos los elementos básicos explicados con anterioridad (palabras reservadas, identi-
ficadores, constantes, variables e instrucciones), existen otros elementos que forman parte de los
programas cuya comprensión y funcionamiento será vital para el correcto diseño de un algoritmo
y, naturalmente, la codificación del mismo para crear el programa informático. Entre estos otros
elementos destacan:

Funciones internas. Las operaciones que se requieren en los programas exigen, en numerosas
ocasiones, un número determinado de operaciones especiales que no se pueden realizar con
los operadores aritméticos básicos explicados anteriormente como, por ejemplo, operaciones
de cálculo trigonométrico, logarítmico, etc. Por ello, en la mayoría de lenguajes, existen ya
implementadas una serie de funciones internas que permiten realizar este tipo de cálculos más
avanzados de forma sencilla. Algunos ejemplos de estas funciones en pseudocódigo son: abs(x)

92
para calcular el valor absoluto de x, cos(x), para calcular el coseno de x, ln(x), para calcular
el logaritmo neperiano de x, raiz2(x), para calcular la raíz cuadrada de x, etc.

Bucles. Un bucle o loop es un segmento de un algoritmo o programa cuyas instrucciones se


repiten un número determinado de veces mientras se cumple una determinada condición. A
cada paso o iteración del bucle (cuando se realizan todas las tareas) se comprueba la condición
(que puede ser verdadera o falsa). Si es falsa, vuelve a iterar el bucle.

Contadores. Los procesos repetitivos son la base del uso de las computadoras. En estos
procesos se necesita normalmente contar los sucesos o acciones internas del bucle, y una
forma de controlar un bucle es mediante un contador. Así, un contador será una variable cuyo
valor se incrementa o decrementa en una cantidad constante en cada iteración del bucle. Por
tanto, un contador en programación puede ser positivo (incrementos de uno en uno, de dos
en dos, etc.) o negativo (decrementos de uno en uno, de dos en dos, etc.).

Acumuladores. En programación se le denomina acumulador a una variable cuya misión es


ir almacenando cantidades que resultan de sumas sucesivas. Normalmente la representación
de la instrucción en la que interviene un acumulador es la siguiente: ac ← ac + v, donde v es
una variable y ac sería la variable que hace de acumulador. Su traducción al lenguaje natural
sería algo parecido a: “guarda en la variable ac lo que hubiera en ac más el valor de v”.

Banderas o interruptores. Una variable bandera (flag, en inglés) o interruptor puede tomar
diversos valores a lo largo de la ejecución del programa y permite comunicar información
sobre algo que ha cambiado durante la ejecución del mismo. Los interruptores suelen tomar
dos valores diferentes (0 o 1, sí o no, encendido o apagado, etc.), de ahí que este tipo de
variables se vean como variables de tipo lógico, aunque no tienen por qué almacenar valores
lógicos.

Instrucciones avanzadas de control de ejecución. En los lenguajes de programación


suelen existir instrucciones avanzadas que pueden ayudar en ocasiones a construir un código
más legible, si bien no conviene abusar de su uso porque se alejan del paradigma de progra-
mación estructurada en cuanto a que sirven para romper la secuencia lineal de los programas.
Estas instrucciones se utilizarán para pseudocódigo en esta asignatura tal y como se hace en
la mayoría de lenguajes, es decir, escritas en inglés:

• break. Rompe la ejecución de un bucle y continúa la ejecución por la primera instrucción


que haya inmediatamente después del bucle (termina el bucle prematuramente, aunque
no se cumpla la condición de salida).

• continue. Salta a la siguiente iteración del bucle, reevaluando la condición de conti-


nuidad del mismo. Es decir, termina la iteración del bucle prematuramente y salta a la

93
siguiente iteración (si la condición de continuidad sigue siendo cierta).

• return. Termina prematuramente la ejecución de un módulo, retornando la ejecución


al programa llamador (normalmente el módulo principal). Cuando se utiliza dentro de
un módulo esta instrucción suele ir acompañada del valor de retorno.

• exit. Termina prematuramente la ejecución del programa.

Comentarios. La documentación de un programa es el conjunto de información interna y


externa al programa que facilitará su posterior mantenimiento y actualización. La documen-
tación interna se realiza mediante comentarios significativos en el propio código fuente. Estos
comentarios se representan con diferentes notaciones, según el tipo de lenguaje de progra-
mación. En esta asignatura, para pseudocódigo, se utilizará la misma notación que para el
lenguaje Python:

• El símbolo almohadilla # para comentarios que ocupen una sola línea, situando el sím-
bolo justo delante del texto que forma parte del comentario.

• Triple comilla simple ”’ para comentarios que ocupen más de una línea, terminando el
comentario multilínea también con triple comilla simple.

3.3 Tipos simples de dato

En el primer tema se introdujeron brevemente los tipos simples de dato o tipos básicos que general-
mente se consideran en programación. En esta sección se explican de forma más detallada, poniendo
especial énfasis en la notación que se debe utilizar para representar los tipos en la descripción de
algoritmos mediante pseudocódigo y organigramas, y no mediante un lenguaje de programación
específico.
La mayoría de algoritmos que se diseñan manipulan datos que pertenecen a un conjunto o clase
de valores identificados mediante lo que se llama un tipo de dato. Por tanto, tipo de dato se
puede definir como el conjunto de valores que puede tomar ese dato. Así, un tipo de dato determina
el dominio de valores que puede tomar ese dato y qué tipo de operaciones se pueden realizar con
él. Los tipos simples de dato a considerar en esta asignatura cuando se diseñen algoritmos en
pseudocódigo son:

entero. Los números enteros son aquellos que no tienen decimales, tanto positivos como
negativos (además del cero). Mientras que en la mayoría de lenguajes de programación existen
diferentes subtipos del tipo entero, en pseudocódigo utilizaremos la palabra reservada entero
para referirnos a cualquier rango de valores enteros.

real. Los números reales son los que tienen decimales y, como en los enteros, pueden ser
positivos y negativos. En pseudocódigo utilizaremos la palabra reservada real para referirnos

94
a cualquier rango de valores reales aunque, al igual que para el tipo entero, la mayoría de
lenguajes proporcionan diferentes subtipos para números reales en función del rango repre-
sentado.

caracter. El tipo caracter (sin acento en pseudocódigo) consiste en el conjunto finito y


ordenado de los caracteres representables por el ordenador (en el caso de los PCs consiste en
el conjunto de caracteres incluido en la tabla de códigos ASCII). Un literal de tipo caracter
se representa encerrado entre comillas simples o apóstrofos. Ejemplos válidos de literales de
tipo caracter serían: ’a’, ’,’, ’>’, ’A’, ’3’... Ejemplos de literales de tipo caracter no válidos
serían: ’ab’, ’A, q,“b”...

logico. El tipo de dato logico (sin acento en pseudocódigo) se define mediante un conjunto de
dos únicos valores: True (verdadero) y False (falso). Como ya se ha explicado, estos valores
son especialmente importantes para las expresiones condicionales y los bucles.

En la Sección 3.2.1 se explicó cómo se deben declarar las variables de los algoritmos en pseu-
docódigo, de manera que cada identificador de variable se asocia con su tipo separado por el signo
dos puntos (:). Ahora que ya sabemos las palabras reservadas que se utilizarán para referenciar los
tipos básicos de datos, el extracto del Algoritmo 3.5 muestra un ejemplo de declaración de variables
para cada uno de los tipos simples de dato explicados.

variables
...
v1 , v2 : e n t e r o
x : real
c : caracter
bandera : l o g i c o
...
inicio
...
fin

Algoritmo 3.5: Ejemplo de declaración de variables de tipo simple en pseudocódigo

En el ejemplo del Algoritmo 3.5 se han declarado dos variables de tipo entero con identificadores v1
y v2, una variable de tipo real con identificador x, una variable de tipo caracter con identificador
c y una variable de tipo logico con identificador bandera.

95
3.4 El tipo cadena

Aunque el tipo cadena no se considera formalmente un tipo de dato simple (se definió como tipo
de dato estructurado y se estudiará en profundidad más adelante) se introduce brevemente en este
tema para que se pueda trabajar con información de este tipo desde un principio. No obstante,
algunos lenguajes de programación como Python tienen implementado el tipo cadena como tipo de
dato nativo, es decir, como si fuera un tipo básico propio del lenguaje como un tipo entero o un
tipo real, por ejemplo.
Una cadena (string) de caracteres es una sucesión de caracteres (incluido el espacio en blanco)
que se encuentran delimitados por comillas simples o dobles, dependiendo del tipo de lenguaje
de programación. La longitud de una cadena es el número de caracteres comprendidos entre los
limitadores, y la cadena que no contiene ningún carácter se denomina cadena vacía o nula, siendo
su longitud 0. La cadena vacía no se debe confundir con una cadena compuesta sólo de espacios
en blanco, ya que esta tendrá como longitud el número de espacios en blanco de la misma, ya que
el espacio en blanco es en sí un carácter. En esta asignatura utilizaremos como delimitador para
representar cadenas en pseudocódigo el carácter comilla doble (recordemos que para delimitar el
valor de un tipo caracter se debe utilizar comilla simple). De este modo, tres ejemplos de literales
de tipo cadena serían: “12 de octubre de 1492”, “Esto es un ejemplo de cadena de caracteres” o
“123456789”.
Dado que el lenguaje de programación que vamos a emplear durante las prácticas de la asigna-
tura tiene implementado de forma nativa el tipo de dato cadena, en esta asignatura se permitirá
declarar variables de tipo cadena en pseudocódigo. Para ello se utilizará la palabra reservada cadena
tal y como se muestra en el extracto del Algoritmo 3.6, donde se declaran dos variables de ese tipo
con identificadores cad1 y cad2.

variables
cad1 , cad2 : cadena
inicio
...
cad1 ← " Esto e s un e j e m p l o de cadena "
cad2 ← " 332266 "
...
fin

Algoritmo 3.6: Ejemplo de declaración de variables de tipo cadena en pseudocódigo

96
Aunque se verá con más detalle en el tema dedicado al tipo de dato estructurado array, las instruc-
ciones básicas para asignar, leer y escribir variables de tipo cadena se realizan de modo similar al
tratamiento de dichas instrucciones con datos numéricos. Así, por ejemplo, para asignar la cadena
de texto “En un lugar de la Mancha” a la variable de tipo cadena cad1, en pseudocódigo sería:
cad1 ← ‘En un lugar de la Mancha”. Del mismo modo, para leer de teclado o escribir en pantalla
el contenido de la variable de tipo cadena cad2, en pseudocódigo sería leer(cad2) y escribir(cad2),
respectivamente.
El tratamiento de cadenas es un tema importante debido a la gran cantidad de información
que se almacena en ellas. Según el tipo de lenguaje de programación elegido se tendrá mayor o
menor facilidad para realizar distintas operaciones con ellas. En cualquier caso, las operaciones
más usuales con variables de tipo cadena que se verán con detenimiento en el siguiente tema son:

Cálculo de la longitud. Esta operación se realiza con la función longitud en pseudocódigo, y


devuelve el número total de caracteres de la cadena que se le pasa como argumento.

Comparación de cadenas. Aunque la mayoría de lenguajes implementan funciones específicas


para realizar comparación de cadenas, en esta asignatura se utilizarán los operadores de
comparación explicados con anterioridad.

Concatenación. Concatenar dos cadenas significa unirlas entre sí dando lugar a una nueva
cadena compuesta por ambas, una seguida de la otra. Para esta asignatura, en pseudocódigo,
se utilizará el operador aritmético suma + para ejecutar esta acción, combinando tantas
variables de tipo cadena como se quieran concatenar, al igual que se haría para sumar variables
de tipo entero, por ejemplo.

3.5 Estructuras de control

Recordemos que uno de los aspectos fundamentales en los que se basa el paradigma de la progra-
mación estructurada es que todo programa se puede realizar con la combinación de alguna de
las estructuras conocidas como estructuras de control o estructuras básicas, que son la secuen-
cial, la de selección y la iterativa. Estas estructuras básicas de control se explican con detalle a
continuación.

3.5.1 Estructura secuencial

Una estructura secuencial es un bloque de una o más instrucciones que se ejecutan exactamente una
vez en el programa. Dicho de otra manera, la estructura secuencial es aquella en la que una acción
(instrucción) sigue a otra en secuencia, de tal modo que al terminar una se ejecuta la siguiente y
así sucesivamente hasta el final del proceso.

97
Para representar una estructura secuencial en organigrama se debe utilizar la caja rectangular
que representa la ejecución de una acción (o varias), tal y como se muestra en la Figura 3.2. En
pseudocódigo se haría simplemente escribiendo el nombre de las instrucciones que componen la
estructura, una debajo de otra secuencialmente, tal y como se muestra en el Algoritmo 3.7.

acción 1
acción 1
acción 2
...
acción 2
acción n

acción n

Figura 3.2: Estructura secuencial en organigrama

inicio
< accion1 >
...
< a c c i o n n>
fin

Algoritmo 3.7: Pseudocódigo de una estructura secuencial

Para practicar la representación de este tipo de estructura se propone realizar el análisis y diseño
de un algoritmo que calcule el perímetro y el área de un rectángulo a partir de la base y la altura
dadas por el usuario. Una posible solución se expone a continuación:

ANÁLISIS DEL PROBLEMA

DATOS DE ENTRADA

Id Descripción Restricciones
base Base del rectángulo en centímetros >0
altura Altura del rectángulo en centímetros. >0

98
DATOS DE SALIDA

Id Descripción
per Perímetro del rectángulo. Se calcula como 2 ∗ base + 2 ∗ altura
area Área del rectángulo. Se calcula como base ∗ altura

DISEÑO DEL ALGORITMO

PSEUDOCÓDIGO

variables
base , a l t u r a , per , a r e a : e n t e r o
inicio
e s c r i b i r ( " Base y a l t u r a : " )
l e e r ( base , a l t u r a )
per ← 2 ∗ base + 2 ∗ altura
area ← base ∗ altura
e s c r i b i r ( per , a r e a )
fin

Algoritmo 3.8: Pseudocódigo para el cálculo del perímetro y área de un rectángulo

ORGANIGRAMA

inicio

escribir ("Base y altura:")


leer (base,altura)

per ← 2*base + 2*altura


area ← base * altura

escribir (per, area)

fin

Figura 3.3: Organigrama para el cálculo del perímetro y área de un rectángulo

99
3.5.2 Estructuras selectivas

La descripción formal de algoritmos es realmente útil cuando el algoritmo requiere de una descripción más
complicada que una simple lista secuencial de instrucciones. Normalmente estos casos se dan cuando existen
diferentes alternativas resultantes de la evaluación de una determinada condición.
Las estructuras selectivas en programación se utilizan para tomar decisiones lógicas, de ahí que se
suelan denominar también estructuras de decisión o alternativas. En las estructuras selectivas se evalúa
una condición lógica y en función de su resultado se realizan unas tareas u otras. Las condiciones se
especifican utilizando expresiones lógicas, de ahí que sea fundamental dominar las tablas de verdad
del álgebra de Boole que permiten resolver ese tipo de expresiones. Las estructuras selectivas pueden ser
simples, dobles o anidarse unas y otras. Todas ellas se explican a continuación.

3.5.2.1 Selección simple (si-entonces)

La estructura alternativa simple ejecuta una acción o conjunto de acciones cuando se cumple una determinada
condición. Esta estructura evalúa la condición y

si la condición es verdadera, entonces ejecuta la acción S1 (o acciones, en caso de ser S1 un conjunto


de acciones),

si la condición es falsa, entonces no hace nada y la ejecución pasa a la siguiente instrucción después
de la estructura simple.

La representación gráfica en organigrama de esta estructura se muestra en la Figura 3.4. El pseudocódigo


asociado a la estructura condicional simple se muestra en el Algoritmo 3.9.

V F
condición

acción S1

Figura 3.4: Estructura de selección simple mediante organigrama

100
s i ( < condicion >) entonces
...
< a c c i o n S1 >
...
fin_si

Algoritmo 3.9: Estructura de selección simple en pseudocódigo

Obsérvese que las palabras del pseudocódigo si y fin_si se alinean verticalmente, indentando (sangrando) un
poco hacia la derecha la <accion S1> o bloque de acciones S1 hacia la derecha, y que la expresión lógica que
representa la condición suele ir encerrada entre paréntesis. Indentar las acciones que pertenecen a la
estructura condicional es muy importante hacerlo siempre, aparte de porque hacemos la descripción
del algoritmo más legible, porque a la hora de codificar el programa posteriormente en lenguajes como Python
es fundamental respetar la indentación en los bloques de código para no incurrir en errores de sintaxis con
el lenguaje.

3.5.2.2 Selección doble (si-entonces - si_no)

La estructura de decisión simple es limitada y lo habitual es necesitar una estructura que permita elegir
entre dos opciones o alternativas posibles: un conjunto de acciones a realizar cuando la condición sea cierta y
otro bloque de instrucciones diferente cuando la condición sea falsa. Por tanto, una estructura selectiva doble
deberá emplearse cuando se presenten dos alternativas de actuación mutuamente excluyentes que dependan
del resultado de una misma condición. La representación gráfica de esta estructura es similar a la de la
decisión simple, tal y como se muestra en la Figura 3.5. El pseudocódigo asociado a la estructura condicional
doble se muestra en el Algoritmo 3.10.

V F
condición

acción S1 acción S2

Figura 3.5: Estructura de selección doble mediante organigrama

101
s i ( < condicion >) entonces
...
< a c c i o n S1 >
...
si_no
...
< a c c i o n S2 >
...
fin_si

Algoritmo 3.10: Estructura de selección doble en pseudocódigo

Obsérvese que en el pseudocódigo asociado a la estructura de selección doble que si la expresión lógica de la
condición es verdadera se ejecuta la acción S1 (o bloque de acciones S1 ) y, si es falsa, se ejecuta la acción
S2 (o bloque de acciones S2 ). Al igual que para la estructura de decisión simple, en el pseudocódigo las
acciones que dependen de la parte cierta del si (después de la palabra reservada entonces) y las acciones que
dependen de la parte a ejecutar cuando la condición sea falsa (después de la palabra reservada si_no) están
indentadas hacia la derecha en relación a las palabras reservadas si y fin_si. Esto, como se ha comentado
con anterioridad, es obligatorio hacerlo siempre en lenguajes como Python.

3.5.2.3 Estructuras selectivas anidadas

En las estructuras de decisión anidadas se produce un anidamiento en escalera tanto de estructuras de


decisión simples como dobles, pudiéndose anidar indistintamente. Este anidamiento se puede producir tanto
en las partes ciertas de la estructura de decisión anterior como en el bloque de la parte falsa. De este modo
una sentencia si-entonces podría contener otra estructura si-entonces y esta, a su vez, podría contener otra y
así sucesivamente. Al mismo tiempo, dentro de cada estructura anidada, podrían existir diferentes acciones.
No existe una representación gráfica única para las estructuras selectivas anidadas, ya que esta va a
depender del número de estructuras que formen parte del anidamiento y de cómo se establezca dicho anida-
miento. No obstante, la Figura 3.6 muestra dos posibles ejemplos con un único nivel de anidamiento, el
ejemplo de la izquierda con anidamiento en la parte cierta del primer si-entonces y el ejemplo de la derecha
con anidamiento en la parte falsa del primer si-entonces.
Con respecto al pseudocódigo asociado a una estructura selectiva anidada ocurre igual, no existe un
pseudocódigo único que represente todos los posibles anidamientos que se pueden realizar en una estructura
de este tipo. Como posibles ejemplos, el Algoritmo 3.11 y el Algoritmo 3.12 muestran los pseudocódigos
asociados a los organigramas de la Figura 3.6, respectivamente.

102
V F V F
condición1
condición1

V F
condición2 V F
acción S2 acción S1 condición2

acción S1 acción S2

Figura 3.6: Dos posibles ejemplos de estructuras de selección anidadas mediante organigramas

s i ( < condicion1 >) entonces


s i ( < condicion2 >) entonces
< a c c i o n e s S1 >
fin_si
si_no
< a c c i o n e s S2 >
fin_si

Algoritmo 3.11: Ejemplo de estructura de selección anidada en pseudocódigo

s i ( < condicion1 >) entonces


< a c c i o n e s S1 >
si_no
s i ( < condicion2 >) entonces
< a c c i o n e s S2 >
fin_si
fin_si

Algoritmo 3.12: Ejemplo de estructura de selección anidada en pseudocódigo

103
3.5.2.4 Ejemplo de utilización de estructuras selectivas

Para practicar la representación de estructuras selectivas se propone realizar el análisis y diseño de un


algoritmo que resuelva una ecuación lineal de primer grado del tipo (ax + b = 0). Una posible solución se
expone a continuación:
ANÁLISIS DEL PROBLEMA

DATOS DE ENTRADA

Id Descripción Restricciones
a Variable que multiplica a x (real) -
b Variable independiente (real) -

DATOS DE SALIDA

Id Descripción
x Valor de la incógnita x. Se calcula como x = -b/a (siempre que a! = 0)

COMENTARIOS
Habrá que comprobar el valor de b siempre que a sea igual a 0, de modo que si b es distinto de 0
entonces la solución es imposible, y si b es igual a 0 entonces la solución es indeterminada.

DISEÑO DEL ALGORITMO


PSEUDOCÓDIGO

variables
a , b : real
inicio
e s c r i b i r ( "Dame e l v a l o r de a y b : " )
l e e r (a , b)
s i ( a != b ) e n t o n c e s
x ← -b/a
e s c r i b i r ( " El v a l o r de x e s " , x )
si_no
s i ( b != 0 ) e n t o n c e s
e s c r i b i r ( " Solución imposible " )
si_no
e s c r i b i r ( " Solución indeterminada " )
fin_si
fin_si
fin

Algoritmo 3.13: Pseudocódigo del algoritmo que calcula una ecuación lineal de primer grado

104
ORGANIGRAMA

inicio

escribir ("Dame a y b:")


leer (a,b)

V F
a != b

x ← -b/a b != 0

escribir ("imposible") escribir ("indeterminada")


escribir (x)

fin

Figura 3.7: Organigrama del algoritmo que calcula una ecuación líneal de primer grado

3.5.3 Estructuras iterativas

Debido a su altísima velocidad de ejecución los computadores son dispositivos especialmente diseñados para
realizar determinadas tareas que deben repetirse un número considerable de veces. Por este motivo, un tipo
de estructura muy importante que viene implementada en casi todos los lenguajes de programación es la
estructura iterativa o estructura repetitiva, cuya utilización es ideal cuando en un algoritmo queremos
que un bloque de instrucciones se repita un número determinado (o indeterminado) de veces. A este tipo
de estructuras también se les conoce en programación como bucles, ya que su comportamiento se asemeja
al de cualquier bucle de repetición. Por otro lado, se denomina iteración al hecho de ejecutar una vez de
forma completa el bloque de acciones que componen el bucle, es decir, una iteración corresponde a una única
repetición completa del bucle.
Veamos la utilidad de este tipo de estructuras con el siguiente ejemplo: supongamos que se desea sumar
una lista de números que se introducen por teclado. El algoritmo para resolver este problema si utilizasemos
las estructuras explicadas hasta el momento (secuenciales y/o selectivas) se compondría de una sucesión de
instrucciones para leer los números y añadir sus valores a una variable suma que fuera la encargada de ir guar-
dando las sumas parciales. Al comenzar el algoritmo la variable suma se inicializaría a 0 y, posteriormente,
se iría incrementando con el valor del número leído por teclado (ver Algoritmo 3.14).

variables
numero , suma : e n t e r o
inicio
suma ← 0

105
l e e r ( numero )
suma ← suma + numero
l e e r ( numero )
suma ← suma + numero
l e e r ( numero )
suma ← suma + numero
...
fin

Algoritmo 3.14: Pseudocódigo del algoritmo para sumar una lista de números de forma secuencial

Como se puede observar en el pseudocódigo del Algoritmo 3.14, las instrucciones leer(numero) y suma ←
suma+numero se repiten muchas veces de forma idéntica, por lo que podríamos decir que ambas instrucciones
forman un bucle que se repite continuamente. En ese caso lo ideal en programación es utilizar una estructura
iterativa, pero siempre que vayamos a utilizar este tipo de estructuras en un algoritmo el programador tiene
que responder a dos preguntas fundamentales:

1. ¿Qué instrucciones componen el cuerpo del bucle?

2. ¿Cuántas veces se deben repetir esas instrucciones?

Siguiendo con el ejemplo de sumar una lista de números necesitaremos saber previamente cuántos números
en total se quieren sumar. Ese requisito es lo que se conoce en programación como la condición de parada
del bucle. En el ejemplo podríamos optar por solicitar al usuario cuántos números en total quiere sumar
antes de empezar a leerlos. Con esa estrategia ya tendríamos perfectamente definida la estructura repetitiva
a utilizar en el algoritmo (conocemos las instrucciones que contendrá el bucle y tenemos definida la condición
de parada).
En programación estructurada existen tres tipos básicos de estructuras iterativas que se diferencian
fundamentalmente en cuándo se comprueba si se ha cumplido la condición de parada:

Estructura mientras-hacer. La condición de parada se comprueba antes de ejecutar la primera


instrucción del bucle, es decir, podría darse el caso de que las instrucciones del bucle no se ejecutaran
ninguna vez, siempre y cuando la condición de parada ya se cumpliera cuando la ejecución del programa
llegase al bucle.

Estructura repetir-mientras y estructura repetir-hasta_que. La condición de parada se com-


prueba después de ejecutar todas las instrucciones que componen el bucle al menos una vez, es decir,
la condición de parada está situada al final del bucle. Por tanto en este tipo de estructuras las ins-
trucciones del bucle siempre se ejecutan al menos una vez.

Estructura para-hacer. En este tipo de estructura, al igual que en la estructura mientras-hacer,


la condición de parada se comprueba antes de ejecutar la primera instrucción del bucle, es decir, la
condición de parada está situada al comienzo del bucle.

106
3.5.3.1 Estructura mientras-hacer

La estructura iterativa mientras-hacer (while-do en inglés) es aquella en la que el cuerpo del bucle se repite
mientras se cumpla una determinada condición, es decir, mientras esa condición sea cierta. Este tipo de
estructura evalúa la condición de parada (una expresión lógica) antes de ejecutar la primera instrucción
del bucle. Por tanto, si al comienzo de la estructura esa condición se evalúa como falsa, no se llegaría a
ejecutar ninguna instrucción del bucle, prosiguiendo el programa por la siguiente instrucción que haya justo
a continuación de la estructura repetitiva. Si la condición del bucle es cierta entonces se ejecuta el cuerpo del
bucle entero y, una vez terminada la última instrucción del bucle, se evalúa de nuevo la expresión lógica de la
estructura (condición de parada), y así sucesivamente hasta que la expresión lógica sea falsa. El organigrama y
pseudocódigo asociado a esta estructura se muestran en la Figura 3.8 y en el Algoritmo 3.15, respectivamente.
Nótese que para representar cualquier estructura iterativa mediante organigramas no existe un
símbolo especial, se utiliza el mísmo símbolo rombo que se emplea para especificar la condición en una
estructura selectiva.

F
condición

acciones

Figura 3.8: Organigrama asociado a la estructura mientras-hacer

mientras ( < condicion >) hacer


accion1
accion2
...
fin_mientras

Algoritmo 3.15: Pseudocódigo para describir una estructura mientras-hacer

Veamos cómo se resolvería el ejemplo del algoritmo para sumar una lista de números utilizando una estructura
mientras-hacer. Para ello, podríamos utilizar una variable auxiliar llamada total que se inicialice al comenzar
con la cantidad de números que se desean leer y, a continuación, se iría decrementando de uno en uno en

107
cada iteración del bucle1 . El Algoritmo 3.16 muestra esta estrategia en pseudocódigo.

variables
t o t a l , suma , numero : e n t e r o
inicio
e s c r i b i r ( " I n t r o d u z c a l a c a n t i d a d t o t a l de números a sumar : " )
leer ( total )
suma ← 0
m i e n t r a s ( total > 0 ) h a c e r
l e e r ( numero )
suma ← suma + numero
total ← total − 1
fin_mientras
e s c r i b i r ( " La suma de l o s " , t o t a l , " números e s " , suma )
fin

Algoritmo 3.16: Pseudocódigo del algoritmo para sumar una lista de números utilizando una es-
tructura mientras-hacer

Obsérvese que para que un bucle mientras-hacer termine en algún momento de iterar y no entre en un estado
de bucle infinito (nuestro programa se quedaría colgado sin responder), la condición de parada debería ser
falsa en algún momento. Por ello, en este tipo de estructuras es obligatorio que en el cuerpo del bucle
exista siempre alguna instrucción que modifique alguno de los operandos que intervienen en
la expresión lógica de la condición ya que, de lo contrario, la condición sería cierta siempre (si se han
inicializado los operandos para que sea cierta la primera vez) y nos encontraríamos ante un temido bucle
infinito. La estructura iterativa mientras-hacer suele emplearse en programación cuando se desconoce a priori
el número exacto de iteraciones que debe ejecutarse el bucle y, además, no se tiene la obligación de ejecutar
al menos una vez las instrucciones del cuerpo del bucle.

3.5.3.2 Estructuras repetir-mientras y repetir-hasta_que

Existen muchas situaciones cuando diseñamos algoritmos en las que se desea que un bucle se ejecute al
menos una vez antes de comprobar la condición de repetición. Recordemos que en la estructura iterativa
mientras-hacer si el valor de la expresión lógica de la condición es falso al iniciar el bucle, el cuerpo del bucle
no se ejecutaría, de ahí que se necesite otro tipo de estructura repetitiva para resolver estos casos.
La estructura iterativa repetir-mientras (repeat-while, en inglés) y la estructura iterativa repetir-hasta_que
(repeat-until, en inglés) se ejecutan mientras se cumpla una condición o hasta que se cumpla una condición,
respectivamente, condición que siempre se comprueba al final del bucle, es decir, siempre se ejecuta el bloque
de instrucciones del bucle al menos una vez con cualquiera de las dos estructuras.
1
También se podría haber optado por hacerlo al revés, es decir, inicializar la variable total a 0 o 1, e ir incremen-
tándola de uno en uno en cada iteración del bucle, hasta llegar al número deseado.

108
Obviamente la expresión lógica a utilizar como condición de parada será diferente si utilizamos una u
otra alternativa. Esa expresión lógica vendrá determinada por el propio significado de la estructura que
utilicemos ya que repetir algo mientras se cumpla una condición hace que la expresión lógica de esa con-
dición a configurar sea bien diferente de la que hay que utilizar cuando se desea repetir algo hasta que se
cumpla la condición. Básicamente lo que implicaría sería un cambio en el operador relacional utilizado en la
expresión lógica. El organigrama y pseudocódigos asociados a las dos posibles alternativas de esta estructura
se muestran en la Figura 3.9 y en los Algoritmos 3.17 y 3.18, respectivamente.

acciones acciones

V F
hasta_que mientras
condición condición
F V

Figura 3.9: Organigrama asociado a la estructura repetir-hasta_que y repetir-mientras

repetir
accion1
accion2
...
hasta_que ( < c o n d i c i o n > )

Algoritmo 3.17: Pseudocódigo asociado a la estructura repetir-hasta_que

repetir
accion1
accion2
...
mientras ( < condicion >)

Algoritmo 3.18: Pseudocódigo asociado a la estructura repetir-mientras

El ejemplo del algoritmo que suma una lista de números se podría resolver también utilizando cualquiera de

109
las dos estructuras repetir explicadas, lo único que cambiaría es la condición a comprobar al final del bucle,
dependiendo de si utilizamos la estructura repetir-hasta_que o repetir-mientras. Los Algoritmos 3.19 y 3.20
muestran una posible solución utilizando ambas alternativas.

variables
t o t a l , suma , numero : e n t e r o
inicio
e s c r i b i r ( " I n t r o d u z c a l a c a n t i d a d t o t a l de números a sumar : " )
leer ( total )
suma ← 0
repetir
l e e r ( numero )
suma ← suma + numero
total ← total − 1
hasta_que ( total = 0 )
e s c r i b i r ( " La suma de l o s " , t o t a l , " números e s " , suma )
fin

Algoritmo 3.19: Pseudocódigo del algoritmo para sumar una lista de números utilizando una es-
tructura repetir-hasta_que

variables
t o t a l , suma , numero : e n t e r o
inicio
e s c r i b i r ( " I n t r o d u z c a l a c a n t i d a d t o t a l de números a sumar : " )
leer ( total )
suma ← 0
repetir
l e e r ( numero )
suma ← suma + numero
total ← total − 1
m i e n t r a s ( total > 0 )
e s c r i b i r ( " La suma de l o s " , t o t a l , " números e s " , suma )
fin

Algoritmo 3.20: Pseudocódigo del algoritmo para sumar una lista de números utilizando una es-
tructura repetir-mientras

110
3.5.3.3 Estructura para-hacer

Existen otras situaciones cuando se están diseñando algoritmos en las que se conoce a priori el número de
veces exacto que se tienen que ejecutar las acciones de un bucle. En estos casos es recomendable utilizar
la estructura iterativa para-hacer (for-do, en inglés). Esta estructura ejecuta las acciones del cuerpo del
bucle un número de veces determinado y la ventaja de utilizarlas es que la propia estructura controla de
modo automático las iteraciones, de manera que el programador se despreocupa de tener que incluir una
instrucción dentro del bucle que modifique en algún momento la condición de parada del mismo. Ese control
automático de las iteraciones lo realiza la propia estructura a través de lo que se conoce como variable índice,
contador o variable de control del bucle, de manera que dicha variable se incrementa o decrementa de forma
automática al terminar cada iteración. No obstante, el programador sí deberá especificar en la definición de
la estructura hasta que valor podrá alcanzar la variable de control.
La ejecución de la estructura iterativa para-hacer comienza asignando un valor inicial (vi) a la variable
de control o variable contador (vc) del bucle y, a continuación, comprueba si ese valor supera o iguala el
valor final (vf ) definido, es decir, el número de veces que queremos que se ejecute el cuerpo del bucle. Si
es así, la ejecución del bucle finaliza, pasando la ejecución del algoritmo a la siguiente instrucción que haya
justo después de la estructura iterativa. En otro caso, la ejecución del cuerpo del bucle se repetirá hasta que
el valor vi iguale o supere el valor finalvf establecido, incrementando (o decrementando) automáticamente
el valor de la variable contador cada vez que se produce una iteración completa2 .

En la definición de una estructura iterativa para-hacer cabe reseñar que la variable contador del bucle
se puede configurar para que se incremente o para que se decremente en el valor que sea, de 1 en 1, de 2 en
2, etc. Para esta asignatura, cuando se utilice una estructura de este tipo en pseudocódigo, si no se indica
nada en la cabecera de la estructura, se entenderá que, por defecto, la variable contador se incrementa de 1
en 1. De lo contrario habrá que indicar el incremento o decremento (con su valor) justo después de establecer
el valor final (vf ) y antes de la palabra reservada hacer.
Un aspecto muy importante que hay que tener en cuenta cuando se tiene que representar una es-
tructura iterativa para-hacer mediante organigrama (ver Figura 3.10) es la obligatoriedad de dedicar
una acción al final del bucle para incrementar (o decrementar) manualmente la variable contador, ya que
cuando utilizamos organigramas no existe un símbolo especial para representar estructuras iterativas del
tipo para-hacer. Por tanto, se ha de utilizar el mismo símbolo rombo que para las estructuras iterativas
mientras o repetir y es el programador quien debe indicar el incremento o decremento mediante
una última acción dentro del cuerpo del bucle. En pseudocódigo, como se ha comentado, esto no es
necesario, ya que ese incremento (o decremento) se hace de forma automática en la cabecera que define la
estructura (ver Algoritmo 3.21 y Algoritmo 3.22).

2
De forma análoga, tal y como se muestra en la parte derecha de la Figura 3.10, la estructura para-hacer se puede
configurar inicializando la variable contador al valor final y decrementando dicho valor hasta que se llegue al valor
inicial.

111
vc ← vi vc ← vf

F F
vc < vf vc > vi

V V

acciones acciones

incrementar vc decrementar vc

Figura 3.10: Organigramas asociados a la estructura iterativa para-hacer con incremento o decre-
mento

p a r a vc ← vi h a s t a v f h a c e r
accion1
accion2
...
fin_para

Algoritmo 3.21: Pseudocódigo asociado a la estructura iterativa para-hacer por defecto (incremento
de la variable de control de 1 en 1)

p a r a vc ← vf h a s t a v f [ i n c | dec ] < v a l o r > h a c e r


accion1
accion2
...
fin_para

Algoritmo 3.22: Pseudocódigo asociado a la estructura iterativa para-hacer estableciendo un incre-


mento o decremento determinado (valor)

El Algoritmo 3.23 y el Algoritmo 3.24 muestran en pseudocódigo cómo se resolvería el ejemplo de sumar
una lista de números introducidos por teclado utilizando una estructura iterativa para-hacer por defecto
(incremento de 1 en 1) o una estructura iterativa para-hacer con decremento de 1 en 1, respectivamente.

112
variables
t o t a l , suma , numero : e n t e r o
inicio
e s c r i b i r ( " I n t r o d u z c a l a c a n t i d a d t o t a l de números a sumar : " )
leer ( total )
suma ← 0
p a r a vc ← 1 h a s t a total + 1 h a c e r
l e e r ( numero )
suma ← suma + numero
fin_para
e s c r i b i r ( " La suma de l o s " , t o t a l , " números e s " , suma )
fin

Algoritmo 3.23: Pseudocódigo del algoritmo para sumar una lista de números utilizando una es-
tructura iterativa para-hacer por defecto (con incremento de 1 en 1)

variables
t o t a l , suma , numero : e n t e r o
inicio
e s c r i b i r ( " I n t r o d u z c a l a c a n t i d a d t o t a l de números a sumar : " )
leer ( total )
suma ← 0
p a r a vc ← total h a s t a 0 dec 1 h a c e r
l e e r ( numero )
suma ← suma + numero
fin_para
e s c r i b i r ( " La suma de l o s " , t o t a l , " números e s " , suma )
fin

Algoritmo 3.24: Pseudocódigo del algoritmo para sumar una lista de números utilizando una es-
tructura iterativa para-hacer con decremento de 1 en 1

3.5.3.4 Estructuras iterativas anidadas

Al igual que puede ocurrir con las estructuras selectivas, al diseñar un algoritmo se pueden anidar estructuras
iterativas, de manera que un bucle se inserte en el cuerpo de instrucciones de otro bucle. La única regla que se
ha de respetar para construir estructuras iterativas anidadas es que la estructura interna debe estar incluida

113
totalmente dentro de la estructura inmediatamente exterior y no puede existir solapamiento. De este modo,
por cada iteración de la estructura más externa se deben ejecutar de forma completa todas
las iteraciones del bucle de la estructura inmediatamente interior (y así sucesivamente).
Para ver un ejemplo de uso de este tipo de estructuras el Algoritmo 3.25 muestra una posible solución
en pseudocódigo para resolver el problema del cálculo del factorial de n números leídos desde teclado.

variables
n , numero , i , j , f a c t o r i a l : e n t e r o
inicio
e s c r i b i r ( "A c u á n t o s números l e q u i e r e c a l c u l a r e l f a c t o r i a l ? " )
l e e r (n)
para i ← 1 hasta n + 1 hacer
e s c r i b i r ( " Dime número : " )
l e e r ( numero )
factorial ← 1
p a r a j ← 1 h a s t a numero + 1 h a c e r
factorial ← factorial ∗ j
fin_para
e s c r i b i r ( " El f a c t o r i a l d e l número " , numero , " e s " , f a c t o r i a l )
fin_para
fin

Algoritmo 3.25: Pseudocódigo del algoritmo para calcular el factorial de n números

3.5.3.5 Instrucciones avanzadas para salir de un bucle

En la mayoría de lenguajes de programación existen instrucciones avanzadas de control de la ejecución de


los programas que permiten romper la secuencia lineal de la ejecución de las instrucciones. Aunque este
tipo de instrucciones se alejan del paradigma de la programación estructurada, en ocasiones, sobre todo en
estructuras iterativas, es necesario utilizarlas para salir de un bucle en un punto intermedio determinado,
cuando se cumpla cierta condición. Las dos instrucciones que se van a utilizar en esta asignatura para llevar
a cabo esta función son:

break, cuyo cometido es romper la ejecución de un bucle y continuar la ejecución del programa por
la instrucción inmediatamente posterior a la estructura iterativa (termina el bucle prematuramente,
aunque no se cumpla la condición de parada).

continue, cuyo efecto es saltar a la siguiente iteración del bucle, reevaluando la condición de continui-
dad del mismo. Es decir, termina la iteración del bucle prematuramente donde estuviera la instrucción
continue y salta a la siguiente iteración, incrementando (o decrementando) la variable de control. Si
la condición de continuidad sigue siendo cierta, el bucle continuará iterando.

114
En el Algoritmo 3.26 se muestra un ejemplo en pseudocódigo de una posible utilización de la instrucción
break. Al ejecutar dicho código, si en algún momento la condición2 es cierta, se ejecutaría la instrucción break
y la ejecución del cuerpo del bucle mientras-hacer se terminaría en ese momento, siguiendo la ejecución del
programa por la instrucción que hubiera justo después de la instrucción fin_mientras.

...
mientras ( < condicion1 >) hacer
accion1
accion2
...
s i ( < condicion2 >) hacer
break
fin_si
...
accion n
fin_mientras
...

Algoritmo 3.26: Ejemplo en pseudocódigo de una posible utilización de la instrucción avanzada


break

3.6 Escritura de algoritmos y programas en pseudocódigo


La escritura de un algoritmo debe ser estructurada y lo más legible posible, de modo que su lectura facilite
considerablemente el entendimiento del algoritmo y su posterior codificación en un lenguaje de progra-
mación. Para ello los algoritmos deben ser escritos o representados en lenguajes que sean lo más similar
posible a los programas. En esta asignatura, como ya se ha comentado, se utilizarán el lenguaje algorítmico
(pseudocódigo) y los diagramas de bloques, también conocidos como organigramas o diagramas de flujo.
Cuando se describa un algoritmo mediante pseudocódigo se deberán definir y completar las siguientes
secciones:

1. Cabecera de programa. En esta sección se indicará el nombre asignado al algoritmo, utilizando


previamente la palabra reservada algoritmo. El nombre asignado al algoritmo debe representar de
forma resumida qué resuelve dicho algoritmo.

2. Parte declarativa. Contiene las declaraciones de constantes y variables que utilizará el algoritmo. En
la sección constantes se declaran todas las constantes del algoritmo, colocando primero el identificador
de la constante seguido del signo igual (=) y el valor que toma la constante. Después, en la sección
variables, se declaran todas las variables del algoritmo colocando el identificador de la variable seguido
del signo dos puntos (:) y el tipo de la variable. Se permite declarar más de una variable del mismo

115
tipo en la misma línea separadas por comas. El tipo podrá ser uno de los tipos de dato simples (entero,
real, carácter o lógico) o alguno de los tipos estructurados que se ven en la asignatura.

3. Pseudocódigo. Contiene las acciones que serán ejecutadas por el programa (el algoritmo) y siempre
deberá comenzar con la palabra reservada inicio y terminar con la palabra reservada fin.

Otros aspectos a tener en cuenta en relación al estilo que debemos utilizar cuando escribamos algoritmos
en pseudocódigo son:

Las secciones de declaración de constantes y/o variables se omitirán cuando no sean necesarias, es
decir, cuando el algoritmo no necesite utilizar constantes y/o variables.

Se deberán indentar aquellas instrucciones que pertenezcan a una determinada estructura para pro-
porcionar mayor legibilidad en la lectura del algoritmo.

Se podrán añadir comentarios en el pseudocódigo utilizando el carácter almohadilla (#) antes del
texto del comentario.

Por ejemplo, el Algoritmo 3.27 muestra cómo se debería escribir en pseudocódigo (de forma completa) el
algoritmo que resuelve una ecuación de segundo grado.

a l g o r i t m o ecuacion_segundo_grado
# a l g o r i t m o que r e s u e l v e una e c u a c i o n de segundo gr a do
variables
a ,b, c ,d : real
inicio
leer (a ,b , c)
d ← b ^ 2 - 4 * a * c
s i d < 0 entonces
e s c r i b i r ( " Raíces complejas " )
si_no
s i d = 0 entonces
e s c r i b i r ( - b / 2* a )
si_no
e s c r i b i r ( ( - b - s q r t (d ) ) / (2* a ) )
e s c r i b i r ( ( - b + s q r t (d ) ) / (2* a ) )
fin_si
fin_si
fin

Algoritmo 3.27: Pseudocódigo del algoritmo que resuelve una ecuación de segundo grado

Como se puede observar en el pseudocódigo del Algoritmo 3.27, la cabecera estaría formada por la primera
línea (algoritmo ecuación_segundo_grado), donde se le asigna un nombre algoritmo, la parte declarativa

116
estaría formada exclusivamente por la sección variables, donde se declaran las variables a, b, c y d de tipo
real, y la parte del pseudocódigo del algoritmo estaría compuesta por todas las instrucciones comprendidas
entre las palabras reservadas inicio y fin inclusive.

3.6.1 Depuración y control de errores

Durante las fases de diseño y programación o codificación de programas es habitual cometer errores. En
particular, los errores que se pueden cometer se clasifican en dos tipos principalmente:

De sintaxis, que son errores cometidos a la hora de escribir el código conforme a las normas del
lenguaje de programación. Como consecuencia de estos errores, el programa no puede ser compilado o
interpretado. Los errores de sintaxis son fácilmente detectados porque el propio compilador o intérprete
indican en qué línea del código se produce el error, sólo es necesario conocer las normas sintácticas del
lenguaje para poder corregirlos.

De lógica, que son errores relacionados con la lógica del programa y, por tanto, cometidos durante la
fase de diseño del algoritmo normalmente, propiciando como consecuencia que el programa no haga lo
que se pretendía y que el resultado sea incorrecto. Los errores de lógica pueden llegar a ser difíciles de
detectar, especialmente si el código no presenta una buena legibilidad. Estos errores suelen detectarse
durante la propia ejecución del programa y para solucionarlos se suele realizar una batería o test de
pruebas que permitan mostrar el funcionamiento correcto o incorrecto del programa para diferentes
valores de entrada.

Una de las herramientas habituales que facilitan la detección de posibles errores en el código de un
programa son los depuradores (debugger, en inglés). Un depurador es un programa que acompaña nor-
malmente al compilador o intérprete del lenguaje y que permite hacer un seguimiento paso a paso de las
instrucciones que se ejecutan en el programa. A ese proceso de ejecutar paso a paso las instrucciones de
un programa se le conoce como traza de un programa. Cuando la herramienta con la que trabajamos para
codificar el algoritmo dispone de un editor de código fuente, un compilador y un depurador (todo en la
misma herramienta), decimos que estamos utilizando un entorno de programación integrado o entorno de
desarrollo integrado (IDE, Integrated Development Environment, en inglés).
La utilización de un depurador para detectar posibles errores en nuestro código puede suponer una
ventaja porque permite:

Ejecutar instrucción a instrucción, de modo que se ejecuta una única instrucción y el programa se
detiene hasta que el programador indique que se ejecute la siguiente. De este modo es posible con-
sultar en cualquier momento el estado del programa, es decir, comprobar los valores almacenados por
cualquier variable del programa en ese momento.

Introducir puntos de ruptura (breakpoints) en el código, de manera que podremos indicarle al depurador
que ejecute el programa hasta llegar a ese punto de ruptura definido y, a partir de ahí, continuar paso
a paso, por ejemplo. Utilizando puntos de ruptura podremos saltar código de nuestro programa en el
que tenemos la certeza de que no hay ningún tipo de error y así ahorrar tiempo en la detección de
posibles problemas.

117
Consultar y/o modificar el valor de las variables durante la ejecución paso a paso. En todo momento,
el depurador nos permitirá consultar el valor de una determinada variable durante la ejecución, o el
valor que retorna un módulo, e incluso asignar un valor en tiempo de ejecución a una variable en
concreto, etc.

Además de los errores de sintaxis y los errores de lógica debemos considerar otro tipo de problemas
que pueden presentar los programas y que se deben a los valores no permitidos en los datos de entrada
(restricciones). Por ejemplo, un programa que calcula una raíz cuadrada no debería permitir introducir
un valor negativo, o un programa que calcule el factorial de un número no debería permitir que dicho
número fuera tan grande que sobrepasara la capacidad de cálculo del computador. Ante una situación de
este tipo si el programa no está preparado podría proporcionar un resultado incorrecto o incluso causar un
problema importante en el sistema, dejándolo inutilizado (lo que comúnmente se conoce como colgado). En
este sentido, un buen programador debe construir programas robustos frente a todo tipo de situaciones que
puedan poner en riesgo su funcionamiento, respondiendo adecuadamente ante cualquier eventualidad (datos
de entrada imprevistos, fuera del rango permitido, con formato incorrecto, ausentes, etc.). No obstante,
cuando se detecta una situación de este tipo, el programa siempre debe informar al usuario del error
que se ha producido y, además, se pueden adoptar dos enfoques diferentes:

Prevenir el error, es decir, tomar las medidas necesarias para que no se produzca una situación de
riesgo. Por ejemplo, que sea imposible escribir números negativos para calcular su raíz cuadrada.

Recuperar el error, o lo que es lo mismo, tomar las medidas necesarias para que si se detecta la situa-
ción, poder solucionarlo. Por ejemplo, si se intenta calcular la raíz cuadrada de un número negativo,
retroceder para repetir la entrada de datos hasta que sea válida.

Por último, una vez conocidos los riesgos de un programa siempre se debe probar su funcionamiento con
arreglo a dichos riesgos, es decir, se deberían hacer pruebas (tests) de todo tipo, utilizando valores de entrada
correctos, extremos pero correctos, extremos pero incorrectos, valores totalmente fuera del rango correcto,
valores aleatorios, etc. Cuanto más pruebas se realicen más robusto será nuestro programa.

118

También podría gustarte