FechaVersiónDescripción
14/09/20211.0.0Versión inicial
27/09/20211.0.1Corrección de erratas
05/10/20211.0.2Se añade el tratamiento de String
08/10/20211.0.3Corrección de erratas en ejemplos de recorridos. Implementación en Java de ejemplos que faltaban.
04/09/20241.1.0Se cambia direccionamiento imágenes en fichero.
11/09/20242.0.0Añadimos Dart

Unidad 2 Programación estructurada. Estructuras de control y repetición

 

 

 

1 Objetivos

El modelo de programación a seguir trata de conseguir, ante todo, programas simples y claros, que puedan ser mantenidos y actualizados fácilmente.

Para conseguir este propósito se siguen los criterios de la Programación Modular y Estructurada.

 

2 Programación Modular

Esta técnica de programación consiste en dividir un programa en partes bien diferenciadas lógicamente, llamadas MODULOS, que puedan ser analizadas y programadas por separado.

Un Módulo se puede definir como un conjunto formado por una o varias instrucciones lógicamente enlazadas.

A cada módulo se le asigna un nombre, que elige el programador, para poder identificarlo. Cuando en un punto del programa se referencia un módulo, el programa le cede el control para que se ejecuten todas sus instrucciones. Finalizado el mismo, el control se devuelve al punto del programa desde donde se llamó al módulo y se continúa con la ejecución de la instrucción siguiente a la que realizó la llamada.

Si un módulo es lo suficientemente grande o complicado puede subdividirse en otros módulos y éstos a su vez en otros, obteniéndose un diagrama:

T2_01

 

Siempre debe existir un módulo raíz o principal, que es el encargado de controlar y relacionar a todos los demás.

No hay una forma fija para dividir un programa en módulos, pero se deben seguir las siguientes normas:

La programación modular es una técnica que se basa en el desarrollo de programas partiendo de lo general a lo particular, DISEÑO TOP-DOWN. Se comienza considerando qué funciones debe realizar el programa desde un punto de vista muy general, dándolas como resueltas y dejando su diseño (cómo) para un paso posterior. De esta manera se avanza hasta llegar al máximo nivel de detalle.

Es decir, si dentro de un módulo aparece una función cuyo desarrollo completo queremos posponer, esto lo representaremos indicando en ese punto el nombre que tendrá el módulo que lo desarrollará, ocupándonos posteriormente de dicho desarrollo.

La programación modular aporta una serie de ventajas frente a la programación convencional:

 

3 Programación Estructurada

La P.E. es un criterio de programación basado en el TEOREMA DE LA ESTRUCTURA de Bohn y Jacopini: “Todo programa propio, es decir, con un solo punto de entrada y un solo punto de salida, puede ser escrito utilizando únicamente tres tipos de estructuras de control: Estructura secuencial, condicional y repetitiva”.

Cualquier programa por largo y complejo que sea siempre se puede desarrollar mediante el anidamiento apropiado de estos tres tipos de estructuras.

La programación modular y estructurada son criterios complementarios. El primero tiende a dividir un programa en partes más pequeñas, llamadas MÓDULOS, y el segundo se encarga de desarrollar estructuradamente cada una de esas partes.

 

4 Recordando que es un algoritmo

Recordamos que un algoritmo es una descripción clara y no ambigua de las acciones necesarias para solucionar un problema en un orden determinado. Los elementos que tiene un algoritmo son los siguientes:

4.1 ¿Como se representa un algoritmo?

Se ha de usar un método independiente de cualquier lenguaje de programación. Para ello hay diversas maneras, pero veremos dos:

Junto a estas dos maneras de representar un algoritmo nos haremos servir del lenguaje de programación Java para poder representar.

 

4.2 Calidad de un algoritmo

Para resolver un problema determinado es posible diseñar una infinidad de algoritmos. La calidad de un algoritmo depende de:

5 Elementos de un algoritmo

5.1 Instrucciones de inicio y fin

