En esta segunda lección os vamos a resumir los conceptos más importantes relacionados con la representación de algoritmos en pseudocódigo, centrándonos en las principales acciones y estructuras de control que debemos conocer para seguir el curso de sistemas digitales. La especificación de sistemas digitales mediante algoritmos es uno de los aspectos centrales de nuestro curso. Muchas veces para poder definir algoritmos, vamos a utilizar secuencias de instrucciones muy similares a las instrucciones de los lenguajes de programación. Si ya conocéis otros lenguajes, por ejemplo C, Java, etcétera, el contenido de este tema os resultará muy familiar. ¿Cómo se define un algoritmo? Es un procedimiento no ambiguo que resuelve un problema, nos indica una secuencia de operaciones a realizar para conseguir el resultado deseado, como por ejemplo, un cálculo complejo, el control de algún proceso, etc. El algoritmo es independiente del lenguaje de programación que se utilizará. Para poder representar los algoritmos existen diferentes alternativas, el lenguaje natural, el pseudocódigo, los diagramas de flujo y los lenguajes de programación. ¿Qué se entiende por pseudocódigo? Es una forma de descripción informal de un algoritmo con una sintaxis cercana a los lenguajes de programación. El pseudocódigo nos permite describir un algoritmo utilizando una mezcla de frases en lenguaje común, instrucciones de programación y palabras clave que definen las estructuras básicas. Aquí presentamos un resumen de las principales acciones y estructuras de control que podemos utilizar cuando describimos un algoritmo mediante pseudocódigo. Comenzaremos definiendo las asignaciones, los operadores, entre los que destacaremos los de comparación, los lógicos y los operadores aritméticos. Las estructuras de control que utilizaremos principalmente son las estructuras de selección y estructuras de iteración. Y finalmente definiremos el concepto de procedimiento o sub-rutina. Asignaciones. La acción denominada asignación nos permite almacenar un valor en una variable. Se representa por este símbolo. El valor puede ser una constante o el resultado de la evaluación de una expresión. Primero se evalua siempre la expresión de la derecha para asignarla a la variable que tenemos en la izquierda, por ejemplo, en este caso tenemos la asignación de 3 al valor X, 2 al valor Y, y 1 al valor Z. En la siguiente asignación, el valor Y que recibe será 2 por X, por tanto 2 por 3, más Y, que será el valor 2, más Z que vale 1. Por tanto, estamos asignando a la variable Y el valor 9. Operadores. En las descripciones en pseudocódigo, podemos encontrarnos operadores de tipo comparación, aritméticos y lógicos. Los principales operadores de comparación son "menor que", "mayor que", "igual que", "menor o igual que", que lo podemos representar con este símbolo o con este otro, "mayor o igual que", y el símbolo "diferente". Aritméticos. Podemos encontrarnos cuatro tipos, el operador "suma", el operador "resta", "producto" y "división". Finalmente dentro de los operadores del tipo lógico, destacaremos el operador "suma lógica", "producto lógico" y "negación". Vamos a proponer un ejercicio. Si tenemos la siguiente descripción en pseudocódigo, ¿Qué valor tendrá la variable X después de la línea 4? ¿Qué valor tendrá la variable Z después de la línea 5? Y ¿qué valor tendrá la variable Y después de la línea 6? Vamos a resolver el ejercicio propuesto. Si tenemos la siguiente descripción en pseudocódigo, ¿Qué valor tendrá la variable X después de la línea 4? Después de la línea 4 hemos asignado a la variable X el valor 2 y a la variable Y el valor 3, por tanto cuando lleguemos a la línea 4, la variable X recibirá la asignación de X, que valía 2, por 3, por tanto el valor que tendrá la variable X será 6. ¿Qué valor tendrá la variable Z después de la línea 5? En este caso la variable Z está recibiendo el valor X que lo acabamos de actualizar a 6 y le estamos sumando el valor de Y que valía 3. Por tanto la variable Z está recibiendo el valor 9. Por último, ¿qué valor tendrá la variable Y después de la línea 6? En este caso la variable Y está recibiendo el valor de X que lo hemos actualizado a seis, el valor de Y que vale 3, y el valor de Z que vale 9, por tanto el valor final será 18. Estructuras de control. Las acciones que podemos describir mediante pseudocódigo se organizan en bloques de acciones que siguen unas estructuras básicas. Las estructuras de selección y las estructuras de iteración. Dentro de las estructuras de selección veremos las estructuras simple, doble y múltiple. Dentro de las estructuras de iteración veremos While y For. Estructuras de selección o decisión. Las estructuras de selección nos permiten tomar decisiones condicionadas a la evaluación de una expresión, normalmente una condición lógica. Veremos la primera, la estructura de selección simple. La sintaxis que utiliza es "If", la evaluación de una condición, entonces realizamos un conjunto de acciones. Desde un punto de vista de diagrama de flujo, veríamos que evaluamos la condición, si es cierta realizamos el conjunto de acciones que describamos, si no es cierta seguimos en secuencia. Por ejemplo, si X es par y X es mayor o igual que 8, realizaremos el conjunto de acciones que podamos incluir en este bloque. Estamos en este caso describiendo que haremos el conjunto de acciones cuando X tenga el valor 8, 10, 12, etc. La estructura de selección doble tiene la siguiente sintaxis, "Si" condición, entonces realizamos la acción 1, "si no" realizamos la acción 2. Desde el punto de vista de diagrama de flujo veremos que analizamos la condición; si es cierta incluimos el conjunto de acciones 1 a realizar; si no es cierta, indicamos el conjunto de acciones 2 a realizar. Por ejemplo, si X es par y X es mayor o igual que 8 realizaremos las acciones 1, si es el caso contrario realizaremos las acciones 2. Por tanto, la diferencia entre este ejemplo y el que hemos visto anteriormente, es que en este segundo realizamos un conjunto de acciones cuando la condición <b>no</b> se cumple. En cambio en el primero solamente realizábamos las acciones cuando se cumplía la condición. Estructuras de selección múltiple. La sintaxis es: "si" condición, entonces realizamos la acción 1; "si no" "si" la condición dos es cierta realizamos la acción 2, "si no" "si" la condición 3 es cierta realizamos la acción 3, y podríamos continuar sucesivamente. Desde un punto de vista gráfico, la realización de esta estructura múltiple sería: evalúo la condición, si es cierta ya defino las acciones a realizar, si no es cierta paso a evaluar una segunda condición. Si es cierta ya evaluamos y hacemos las acciones, si no es cierta pasamos a evaluar una tercera condición y así sucesivamente. Por ejemplo, si A es igual a 1 realizamos en este caso la asignación a la variable Z de la suma de las dos variables X e Y. Si no, si A es igual a 2 lo que estamos realizando es la resta entre dos variables X e Y sobre la variable Z; si no, si la variable vale 3 o cualquier valor superior a 3, lo que estamos realizando es la multiplicación. Por lo tanto, hemos definido con esta estructura múltiple, tres acciones distintas a realizar, la suma, la resta o la multiplicación en función de tres condiciones distintas que hemos evaluado. O bien cuando A vale 1, o bien cuando A vale 2 o bien cuando A vale 3 o un valor superior a 3. Vamos a proponer un ejercicio en el que debemos calcular el pseudocódigo de la operación X dividido entre 2 redondeando por defecto. Para ello analizaremos los diferentes casos. Cuando X es par, el resultado de la división siempre será exacto tanto si X es positivo como si es negativo. Por ejemplo, si X vale 8 el resultado será 8 ente 2, 4. En cambio si X vale -8, el resultado igualmente será realizar (-8) / 2 = -4. Cuando X es impar, el redondeo lo deberemos calcular de manera diferente. Si es negativo el cálculo a realizar será X más 1 dividido por 2. Por ejemplo, si X vale -7 realizaremos (-7) + 1, es decir, -6, (-6) / 2 = -3. Si es positivo el cálculo será (X - 1) / 2. Por ejemplo, si X igual a 7, Y valdrá (7-1) = 6; 6/2 = 3. Vamos a resolver el ejercicio propuesto. Queremos realizar en pseudocódigo la operación X dividido entre dos redondeando por defecto. Los diferentes casos analizados han sido: cuando X es par, el resultado siempre será exacto tanto si es positivo como si es negativo. En cambio cuando X era impar, el redondeo se calculaba de manera diferente, en el caso negativo con esta fórmula y en el caso positivo con esta otra. Por tanto, el pseudocódigo que implementa esta operación será el siguiente: si condición, es decir si X es par, entonces asignamos a la variable Y el valor X/2. Si no, por tanto si X es impar, si X es negativo asignamos a la variable Y el valor (X+1)/2; si no, es decir, si X es positivo asignamos a la variable Y el valor (X-1)/2. Estructuras de selección. Vamos a ver las estructuras de selección. Case. Con esta estructura el resultado de una expresión nos permite escoger entre un conjunto de bloques alternativos. Evaluamos la condición X, y si toma el valor 1, realizaremos el conjunto de acciones 1, cuando toma el valor 2 las acciones 2, valor 3 acciones 3 y así sucesivamente. Podemos ver el esquema en diagrama de bloques en el que evaluamos la condición y en función de su valor nos derivaremos hacia una de las posibles alternativas que hayamos establecido. Por ejemplo, si X es un dígito decimal y queremos tener su representación en binario sobre la variable Y, utilizaremos la estructura Case. "Case" X valga 0, asignamos a la variable Y su representación en binario, 0000. Cuando vale 1 le asignamos el valor 0001. Cuando la variable X toma el valor 9 le asignamos a la variable Y el valor 1001. Estructuras de iteración. Estas estructuras nos permiten repetir acciones condicionadas a la evaluación de una expresión. La primera de ellas que veremos es la estructura "While". Es un esquema iterativo con la condición de finalización del bucle al principio. El número de iteraciones es variable y el bloque de acciones se repite mientras la evaluación de la condición sea cierta. Por tanto, hacemos, "mientras condición cierta" realizar el bucle involucrado en las acciones. Estructura "For". En este esquema iterativo el número de iteraciones es constante y está controlado por un contador que se inicializa al valor mínimo. Cada vez que se ejecuta el bucle se incrementa hasta llegar a un valor máximo. Vamos a proponer el siguiente ejercicio: Dados dos vectores de 8 posiciones a(0), a(1), hasta la posición a(7) y el vector x(0), x(1) hasta la posición x(7), queremos realizar el cálculo de a(0) por x(0), a(1) por x(1), sumándole los siguientes valores hasta el valor a(7) por x(7). Vamos a resolver el ejercicio propuesto. Queríamos, dados dos vectores, resolver esta fórmula. Para ello utilizamos la estructura "For". Inicializamos primero una variable que le llamamos acumulador con el valor 0 y hacemos una estructura repetitiva en la que el índice irá cambiando desde el valor 0 hasta el valor 7 y nos irá acumulando en la variable "acc", acumulador, los diferentes cálculos. La primera vez realizaremos el cálculo de "acumulador toma el valor 0 más a(0) por x(0)"," y así sucesivamente iremos incrementando la variable y hasta el valor máximo. Vamos a comentar el último apartado correspondiente a esta lección. Vamos a ver los procedimientos o subrutinas. Un procedimiento es un conjunto de acciones a realizar que resuelven una tarea concreta. A cada procedimiento se le asigna un nombre con el que puede ser llamado desde otra parte de nuestro programa principal. Vemos en este esquema donde tenemos un algoritmo en el que hemos definido una llamada a un procedimiento. Se ejecutan el conjunto de instrucciones involucradas en dicho procedimiento y al acabarse devuelve el control a la instrucción siguiente desde donde se produjo la llamada. Cuando llamamos a un procedimiento sólo debemos dar valor a las variables que intervienen, los parámetros, y dichos valores se asignarán cuando realicemos la llamada al mismo. Los procedimientos nos facilitan el diseño, la estructura y la comprensión de los algoritmos y nos evitan repetir sentencias en el algoritmo principal. Vamos a explicar un ejemplo de uso de los procedimientos. Queremos realizar el cálculo de a.√x + b.√y por raíz cuadrada Queremos realizar el cálculo de a.√x + b.√y por raíz cuadrada En este ejemplo queremos hacer dos cálculos muy similares, √x y √y, por lo que parece adecuado definir un procedimiento que vamos a denominar raíz_cuadrada y que tendrá tres parámetros. El primero corresponde al valor sobre el cual queremos realizar el cálculo. El segundo parámetro corresponde a la variable sobre la que vamos a devolver el resultado. Y finalmente el parámetro n nos está definiendo los dígitos fraccionarios con los que vamos a trabajar. Por lo tanto la propuesta de nuestro algoritmo será la siguiente: primero una llamada al procedimiento raíz_cuadrada en este caso con el parámetro x de entrada puesto que queremos realizar el primer cálculo. Se devolverá el resultado sobre la variable u, y vamos a trabajar con 16 dígitos fraccionarios. Una vez el procedimiento ha realizado el cómputo se devuelve el resultado sobre la variable u, por lo tanto podemos pasar a realizar el primer producto del valor a por la variable u que contendrá el valor de √x, guardándose este primer cálculo en la misma variable u. Ahora podemos hacer de nuevo una llamada al procedimiento raíz_cuadrada, en este caso con el parámetro de entrada y. El resultado nos lo va a devolver sobre la variable r, y de nuevo trabajamos con 16 dígitos fraccionarios. Una vez ejecutado el procedimiento, la variable r contendrá el valor de √y, por lo tanto podremos hacer este producto de b por r; por tanto b por √y, almacenándose de nuevo en la misma variable r. Finalmente acabamos ya de calcular este valor seguido del siguiente por lo que podemos proceder a la última suma. Vamos a ver esta ejecución de un modo más gráfico. En este algoritmo como hemos indicado se produce la primera llamada al procedimiento con la variable x de entrada. Ejecutamos el procedimiento para que calcule el valor de la √x, y este procedimiento nos va a devolver el resultado sobre la variable u. Una vez tenemos el cálculo de √x en la variable u podemos multiplicarla por el valor a y almacenarlo en la misma variable u. De nuevo vamos a proceder ahora a la llamada del procedimiento, en este caso con el valor de entrada y. Ejecutamos de nuevo el procedimiento y nos devuelve el resultado sobre la variable r, trabajando también con 16 dígitos fraccionarios. Una vez tenemos el valor en la variable r podemos multiplicarla por el valor b para almacenar como hemos indicado el resultado en la variable r. Finalmente sólo nos queda sumar la variable u más la variable r almacenando el resultado final en la variable z. Como resumen de esta lección, hemos estado viendo la representación de los algoritmos en formato pseudocódigo. Concretamente hemos visto qué acciones y qué estructuras de control son las más habituales, las asignaciones y operadores, estructuras de control del tipo selección como son la simple, la doble o múltiple y estructuras de control del tipo iteración, los While y For. Para acabar hemos estado viendo el concepto de procedimiento o subrutina.