Para crear un algoritmo básico estructurado es necesario que existan al menos estos tres bloques: ALGORITMO, INICIO, FIN ALGORITMO.

En Diagrama de Flujo también nos encontramos con esto:

T2_02

 

Como hemos indicado, en pseudocódigo lo encontramos así, comenzando por la palabra ALGORITMO y asignandole un nombre:

 

En Java para indicar el inicio y fin se utilizan las llaves { }. En el ejemplo del programa hola mundo lo podemos ver claramente:

 

5.2 Instrucciones de asignación. Operadores y expresiones.

Una asignación consiste en guardar un valor en una variable. En diagrama de flujo se puede ver de la siguiente forma (por ejemplo asignamos un valor de 45 a la variable edad):

T2_03

En pseudocódigo lo podemos encontrar de la siguiente forma (lo explicamos un poco más adelante):

 

En Java nos lo encontramos así:

 

En pseudocódigo, a la hora de declarar la información que necesitamos en el algoritmo para trabajar tendremos 2 clases de tipos de datos:

Todos los datos precisan de una declaración e inicialización (poner un valor inicial). Esto se especificará antes de comenzar el bloque del cuerpo del programa, es decir, antes de la etiqueta INICIO.

Para distinguirlos vamos a poner dos bloques que nos clasifiquen los datos, un bloque con la expresión CONST (para los datos constantes) y otro bloque con la expresión VAR (para las variables).

Existen diversos tipos de variables, según el número de elementos que puedan almacenar:

Tipos de Datos por contenido: Se debe especificar el tipo de dato antes de su nombre. Se inicializan con el carácter igual (=).

Los nombres de los datos deben ser descriptivos y por estándar de calidad. Tanto las constantes como las variables en minúsculas. Además pueden tener hasta 40 caracteres, deben empezar obligatoriamente con una letra y no pueden contener espacios en blanco, sólo se pueden incluir caracteres especiales como el guión o el punto.

No serían nombres válidos:

 

Un ejemplo en pseudocódigo puede ser el siguiente:

 

Por ejemplo podemos crear un algoritmo que calcule el número de horas que hay en 10 años. En diagrama de flujo la solución es la siguiente:

T2_04

Si se desea, es posible poner varias instrucciones dentro de un mismo rectángulo (para simplificar):

T2_05

En pseudocódigo lo encontramos de la siguiente forma (ya modificamos AÑOS por ANYOS, ya que la "Ñ" no es un carácter aceptado a la hora de declarar variables/constantes):

 

En Java nos lo encontramos de la siguiente forma:

 

En todos los lenguajes de programación se utilizan operadores para efectuar operaciones aritméticas. Combinando las variables y constantes en expresiones aritméticas por medio de funciones adecuadas. Los operadores que existen son:

NombreOperadores
Asignación= , :=
Aritméticos+, -, *, /, ^, %
Unitarios-, --, ++ (Especialmente recomendados para contadores)
Relacionales<,>,<=,>=,==,!=
Lógicos/BooleanosY (AND,&&), O (OR, ||), NO (NOT, |)
Operadores de bits y de cadenas 

También hemos de tener en cuenta que la prioridad de los operadores es la siguiente:

  1. Paréntesis

  2. Exponentes

  3. Productos y divisiones

  4. Sumas y restas

  5. Relaciones (Negación, Conjunción, Disyunción)

 

Las expresiones lógicas y relacionales devuelven verdadero o falso.

En programación, una expresión es una combinación de constantes, variables o funciones, que es interpretada (evaluada) de acuerdo a las normas particulares de precedencia y asociación para un lenguaje de programación en particular. Como en matemáticas, la expresión es su "valor evaluado", es decir, la expresión es una representación de ese valor.

Ejemplos de expresiones:

5.3 Instrucciones de entrada y salida de información.

Todos los programas informáticos utilizan algún tipo de entrada y salida de información. Es una parte muy importante de la programación ya que, entre otras cosas, es lo que permite a un usuario interactuar con un programa. Por ejemplo:

  1. El usuario introduce información por teclado (entrada).

  2. El programa procesa la información (hace algún cálculo).

  3. El programa muestra el resultado por pantalla (salida).

 

Todos los programas de ordenador, apps de teléfono, páginas web, etc. siguen estos tres pasos. Tú apretas un botón (o haces click, tocas la pantalla...), luego el procesador lo procesa y por último sucede algo (se visita una página web, se escucha una canción, se envía un whatsapp, etc.).

No tendría sentido crear programas sin entrada ni salida ya que solo realizarían cálculos sin comunicarse con el exterior (como en los dos ejemplos del apartado anterior).

Algunos ejemplos de dispositivos utilizados para entrada y salida de información:

 

Por ahora nos vamos a centrar en la entrada y salida más sencilla: el teclado y la pantalla por lo que trabajaremos con las siguientes instrucciones:

 

Vamos a ver un ejemplo donde calcularemos el área de un círculo (recordamos que el área de un círculo se calcula multiplicando el radio del círculo por el número PI, que es 3,14). En un diagrama de flujo será de la siguiente forma:

T2_06

En pseudocódigo lo podemos encontrar de la siguiente:

 

En Java será de la siguiente forma:

 

5.4 Instrucciones de control

Hasta ahora hemos visto algoritmos (representados como ordinogramas) en los que las instrucciones se ejecutan secuencialmente (una después de la otra). Pero a menudo es necesario diseñar algoritmos cuyas instrucciones no se ejecuten secuencialmente, para lo que es necesario utilizar estructuras de control.

Las estructuras de control son utilizadas para controlar la secuencia (el orden) en el que se ejecutan las instrucciones.

Existen dos tipos:

 

5.5 Estructuras alternativas

Controlan la ejecución o la no ejecución de una o más instrucciones en función de que se cumpla una condición. Dicho de otra manera, se utilizan para que sucedan cosas distintas dependiendo de una condición.

Por ejemplo, al introducir una contraseña si esta es correcta se iniciará sesión, pero si es incorrecta mostrará un mensaje de error. Por poner otro ejemplo, cuando aprietas una letra o un número del teclado ésta se mostrará por pantalla, pero si es la tecla de intro entonces el cursor bajará a la siguiente línea.

Puede parece obvio pero esto sucede así porque un programador ha utilizado una estructura alternativa para indicar exactamente qué debe suceder en cada caso. Las estructuras alternativas son muy importantes para establecer distintos comportamientos a un programa.

Existen tres tipos de estructuras alternativas o condicionales: Simple, Doble y Múltiples.

Todas ellas utilizan condiciones, como (precio > 200), (edad >= 18), (contraseña == “1234”), etc. En un diagrama de flujo una condición se expresa mediante un rombo.

T2_07

Al explicar los tres tipos de estructuras haremos referencia al pseudocódigo y al código Java.

 

5.5.1 Estructura alternativa (o condicional) simple

La estructura alternativa simple es muy sencilla. Si la condición es verdadera se ejecutará una o varias instrucciones concretas, pero si es falsa éstas no se ejecutarán. Se representa así:

T2_08

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Por ejemplo, un programa que pide la edad por teclado, si es mayor o igual a 18 mostrará por pantalla el texto “Mayor de edad”, en caso contrario no hará nada:

T2_09

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente forma:

En Dart lo realizaremos de la siguiente forma:

 

La estructura condicional más simple en Java es el if, se evalúa una condición y en caso de que se cumpla se ejecuta el contenido entre las llaves {} o en caso de que se omitan se ejecuta el código hasta el primer «;» por lo tanto si no se usan los {} la condición aplica solo a la siguiente instrucción al if.

Podemos observar además, que en Dart es igual

 

5.5.2 Estructura alternativa (o condicional) compuesta

La estructura alternativa doble es muy similar a la simple. La única diferencia es que si la condición es cierta se ejecutarán unas instrucciones y si es falsa se ejecutarán otras distintas.

T2_10

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Siguiendo el ejemplo anterior crearemos un programa que pide la edad por teclado, si es mayor o igual a 18 mostrará por pantalla el texto “Mayor de edad”, en caso contrario mostrará por pantalla el texto "Menor de edad":

T2_11

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente forma:

En Dart:

 

5.5.3 Estructuras alternativas (o condicionales) anidadas.

Decimos que una estructura condicional es anidada cuando por la rama del verdadero o el falso de una estructura condicional hay otra estructura condicional. En diagrama de flujo será de la siguiente forma:

T2_12

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Siguiendo el ejemplo anterior crearemos un programa que pide la edad por teclado, si es mayor o igual a 18 mostrará por pantalla el texto “Mayor de edad”, en caso contrario, si la edad es menor de 6 mostrará por pantalla "Es un niño/a pequeño/a" sino mostrará por pantalla el texto "Menor de edad":

T2_13

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente

En Dart:

 

5.5.4 Estructura alternativa (o condicional) múltiple.

La estructura alternativa (o condicional) múltiple permite seleccionar un camino entre varios caminos posibles teniendo como base el valor de la variable seleccionada la cual es comparada con una lista de constantes enteras o de carácter. Cuando el valor de la variable seleccionada es igual a una de las constantes, se ejecuta la instrucción o el grupo de instrucciones que pertenecen a dicha constante. En diagrama de flujo será de la siguiente forma:

T2_14

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Vamos a poner un ejemplo con esta estructura: se pide diseñar un algoritmo que pide una vocal (suponemos que siempre es mayúscula) y nos dice cuál es. En diagrama de flujo será el siguiente:

T2_15

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente:

En Dart:

 

5.6 Estructuras repetitivas

Las instrucciones repetitivas (o bucles) son aquellas que permiten variar o alterar la secuencia normal de ejecución de un programa haciendo posible que un grupo de operaciones (acciones) se repita un número determinado o indeterminado de veces, dependiendo del cumplimiento de una condición.

Veremos tres tipos:

 

5.6.1 Estructura repetitiva Mientras (While)

En la estructura Mientras o "WHILE" el bloque de acciones se repite mientras la condición sea cierta, evaluándose siempre la condición antes de entrar en el bucle, por ello es posible que las acciones no se ejecuten nunca. En diagrama de flujo será de la siguiente forma:

T2_16

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Vamos a implementar un ejemplo que acabaremos implementandoló en Java: Se pide un programa que pida por consola un número entero y que muestre por pantalla la tabla de multiplicar de dicho número. La salida que buscamos implementar es la siguiente:

 

En diagrama de flujo la solución será de la siguiente forma:

T2_17

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente:

En Dart:

 

5.6.2 Estructura repetitiva Hacer Hasta (Do-While)

En la estructura "hacer hasta" o “DO-WHILE”, el bloque de instrucciones se repite mientras que la condición sea cierta, y la condición se evalúa al final del bloque por lo que siempre se ejecutarán al menos una vez el bloque de instrucciones. En diagrama de flujo será de la siguiente forma:

T2_18

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Siguiendo el ejemplo anterior vamos a implementar el mismo programa pero usando esta estructura. En diagrama de flujo quedará de la siguiente forma:

T2_19

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente:

En Dart:

 

5.6.3 Estructura repetitiva Para (For)

En la estructura Para o “FOR” se conoce de antemano el número de veces que se ejecutará el bloque de instrucciones.

El bloque de acciones se repite mientras que la condición sea cierta, evaluándose siempre la condición antes de entrar en el bucle, por ello es posible que las acciones no se ejecuten nunca.

Esta explicación es idéntica a la del bucle WHILE, pero un bucle FOR debe cumplir las siguientes características:

  1. La variable contador se inicializa con un valor inicial.

  2. La condición siempre debe ser: variable_contador <= valor_final.

  3. En cada interacción, la variable contador se incrementa en un determinado valor de forma automática.

En diagrama de flujo será de la siguiente forma:

T2_20

En pseudocódigo nos lo encontraremos de la siguiente forma:

 

Siguiendo el ejemplo anterior vamos a implementar el mismo programa pero usando esta estructura. En diagrama de flujo quedará de la siguiente forma:

 

T2_21

En pseudocódigo será de la siguiente forma:

 

En Java nos lo encontraremos de la siguiente:

En Dart:

 

6 Elementos auxiliares

Los elementos auxiliares son variables que realizan funciones especificas dentro de un programa, y por su gran utilidad, frecuencia de uso y peculiaridades, conviene hacer un estudio separado de las mismas.

 

6.1 Contadores

Si vamos a repetir una acción un número determinado de veces y esa variable se va a incrementar siempre en una cantidad constante, se denomina contador. Sería útil llamarla algo así como

cont, conta, contador...

Si tuviéramos varios contadores dentro de un programa podríamos llamarlos

cont1, cont2...

Se utilizan en los siguientes casos:

6.2 Acumuladores

Si por el contrario, dicho objeto se va incrementando de forma variable se denomina acumulador. Deberemos llamarla:

acu, acum, acumula, acumulador, suma... u otra palabra significativa.

Se utiliza en aquellos casos en que se desea obtener el total acumulado de un conjunto de cantidades, siendo inicializado con un valor cero.

También en ocasiones hay que obtener el total acumulado como producto de distintas cantidades, en este caso se inicializará a uno. Por ejemplo: imprimir la suma de N edades.

 

6.3 Interruptores

Por último, tenemos ciertas variables que pueden tomar dos valores: cierto o falso. Se les denomina interruptores o switches y su función es que ciertas instrucciones se ejecuten mientras tenga un valor determinado.

Se utiliza para:

Por ejemplo: introducir N edades y acabar al introducir un 99.

 

7 Vectores y matrices

7.1 Introducción

Con lo aprendido hasta ahora podríamos resolver los siguientes tipos de problemas:

Sería posible resolver estos problemas, pero deberíamos declarar 50 variables en el primero y n variables en el segundo para poder resolverlos.

Una de las principales dificultades que se observan con estos problemas es que en la resolución de ambos es necesario almacenar la totalidad de los datos a procesar, por lo que no es eficiente.

Para resolver este tipo de problemas se usa una estructura de datos denominada vector.

 

7.2 Estructura de datos: vector

Una estructura de datos es un conjunto de datos con un cierto orden. Las estructuras de datos pueden ser dinámicas o estáticas.

Un vector (o arreglo unidimensional) es una estructura de datos en la cual se almacena un conjunto de datos de un mismo tipo, es decir que un arreglo es una lista de n elementos que posee las siguientes características:

T2_22

Posición: 1 (que es indicada por el subíndice)

Contenido: vector[1] = 4

 

EJEMPLO 1: Cargar 10 elementos en un vector, sumarlos y mostrar el resultado por pantalla.

Pasos para resolver este problema:

  1. Introducir los datos en el vector de 10 elementos

  2. Leer el vector.

  3. Sumar los elementos.

  4. Mostrar el resultado en pantalla.

 

En pseudocódigo sería:

 

En Java sería:

En Dart:

 

EJEMPLO 2: Dados 50 números enteros, obtener el promedio de ellos (promedio entero). Mostrar por pantalla dicho promedio y los números ingresados que sean mayores que él.

Los pasos para resolver este problema son:

  1. Pedir los valores de los 50 números.

  2. Calcular el promedio y mostrarlo por pantalla.

  3. Comprobar si cada valor de los 50 es superior al promedio y si es así mostrarlo por pantalla.

     

En pseudocódigo sería:

 

En Java sería:

En Dart:

 

7.3 Vectores paralelos

Dos o más vectores que utilizan el mismo subíndice para acceder a elementos de distintos vectores se denominan vectores paralelos. Estos vectores pueden ser procesados paralelamente.

T2_23

Como vemos en la imagen anterior, tenemos dos vectores de 5 elementos cada uno. En uno se almacenan los nombres de personas en el otro las edades de dichas personas.

Decimos que el vector nombres es paralelo al vector edades si en la componente 0 de cada vector se almacena información relacionada a una persona (Juan - 12 años). Es decir hay una relación entre cada componente de los dos vectores.

Esta relación la conoce únicamente el programador y se hace para facilitar el desarrollo de algoritmos que procesen los datos almacenados en las estructuras de datos.

Vamos a poner un ejemplo: se pide desarrollar un programa que permita cargar 5 nombres de personas y sus edades respectivas. Luego de realizar la carga por teclado de todos los datos imprimir los nombres de las personas mayores de edad (mayores o iguales a 18 años).

En pseudocódigo sería:

 

La implementación en Java sería:

 

7.4 Matrices.

Una matriz es una estructura de datos que permite almacenar un CONJUNTO de datos del MISMO tipo.

Con un único nombre se define la matriz y por medio de DOS subíndices hacemos referencia a cada elemento de la misma (componente).

 

T2_24

Como se ve en la imagen, se tiene un arreglo (o matríz) de 3 filas por 4 columnas siendo n=3 y m=4, la lógica de la estructura es muy similar a los arreglos unidimensionales (o vectores), cada índice inicia en 0 hasta el tamaño-1 por esa razón las posiciones de las filas van de 0 a 2 y el de las columnas de 0 a 3 en una matríz de 3x4.

Se puede ver que la matriz anterior es como si fueran 3 arreglos de tamaño 4 juntos, pues se puede entender cada fila como uno de ellos, por lo tanto la declaración, construcción e inicialización es muy similar.

Para definir una matriz hay que pensar en como se define un vector unidimensional, ya que en este caso, en vez de tener una sola dimensión tiene varias. Por ello, en vez de tener un corchete tendrá dos en su declaración. Hay 2 formas de declarar las matrices:

Donde tipoDato define el tipo de dato de cada uno de los valores que puede contener la matriz.

En cuanto a la construcción de la matriz existen 2 formas de construirlas:

  1. la primera se usa cuando inicialmente no sabemos cuáles son los valores que va a contener la matriz, ya que luego serán ingresados, se crea con la siguiente estructura:

    Identificador = new <tipoDato> [filas] [columnas];

    Ej. matrizDeEnteros = new int[3] [4];

  2. Cuando sabemos con exactitud cuáles son los valores que va a contener la matriz, aquí el proceso de construcción e inicialización se hace directo y se realiza de la siguiente manera:

    Identificador = { {valor, valor,valor}, {valor, valor,valor}, {valor, valor,valor} };

    int matriz[ ][ ] = {{1,2,3},{4,5,6},{7,8,9}};

El acceso a sus valores, tanto como asignación como para obtener el valor se realiza similar a los arreglos unidimensionales (vectores), o sea con los corchetes e indicando la posición a la que deseamos acceder (recordar que las posiciones van de 0 a n-1, siendo n el tamaño del arreglo).

matriz[3][2] = 7;

System.out.println("El valor en la posición (3,2) es =" + matriz[3][2]);

Cuando hablamos de la posición (3,2) de una matriz nos estamos refiriendo a los componentes x e y de la matriz, es decir, las posiciones vienen representadas por (x,y).

T2_25

En la imagen anterior, si cogemos cualquier posición dentro del array como una posición (x,y):

7.4.1 Lectura y escritura de matrices

Para realizar una inserción de datos o una lectura de una matriz utilizaremos dos bucles Para, ya que usaremos un bucle para recorrer el componente "x" y otro bucle para recorrer el componente "y".

En pseudocódigo quedaría de la siguiente forma:

 

Vamos a desarrollar un ejercicio como ejemplo. Para ello vamos a definir una matriz de 5x5, la cual tendrá estos valores. La idea es recorrer todos los valores de la matriz y sumarlos.

 

T2_26

 

 

Si lo implementamos en Java queda de la siguiente forma:

En Dart: