Algoritmos en PSeInt Jéfferson Beltrán Morales Algoritmos en PSeInt Algoritmos en PSeInt Jéfferson Beltrán Morales 2 3 Jéfferson Beltrán Morales Algoritmos en PSeInt. Todos los derechos reservados. No está permitida la reproducción total o parcial de este libro, ni su tratamiento informático, ni la transmisión de ninguna forma o por cualquier medio, ya sea electrónico, mecánico, por fotocopia u otros métodos, sin el permiso previo y por escrito del titular del Copyright. Copyright © 2015. Jéfferson Beltrán Morales. ISBN- 978-9942-21-557-4 Septiembre 2015 Quito – Ecuador Algoritmos en PSeInt CONTENIDO INTRODUCCIÓN. .............................................................................................. 6 OBJETIVO.......................................................................................................... 6 EL LIBRO COMO HERRAMIENTA DOCENTE.................................................. 6 ORGANIZACIÓN DEL LIBRO. ........................................................................... 6 ACERCA DEL AUTOR. ...................................................................................... 7 PARTE I: ALGORITMOS.................................................................................... 8 1 ALGORITMOS. ............................................................................................ 8 1.1 CARACTERÍSTICAS DE LOS ALGORITMOS. ..................................... 8 1.2 PARTES DE UN ALGORITMO. ............................................................ 8 1.2.1 DATOS Y TIPOS DE DATOS. .............................................................. 8 1.2.2 CONSTANTES Y VARIABLES. ............................................................ 9 1.2.3 SENTENCIAS O INSTRUCCIONES. .................................................... 9 1.2.4 OPERADORES Y EXPRESIONES. .................................................... 10 1.2.5 ESTRUCTURAS DE CONTROL. ........................................................ 11 1.3 1.4 PASOS PARA CREAR UN ALGORITMO. .......................................... 11 REPRESENTACIÓN GRÁFICA DE LOS ALGORITMOS. ..................... 12 1.4.1 DESCRIPCIÓN NARRADA. ................................................................ 12 1.4.2 DIAGRAMAS DE FLUJO O FLUJOGRAMAS. .................................... 12 1.4.3 PSEUDOCÓDIGO............................................................................... 14 1.5 PSEINT. .............................................................................................. 14 PARTE II: ALGORITMOS EN PSeInt. .............................................................. 16 2 PROGRAMACIÓN ESTRUCTURADA....................................................... 16 2.1 ESTRUCTURA SECUENCIAL. ........................................................... 23 2.2 ESTRUCTURAS SELECTIVAS O CONDICIONALES. ....................... 27 2.2.1 ESTRUCTURA SELECTIVA “SI-ENTONCES”. .................................. 27 2.2.1.1 EXPRESIONES LÓGICAS. .......................................................... 32 2.2.1.2 ESTRUCTURAS ANIDADAS........................................................ 36 2.2.2 ESTRUCTURA DE SELECCIÓN MÚLTIPLE “SEGÚN”...................... 41 2.3 ESTRUCTURAS REPETITIVAS O ITERATIVAS. .............................. 46 2.3.1 ESTRUCTURA REPETITIVA “PARA”. ................................................ 47 4 Jéfferson Beltrán Morales 5 2.3.1.1 CONTADOR. ................................................................................ 48 2.3.1.2 ACUMULADOR O TOTALIZADOR. ............................................. 48 2.3.2 ESTRUCTURA ITERATIVA “MIENTRAS”. ......................................... 51 2.3.2.1 CENTINELAS. .............................................................................. 54 2.3.2.2 BANDERAS. ................................................................................. 56 2.3.3 ESTRUCTURA ITERATIVA “REPETIR”. ............................................ 59 2.4 ESTRUCTURA DE DATOS Y ARREGLOS............................................ 64 2.4.1 ESTRUCTURA DE DATOS. ............................................................... 65 2.4.2 ARREGLOS. ....................................................................................... 65 2.4.2.1 ARREGLO UNIDIMENSIONAL. ................................................... 69 2.4.2.1.1 PARTES DE UN ARREGLO. .................................................... 69 2.4.2.1.2 OPERACIONES CON ARREGLOS. ......................................... 70 2.4.3 ARREGLO BIDIMENSIONAL. ............................................................. 80 2.5 MODULARIDAD. .................................................................................... 84 2.5.1 PROCEDIMIENTOS Y FUNCIONES. ................................................. 85 2.5.1.1 IMPLEMENTACIÓN DE PROCEDIMIENTOS Y FUNCIONES..... 86 2.6 RECURSIVIDAD. ................................................................................. 104 2.7 ESTILO DE PROGRAMACIÓN. ........................................................... 110 BIBLIOGRAFÍA. ............................................................................................. 112 Algoritmos en PSeInt ALGORITMOS EN PSeInt INTRODUCCIÓN. Este libro muestra al estudiante y futuro programador, los conceptos básicos y fundamentos de los algoritmos computacionales, mediante el diseño y elaboración de algoritmos básicos y sencillos que se presentan comúnmente en el proceso de aprendizaje, utilizando técnicas algorítmicas estructuradas y modulares, como pseudocódigo y diagramas de flujo y herramientas especializadas, y de esta manera entender de manera más sencilla el fascinante mundo de la algoritmia y programación. Ya que un programador, es decir, la persona que diseña y construye sistemas computacionales o aplicaciones de software, antes de comenzar a interactuar con la computadora, tiene que aprender a pensar diferente a las demás personas, para poder analizar y resolver problemas mediante aplicaciones de software, los cuales primero debe plasmarlos en papel o en alguna herramienta de software específica para ello como PSeInt, para luego implementarlos en un lenguaje de programación. OBJETIVO. El objetivo del libro es servir como material de lectura y estudio en un curso introductorio a la programación, mostrando los fundamentos de los algoritmos computacionales mediante técnicas de programación estructurada y modular, usando PSeInt como herramienta para el diseño de algoritmos en pseudocódigo y/o diagramas de flujos. EL LIBRO COMO HERRAMIENTA DOCENTE. El libro es una guía y aporte sobre los fundamentos de algoritmos computacionales para los docentes de Programación I, de las carreras de Ingeniería Informática, Ingeniería Matemática, Ingeniería en Computación Gráfica e Ingeniería en Diseño Industrial de la Universidad Central del Ecuador. Puede ser extendido su uso en otras materias introductorias a la programación y algoritmos, pues las bases de la algoritmia son similares, está pensando su uso para la mitad de un semestre de un primer curso de programación, no pretende ser un manual de PSeInt, sino una guía práctica para el diseño de algoritmos mediante ejemplos, usando pseudocódigo y diagramas de flujo en PSeInt, mediante técnicas de programación estructurada y modular. La bibliografía principal se tomó del sitio web del desarrollador de PSeInt [14]. ORGANIZACIÓN DEL LIBRO. El libro se dividió en dos partes para efecto de un estudio gradual y de fácil lectura. Se inicia enunciando conceptos básicos sobre algoritmos 6 7 Jéfferson Beltrán Morales computacionales, para luego ponerlos en práctica en la segunda parte, con el diseño de algoritmos usando técnicas de programación estructurada y modular. Se incluyen ejemplos en pseudocódigo y diagramas de flujo escritos en PSeInt. ACERCA DEL AUTOR. El autor tiene 15 años de experiencia como docente universitario en materias relacionadas a la matemática aplicada y al desarrollo de software. En el mismo número de años, es responsable del Desarrollo de Software y de la Gestión de Tecnología de Información y Comunicaciones en empresas privadas. Algoritmos en PSeInt PARTE I: ALGORITMOS. 1 ALGORITMOS. Un algoritmo es un método para resolver un problema mediante una serie de pasos precisos, definidos y finitos. Es un conjunto de reglas para resolver determinado problema, describiendo de forma lógica su solución. Cada una de las acciones de que consta un algoritmo computacional se denomina sentencia (instrucciones o acciones) y éstas deben ser escritas en términos de cierto lenguaje comprensible para el computador, que es el lenguaje de programación [2] [15] [16]. 1.1 CARACTERÍSTICAS DE LOS ALGORITMOS. Las características que debe satisfacer un algoritmo computacional son [2] [16]: Entrada. Son cero o más parámetros (datos) que ingresan al algoritmo para ser usados dentro de él. Salida. Al menos un dato es producido como salida del algoritmo. Exactitud/precisión. Cada instrucción debe ser clara y sin ambigüedad (hacen lo que deben hacer). Finito. Terminará después de un número finito de pasos. Eficiente. Cada instrucción puede ser verificada por una persona con una prueba manual que satisfaga los requerimientos planteados por el problema. Repetible. Dada una misma entrada, siempre debe dar la misma salida. 1.2 PARTES DE UN ALGORITMO. Los algoritmos computacionales se construyen utilizando un pseudolenguaje que usa elementos simples para su escritura, centrando la atención en los conceptos de la algoritmia computacional, antes que en la dificultad propia de un lenguaje de programación. Entre ellos se tienen: 1.2.1 DATOS Y TIPOS DE DATOS. Las diferencias piezas de información con las que un algoritmo computacional trabaja se conoce colectivamente como “datos”. Todos los datos tienen un “tipo” asociado con ellos, que determina la naturaleza del conjunto de valores que puede tomar. Por ejemplo, un dato puede ser un simple carácter, tal como ‘B’, un valor entero como 36, un número real como 5.75, una cadena de 8 Jéfferson Beltrán Morales 9 caracteres como “Hola Mundo”, un valor de verdad como Verdadero o Falso, entre otros [16]. 1.2.2 CONSTANTES Y VARIABLES. Los algoritmos computacionales y programas necesitan almacenar datos temporalmente para poder procesarlos y generar así la salida esperada. Estos datos, a grandes rasgos, pueden clasificarse en dos grupos [16]: A un dato cuyo valor no puede cambiar (se mantiene constante) durante la ejecución de un programa se lo denomina Constante. Las constantes deben ser declaradas y asignadas un valor antes de su utilización. En cambio, los datos de un programa cuyo valor puede cambiar durante la ejecución del mismo se conocen como Variables. Una variable es, en realidad, una posición o localidad de memoria donde se puede almacenar información, tiene un nombre (Nombre de la Variable), y almacena un valor (Valor de la Variable). Existen tantos tipos de variables como tipos de datos diferentes. 1.2.3 SENTENCIAS O INSTRUCCIONES. Las sentencias describen acciones algorítmicas que pueden ser ejecutadas. En general, las sentencias se clasifican en ejecutables (especifican, por ejemplo, operaciones de cálculos aritméticos y entradas/salidas de datos) y no ejecutables (no realizan acciones concretas ni afectan a la ejecución del programa, sino que ayudan a su legibilidad) como la declaración de variables [16]. Las sentencias se clasifican, según su tipo y número, en: Sentencias simples: Son sentencias que no contiene ninguna otra sentencia. El ejemplo más típico de sentencia simple, es la sentencia de asignación, la cual se utiliza para almacenar un valor en una variable. La operación de asignación suele representarse en pseudocódigo con el símbolo ‘’, para denotar que el valor situado a su derecha se almacena en la variable situada a la izquierda: o variable Valor o Ejemplo: suma0 (la asignación sólo será válida si el valor es válido para el tipo de dato definido para la variable). Otros ejemplos de sentencias simples son las de entrada/salida. Algoritmos en PSeInt Sentencias estructuradas: Son sentencias compuestas de otras sentencias que se ejecutan en secuencia, condicionalmente o repetidamente. 1.2.4 OPERADORES Y EXPRESIONES. Las variables y constantes se pueden procesar utilizando operaciones y funciones adecuadas para sus tipos. Se denomina expresión a un conjunto de variables y/o constantes unidas por operadores [16]. Un operador es un símbolo o palabra que sirve para indicar la realización de una acción entre uno o dos valores que son llamados operandos [16]. Si en una expresión existe más de una operación, debe tenerse en cuenta que existen una serie de reglas para definir la prioridad en la que éstas se ejecutarán. Por este motivo, se suele utilizar paréntesis para establecer la prioridad de aplicación de los operandos. Existen diversos tipos de operadores, por ejemplo [16]: Aritméticos: Son apropiados únicamente para tipos de datos numéricos. Ejemplos de operadores aritméticos son: “ + ”, “ - ”, “ * ” y “ / ”, etc., los cuales permiten obtener el resultado de la suma, la resta, la multiplicación y la división de dos datos respectivamente. Su resultado es un número. De relación: Los operadores de relación (o relacionales), se utilizan para expresar condiciones y describen una relación entre dos valores. Ejemplos de operadores relacionales son: “ < ” (Menor que), “ >” (Mayor que), “ = ” (Igual a) y “ <> ” (Distinto a), etc. Su resultado es un valor de verdad. Los operadores aritméticos y los relacionales se utilizan de la siguiente forma: variable o constante operador variable o constante. Por ejemplo: a + b, c/d, a<b, c<>d, etc. Lógicos: Estos operadores se utilizan con valores lógicos, de forma similar al modo en que los operadores aritméticos se utilizan con los valores numéricos. Estos operadores trabajan con operandos que son expresiones lógicas. La operación and (y) combina dos condiciones simples y produce un resultado verdadero sólo si los dos operandos son verdaderos. La operación or (o) es verdadera si uno de los dos operandos 10 11 Jéfferson Beltrán Morales es verdadero. La operación not (no) actúa sobre una sola condición simple u operando y simplemente, niega (o invierte) su valor. Su resultado es un valor de verdad. Existen otros operadores lógicos además de los mencionados. Ejemplo de expresión: Cos (2*pi * X) + 75.69 * X 1.2.5 ESTRUCTURAS DE CONTROL. El concepto de flujo de control a través de un algoritmo y luego del programa, se refiere al orden en que se ejecutan las acciones individuales del mismo. Aunque un flujo normal de un programa estructurado es lineal, existen métodos que permiten salir del flujo lineal, a través del uso de las llamadas estructuras de control [2]. Las estructuras de control son fundamentales en el diseño de un algoritmo computacional, pues son métodos para especificar el orden en que las instrucciones de un algoritmo se ejecutarán. Las estructuras de control son [2]: Secuenciales: Sucesión simple de dos o más instrucciones o sentencias (una tras otra). De Selección o Condicionales: Bifurcación condicional de una o más instrucciones. De Repetición o Iteración: Repetición de una instrucción mientras se cumple una condición. 1.3 PASOS PARA CREAR UN ALGORITMO. Para diseñar un algoritmo se debe comenzar por identificar las tareas más importantes para resolver el problema y disponerlas en el orden en que han de ser ejecutadas. Los pasos para crear un algoritmo son [2] [16]: Análisis del problema. Para resolver cualquier problema, se debe comprenderlo completamente, es una actividad que debe realizarse con mucha responsabilidad, nunca se debe diseñar un algoritmo sin haberse hecho un análisis completo del problema. Diseño del algoritmo. Se describen los pasos para resolver el problema. Es mejor solucionar problemas pequeños. Los pasos generales son: o Lectura de datos. o Validación de datos. Algoritmos en PSeInt o Lógica de negocio: Cálculos, lógica / comparación, proceso de datos, etc. o Salida de resultados. Prueba del algoritmo. Se realiza las pruebas manuales necesarias mediante casos de prueba, para comprobar que el algoritmo arroje los resultados deseados. En general, los algoritmos reciben datos de entrada, los procesa y genera la salida [15]. El proceso se refiere a los cálculos que se deben hacer para obtener la salida deseada. A partir de los datos de entrada, se procesan, y se consigue la salida. Los algoritmos generalmente se representan mediante diagramas de flujo y pseudocódigo. 1.4 REPRESENTACIÓN GRÁFICA DE LOS ALGORITMOS. 1.4.1 DESCRIPCIÓN NARRADA. Este algoritmo se caracteriza porque sigue un proceso de ejecución común y lógico, se describe textualmente paso a paso cada una de las actividades a realizarse [16] [2]. Ejemplo: Algoritmo para asistir a clases: 1. Levantarse 2. Bañarse 3. Vestirse 4. Desayunar 5. Cepillarse los dientes 6. Salir de casa 7. Tomar el autobús 8. Llegar a la Universidad 9. Buscar el aula 10. Ubicarse en un asiento 1.4.2 DIAGRAMAS DE FLUJO O FLUJOGRAMAS. 12 13 Jéfferson Beltrán Morales Son la representación gráfica de la solución algorítmica de un problema. Para diseñarlos se utilizan determinados símbolos o figuras, que representan una acción dentro de la solución. Se utiliza unos símbolos normalizados, con los pasos del algoritmo escritos en el símbolo adecuado, los símbolos se unen con flechas, denominadas líneas de flujo, que indican el orden en que los pasos deben ser ejecutados [16] [2]. Para su elaboración se sigue las siguientes reglas [15]: 1. Se escribe de arriba hacia abajo y de izquierda a derecha. 2. Siempre se usan flechas verticales u horizontales, jamás curvas. 3. Evitar cruce de flujos. 4. En cada paso expresar una acción concreta. La secuencia de un flujo normal en una solución de un problema es: 1. Un inicio. 2. Una lectura o entrada de datos. 3. El proceso de datos. 4. Una salida de información. 5. Un final Ejemplo: Dibujar un diagrama de flujo que imprima en pantalla “Hola Mundo”. Entre las ventajas de usar diagrama de flujos se tiene [16]: o Rápida comprensión de las relaciones. o Análisis efectivo de las diferentes secciones del algoritmo. o Pueden usarse como modelos de trabajo en el diseño de nuevos algoritmos. o Comunicación con el usuario. o Documentación adecuada de los algoritmos. o Codificación eficaz de los algoritmos. o Depuración y pruebas ordenadas de los algoritmos. Entre las desventajas podemos anotar [16]: Algoritmos en PSeInt o Diagramas complejos y detallados suelen ser laboriosos en su planteamiento y diseño. o Acciones a seguir tras la salida de un símbolo de decisión, pueden ser difíciles de seguir si existen diferentes caminos. o No existen normas fijas para la elaboración de los diagramas de flujo que permitan incluir todos los detalles que el usuario desee introducir. o Diagramas de flujo muy grandes son difíciles de leer y seguir su secuencia. 1.4.3 PSEUDOCÓDIGO. Pseudo = falso. El pseudocódigo no es realmente un código, sino una imitación y una versión abreviada de instrucciones reales de los lenguajes de programación. Es una técnica para diseño de algoritmos, que permite definir las estructuras de datos, las operaciones que se aplicarán a los datos y la lógica que tendrá el algoritmo computacional para solucionar un determinado problema. Utiliza un pseudolenguaje muy parecido a nuestro idioma, pero que respeta las directrices y los elementos de los lenguajes de programación. Se concibió para superar las dos principales desventajas de los diagramas de flujo: lento de crear y difícil de modificar sin un nuevo dibujo. La principal ventaja es que, como se parece a un lenguaje de programación, la evolución de pseudocódigo hacia un lenguaje de programación es más fácil, que a partir de un diagrama de flujo [15] [2]. Ejemplo: Escribir un algoritmo que imprima en pantalla “Hola Mundo”. Proceso HolaMundo Escribir 'Hola Mundo'; FinProceso 1.5 PSEINT. PSeInt es una herramienta para asistir a un estudiante en sus primeros pasos en programación. Mediante un simple e intuitivo pseudolenguaje en español (complementado con un editor de diagramas de flujo), le permite centrar su atención en los conceptos fundamentales de la algoritmia computacional, minimizando las dificultades propias de un lenguaje y proporcionando un entorno integrado de trabajo con numerosas ayudas y recursos didácticos [14]. PSeInt, es un pseudoentorno de desarrollo integrado (pseudo IDE) en español, cuyas principales características son [14]: 14 Jéfferson Beltrán Morales 15 Un editor, que proporciona el medio para introducir el texto y los símbolos que constituyen el pseudocódigo fuente. Un compilador que convierte el pseudocódigo en código de un lenguaje de programación que la computadora puede comprender y ejecutar, de esta manera se puede ejecutar un algoritmo que realmente no es ejecutable. Un depurador que ayuda a analizar y corregir errores en tiempo de ejecución. Ayuda (manuales, tutoriales, ejemplos, etc.) integrada. Convierte diagramas de flujo en pseudocódigo y viceversa. Convierte pseudocódigo a código de varios lenguajes de programación, entre ellos: C, C++, C#, Java, JavaScript, MatLab, Pascal, PHP, Python 2, Python 3, QBasic Visual Basic. El presente libro, no pretende ser un manual de uso de PSeInt, sino una guía de cómo crear algoritmos con la ayuda de PSeInt. La configuración de Opciones de Lenguaje que se usó en los ejemplos del libro, es la enviada por la Universidad Central del Ecuador, pues hace que el pseudocódigo en PSeInt se parezca a un lenguaje de programación, se trata de que la transición a un lenguaje de programación sea más fácil. Algoritmos en PSeInt PARTE II: ALGORITMOS EN PSeInt. 2 PROGRAMACIÓN ESTRUCTURADA. Programación Estructurada es una técnica en la cual la estructura de un algoritmo o programa se lo realiza tan claramente como sea posible mediante el uso de tres estructuras de control [2] [16]: Secuenciales: Sucesión simple de dos o más instrucciones, sentencias o acciones (una tras otra). Selectivas o Condicionales: Bifurcación condicional de una o más instrucciones. Repetitivas o Iterativas: Repetición de una instrucción mientras se cumple una condición. Escribiremos los algoritmos bajo el paradigma de la programación estructurada, para ello usaremos un pseudolenguaje cuyas características son [14]: Sintaxis sencilla (sin la complejidad de los lenguajes de programación). Manejo de las estructuras básicas de control. Sólo 3 tipos de datos básicos: o Numérico: Números, tanto enteros como reales. Para separar decimales se utiliza el punto. Ejemplos: 12 23 0 -2.3 3.14 [14]. o Lógico: Sólo puede tomar dos valores: VERDADERO o FALSO [14]. o Caracter: Caracteres o cadenas de caracteres encerrados entre comillas (pueden ser dobles o simples). Ejemplos 'hola' "hola mundo" '123' 'FALSO' 'etc'. (generalmente se usa comillas simples para un carácter y dobles para cadena de caracteres o texto) [14]. Estructuras de datos: Arreglos. Todo algoritmo en pseudocógido tiene la siguiente estructura general [14]: Proceso <NombrePrograma> <acción 1>; <acción 2>; . . . <acción n>; FinProceso 16 17 Jéfferson Beltrán Morales Se inicia con la palabra clave Proceso seguida del nombre del programa, luego le sigue una secuencia de instrucciones (acciones) y finaliza con la palabra FinProceso. Una secuencia de instrucciones es una lista de una o más instrucciones, cada una terminada en punto y coma. No puede haber instrucciones fuera del proceso, aunque si comentarios [14]. Las acciones incluyen operaciones de entrada y salida, asignaciones de variables, condicionales si-entonces o de selección múltiple y/o lazos mientras, repetir o para [14]. Se pueden introducir comentarios luego de una instrucción, o en líneas separadas, mediante el uso de la doble barra ( // ). Todo lo que precede a //, hasta el fin de la línea, no será tomado en cuenta al interpretar el algoritmo. No puede haber instrucciones fuera del programa, aunque si comentarios [14]. Las estructuras no secuenciales pueden anidarse. Es decir, pueden contener otras adentro, pero la estructura contenida debe comenzar y finalizar dentro de la contenedora [14]. En diagrama de flujo en cambio se usa la siguiente estructura general para diagramar algoritmos [14] [2]: Inicio Acciones Fin Constantes e identificadores [14]. o Los identificadores deben constar sólo de letras y números, comenzando siempre con una letra. o Las constantes de tipo carácter se escriben entre comillas ( " ). o En las constantes numéricas, el punto ( . ) es el separador decimal. o Las constates lógicas son Verdadero y Falso. Definición de variables [14]. La instrucción Definir permite explicitar el tipo de una o más variables. Esta definición puede ser opcional u obligatoria, aunque la asignación de tipos a los datos tiene dos objetivos principales: 1. Detectar errores de operaciones en los programas durante la fase de codificación. 2. Determinar cómo se ejecutarán las operaciones entre datos. Algoritmos en PSeInt Una operación de suma no tiene sentido con caracteres de texto, sólo con números. Por consiguiente, si el compilador detecta una operación de suma de dos caracteres normalmente producirá un error. Incluso entre tipos numéricos la operación de suma se almacena de modo distinto, ya que los números enteros y los reales se almacenan de formas distintas en memoria. A menos que el programa conozca los tipos de datos no puede ejecutar correctamente la operación de suma [14]. A los lenguajes que exigen que todos los datos utilizados deban tener sus tipos declarados explícitamente se los conoce como “fuertemente tipados”. El tipo de un dato puede ser convertido bajo ciertas condiciones a otro tipo. Este mecanismo explícito de conversión de tipos de datos se suele denominar “CAST”. La sintaxis es [14]: Definir <var1> , <var2> , … , <varN> Como [Real/Entero/Logico/Caracter] ; Una variable debe ser definida antes de ser utilizada por primera vez. Ejemplo: Definir var Como Real; // se define la variable var como Real Definir acumulador Como Entero; // acumulador es una variable Entero Constantes [14]. La única constante definida en PSeInt es PI (3.141592653). Asignación [14]. La instrucción de asignación permite almacenar un valor en una variable. <variable> <- <expresión> ; Al ejecutarse la asignación, primero se evalúa la expresión de la derecha y luego se asigna el resultado a la variable de la izquierda. El tipo de la variable y el de la expresión deben coincidir [14]. Ejemplo: var<-1.0; // asigna 1.0 a var acumulador<-acumulador +1; // incrementa en 1 el acumulador 18 Jéfferson Beltrán Morales 19 Entradas [14]. La instrucción Leer permite ingresar información por teclado [14]. Leer <variablel> , <variable2> , ... ,<variableN> ; Esta instrucción lee N valores desde el ambiente (en este caso el teclado) y los asigna a las N variables mencionadas. Pueden incluirse una o más variables, por lo tanto el comando leerá uno o más valores [14]. Ejemplo: Leer cantidad; Leer valor1, valor2, valor3; Salidas. Las instrucciones Escribir y Escribir Sin Saltar permiten mostrar valores al ambiente [14]. Escribir <exprl>, <expr2> , ... , <exprN> ; Escribir Sin Saltar <exprl>, <expr2> , ... , <exprN> ; Estas instrucciones imprimen en pantalla los valores obtenidos de evaluar N expresiones. Dado que puede incluir una o más expresiones, mostrará uno o más valores. Con Escribir, el cursor luego de imprimir, avanzará a la siguiente línea, si se usa Escribir Sin Saltar, el cursor se mantendrá en la misma línea [14]. Ejemplo: Escribir “Ingrese el nombre: ”; Escribir “El Resultado es: ” , resp*2 ; Otras Acciones Secuenciales [14]. La instrucción "Borrar Pantalla" (o "Limpiar Pantalla") permite, como su nombre lo indica, borrar la pantalla y colocar el cursor en la esquina superior izquierda [14]. Ejemplo: Algoritmos en PSeInt Borrar Pantalla; La instrucción "Esperar Tecla" detiene su algoritmo hasta que el usuario presione una tecla cualquiera de su teclado [14]. Ejemplo: Esperar Tecla; La instrucción "Esperar" también puede utilizarse para pausar el algoritmo durante un intervalo de tiempo predefinido, indicando a continuación de la palabra clave la longitud y unidad de dicho intervalo. Las unidades válidas son Segundos y Milisegundos [14]. Ejemplo: Esperar 3 Segundos; Arreglos [14]. Los arreglos son estructuras de datos homogéneas (todos sus datos son del mismo tipo) que permiten almacenar un determinado número de datos bajo un mismo identificador, para luego referirse a los mismos utilizando uno o más subíndices [14]. Para utilizar un arreglo, primero es obligatorio su dimensionamiento, es decir, definirlo declarando los rangos de sus subíndices, lo cual determina cuantos elementos se almacenarán y como se accederá a los mismos. Con mayor detalle se estudiará los arreglos más adelante [14]. Dimensionamiento [14]. Pseudocódigo: La instrucción Dimension permite definir un arreglo, indicando sus dimensiones [14]. Dimension <identificador> (<max1>,...,<maxN>); Esta instrucción define un arreglo con el nombre indicado en <identificador> y N dimensiones. Los N parámetros indican la cantidad de dimensiones y el valor máximo de cada una de ellas. La cantidad de dimensiones puede ser una o más, y la máxima cantidad de elementos debe ser una expresión numérica positiva [14]. Se pueden definir más de un arreglo en una misma instrucción, separándolos con una coma (,) [14]. 20 Jéfferson Beltrán Morales 21 Dimension <ident1> (<max11>,..,<max1N>),..,<identM>(<maxM1>,..,<maxMN>); Es importante notar que es necesario definir un arreglo antes de utilizarlo. Ejemplo: Definir vector, matriz Como Entero; Dimension vector[100], matriz[10,20]; Diagrama de flujo [14]: El gráfico que se usa en diagrama de flujo para la definición de variables y dimensionamiento de arreglos es el mismo que se utiliza para la asignación. Operadores y Funciones [14]. PSeInt dispone de un conjunto básico de operadores y funciones que pueden ser utilizados para la construcción de expresiones más o menos complejas [14]. Las siguientes tablas exhiben la totalidad de los operadores de este lenguaje reducido [14]: Operador Significado Ejemplo Relacionales > Mayor que 3>2 < Menor que 'ABC'<'abc' = Igual que 4=3 <> Diferente que 4<>3 <= Menor o igual que 'a'<='b' >= Mayor o igual que 4>=5 & Conjunción (y / and). (7>4) & (2=1) //falso | Disyunción (o / or). (1=1 | 2=1) //verdadero ~ Negación (no / not). ~(2<5) //falso + Suma suma <- suma+1 * Multiplicación area <- base * altura - Resta stock <- disponible - venta / División porc <- 100 * parte / total ^ Potenciación sup <- 3.41 * radio ^ 2 Lógicos Algebraicos Algoritmos en PSeInt % 22 Módulo (resto de la división resto <- num % 2 entera) La jerarquía de los operadores matemáticos es igual a la del álgebra, aunque puede alterarse mediante el uso de paréntesis. Para el caso de los operadores & y |, la evaluación se realiza en cortocircuito, es decir, que si dos expresiones están unidas por el operador & y la primera se evalúa como Falso, o están unidas por el operador | y la primera se evalúa como Verdadero, la segunda no se evalúa ya que no altera el resultado [14]. A continuación, se listan las funciones integradas disponibles [14]: Función RC(X) o RAIZ(X) ABS(X) LN(X) EXP(X) SEN(X) COS(X) TAN(X) ASEN(X) ACOS(X) ATAN(X) TRUNC(X) REDON(X) AZAR(X) ALEATORIO(A,B) LONGITUD(S) MAYUSCULAS(S) MINUSCULAS(S) SUBCADENA(S,X,Y) Significado Raíz Cuadrada de X Valor Absoluto de X Logaritmo Natural de X Función Exponencial de X Seno de X Coseno de X Tangente de X Arcoseno de X Arcocoseno de X Arcotangente de X Parte entera de X Entero más cercano a X Entero aleatorio en el rango [0;x-1] Entero aleatorio en el rango [A;B] Cantidad de caracteres de la cadena S Retorna una copia de la cadena S con todos sus caracteres en mayúsculas Retorna una copia de la cadena S con todos sus caracteres en minúsculas Retorna una nueva cadena que consiste en la parte de la cadena S que va desde la posición X hasta la posición Y (incluyendo ambos extremos). Las posiciones utilizan la misma base que los arreglos, por lo que la primera letra será 23 Jéfferson Beltrán Morales CONCATENAR(S1,S2) CONVERTIRANUMERO(X) CONVERTIRATEXTO(S) la 0 o la 1 de acuerdo al perfil del lenguaje utilizado. Retorna una nueva cadena resulta de unir las cadenas S1 y S2. Recibe una cadena de caracteres que contiene un número y devuelve una variable numérica con el mismo. Recibe un real y devuelve una variable numérica con la representación como cadena de caracteres de dicho real. Como habíamos mencionado anteriormente, un problema se puede dividir en acciones elementales o instrucciones, usando un número limitado de estructuras de control (básicas) y sus combinaciones que pueden servir para resolver dicho problema. 2.1 ESTRUCTURA SECUENCIAL. Se caracteriza porque una acción se ejecuta detrás de otra. El flujo del programa coincide con el orden físico en el que se han ido poniendo las instrucciones [2] [16]. Dentro de este tipo podemos encontrar operaciones de inicio/fin, inicialización de variables, operaciones de asignación, cálculo, sumarización, etc. Este tipo de estructura se basa en las 5 fases de que consta todo algoritmo o programa [2] [16]: 1. 2. 3. 4. 5. Definición de variables (Declaración) Inicialización de variables. Lectura de datos. Cálculo. Salida. Ejemplo 1: Se desea encontrar la longitud y el área de un círculo de radio ingresado por teclado. Solución. El objetivo del ejercicio es encontrar la longitud y el área de un círculo con un radio ingresado por teclado. Las salidas serán entonces la longitud y el área. (Fase 5 del algoritmo) Sabemos que la longitud de un círculo viene dada por la fórmula 2 * PI * radio y que el área viene dada por PI * radio al cuadrado. (Fase 4 del algoritmo) Si definimos las variables como: (fase 1 del algoritmo) Algoritmos en PSeInt long = Longitud, area = área, radio = radio, hagamos el algoritmo: Pseudocódigo: Proceso Algoritmo1 //este algoritmo calcula el área y la longitud de un círculo de radio //ingresado por teclado Definir Radio, Area, Long Como Real; //definición de variables Escribir "Ingrese el radio: "; //despliega en pantalla Leer Radio; //Ingresa por teclado el radio Area <- PI * Radio ^ 2; //asignación del valor del área Long <- 2 * PI * Radio ; //asignación del valor de la longitud Escribir "Area: ", Area, " Longitud: ", Long; //salida del algoritmo FinProceso Diagrama de Flujo: 24 Jéfferson Beltrán Morales 25 Ejemplo 2. Leer el sueldo de tres empleados y aplicarles un aumento del 10%, 12% y 15% respectivamente. Desplegar el resultado. Solución. Salidas: Sueldos finales. Entradas: Salarios de los empleados. Datos adicionales: aumentos del 10%, 12% y 15% Cálculos: Sueldo final = sueldo inicial * (1+porcentaje/100) Definición de variables: sueldoFinal1, sueldoFinal2, sueldoFinal3 = los sueldos finales sueldo1, sueldo2, sueldo3 = salarios de los empleados Pseudocódigo: Proceso Algoritmo2 //Leer el sueldo de tres empleados y aplica un aumento del 10, 12 y Algoritmos en PSeInt //15% respectivamente Definir sueldo1, sueldo2, sueldo3 Como Real; //definición de variables Definir sueldoFinal1, sueldoFinal2, sueldoFinal3 Como Real; sueldo1 <- 0; //inicializamos variables sueldo2 <- 0; sueldo3 <- 0; sueldoFinal1 <- 0; sueldoFinal2 <- 0; sueldoFinal3 <- 0; Escribir "Ingrese el sueldo de los 3 empleados"; Leer sueldo1, sueldo2, sueldo3; //lee los sueldos sueldoFinal1 <- sueldo1 * 1.10; //incrementa el 10% al sueldo original //y lo asigna en sueldoFinal1 sueldoFinal2 <- sueldo2 * 1.12; //incrementa el 12% al sueldo original //y lo asigna en sueldoFinal2 sueldoFinal3 <- sueldo3 * 1.15; //incrementa el 15% al sueldo original //y lo asigna en sueldoFinal3 Escribir "Sueldos incrementados: ", sueldoFinal1," ", sueldoFinal2," ", sueldoFinal3; FinProceso Diagrama de flujo: 26 27 Jéfferson Beltrán Morales 2.2 ESTRUCTURAS SELECTIVAS O CONDICIONALES. Con frecuencia nos enfrentamos a situaciones en las que se deben proporcionar instrucciones alternativas que pueden o no ejecutarse dependiendo de los datos de entrada, reflejando el cumplimiento o no de una determinada condición. La realización de acciones alternativas o decisiones se especifican utilizando condiciones que son verdaderas o falsas. Estas condiciones se llaman expresiones lógicas, o booleanas. Dado que las expresiones lógicas toman el valor verdadero o falso, se necesita una sentencia de control que dirija a la computadora a ejecutar una sentencia si la expresión es verdadera, y otra sentencia en caso de que sea falsa [2] [16]. 2.2.1 ESTRUCTURA SELECTIVA “SI-ENTONCES”. Algoritmos en PSeInt La siguiente figura muestra el diagrama de flujo de la estructura “Si-Entonces” [14]: EL pseudocódigo es [14]: Si <condición> Entonces <instrucciones> Sino <instrucciones> FinSi La estructura “Si” funciona de la siguiente manera [14]: 1. Se evalúa la expresión lógica. 2. Si la expresión toma el valor verdadero, se ejecutará la sentencia A y el control pasará a la sentencia inmediatamente siguiente. 3. Si la expresión toma el valor falso, entonces sólo se ejecutará la sentencia B y el control pasa de nuevo inmediatamente a la siguiente sentencia del programa. La cláusula sino es optativa, en ese caso, si la condición es falsa no se ejecuta ninguna instrucción y la ejecución del programa continúa con la instrucción siguiente. . Ejemplo 3. Construir un algoritmo que lea la calificación de un alumno en un examen, escriba "Aprobado" en caso que esa calificación fuese mayor o igual que 7. Solución. Salidas: Mensaje de aprobado si se cumple la condición. Entradas: Calificación. Datos adicionales: Un alumno aprueba si la calificación es mayor o igual que 7. Variables: Cal = calificación 28 Jéfferson Beltrán Morales 29 Pseudocódigo: Proceso Algoritmo3 //lee la calificación de un alumno y escribe "Aprobado" si es mayor o //igual que 7 Definir cal Como Real; //declaro la variable Escribir "Ingrese la calificación del alumno: "; Leer cal; Si cal>=7 Entonces Escribir "Aprobado"; FinSi FinProceso. Diagrama de flujo: Algoritmos en PSeInt Ejemplo 4. Dada la calificación de un alumno en un examen, escriba "Aprobado" si su calificación es mayor o igual que 7 y "Reprobado" en caso contrario. Pseudocódigo: Proceso Algoritmo4 //escribe "Aprobado" si es mayor o igual que 7 o "Reprobado" caso //contrario Definir cal Como Real; //declaro la variable Escribir "Ingrese la calificación del alumno: "; Leer cal; Si cal>=7 Entonces Escribir "Aprobado"; Sino 30 Jéfferson Beltrán Morales 31 Escribir "Reprobado"; FinSi FinProceso Diagrama de flujo: Ejemplo 5. Dado como dato el sueldo de un trabajador, aplicar un aumento del 15% si su sueldo es inferior a $1000 y 12% en caso contrario, luego imprimir el nuevo sueldo del trabajador. Pseudocódigo: Proceso Algoritmo5 //aumento el 15% si su sueldo es inferior a $1000 y 12% en caso //contrario Definir sueldo, nuevoSueldo Como Real; //declaro la variable Algoritmos en PSeInt Escribir "Ingrese el sueldo del trabajador: "; Leer sueldo; Si sueldo<1000 Entonces NuevoSueldo <- sueldo*1.15; Sino NuevoSueldo <- sueldo*1.12; FinSi Escribir "El nuevo sueldo es: ", nuevoSueldo; FinProceso Diagrama de flujo: 2.2.1.1 EXPRESIONES LÓGICAS. 32 Jéfferson Beltrán Morales 33 Sirven para plantear alternativas o decisiones y dan como resultado un valor booleano verdadero o falso, es decir, se cumple o no se cumple la condición. Se pueden clasificar en simples y complejas. Las simples son las que usan operadores relacionales y las complejas las que usan operadores lógicos [2]. Ejemplo 6: Operador lógico Y (“&”): Una escuela aplica dos exámenes a sus aspirantes, por lo que cada uno de ellos obtiene dos calificaciones denotadas como calif1 y calif2. El aspirante que obtenga calificaciones mayores o iguales que 7 en ambos exámenes es aceptado; en caso contrario es rechazado. Pseudocódigo: Proceso Algoritmo6 //escribe aceptado si las 2 calificaciones son >= que 7, caso contrario es //rechazado Definir calif1, calif2 Como Real; Escribir "Ingrese la nota 1: "; Leer calif1; Escribir "Ingrese la nota 2: "; Leer calif2; Si ((calif1>=7) & (calif2>=7)) Entonces Escribir "Estudiante aceptado"; Sino Escribir "Estudiante rechazado"; FinSi FinProceso Diagrama de flujo: Algoritmos en PSeInt En este ejemplo, nótese que también se usa operadores relacionales. Por lo general cuando hay operadores lógicos, éstos van acompañados de operadores relacionales. Ejemplo 7: Operador lógico O (“|”): Una escuela aplica dos exámenes a sus aspirantes, por lo que cada uno de ellos obtiene dos calificaciones denotadas como calif1 y calif2. El aspirante que obtenga una calificación mayor o igual que 9 en cualquiera de los exámenes es aceptado; en caso contrario es rechazado. Pseudocódigo: Proceso Algoritmo7 //escribe aceptado si una de las 2 calificaciones es >= que 9, caso //contrario es rechazado Definir calif1, calif2 Como Real; Escribir "Ingrese la nota 1: "; Leer calif1; Escribir "Ingrese la nota 2: "; Leer calif2; Si ((calif1>=9) | (calif2>=9)) Entonces 34 Jéfferson Beltrán Morales 35 Escribir "Estudiante aceptado"; Sino Escribir "Estudiante rechazado"; FinSi FinProceso Diagrama de flujo: La instrucción del ejemplo 7 equivale a OR ya que nos dice que puede ser en cualquiera de los exámenes no necesariamente en los dos. En ejemplo 6 la palabra ambos equivale a seleccionar la instrucción AND. Si la instrucción nos dijera que obtenga una nota en cualquiera de los exámenes pero no en ambos, nos estaría indicando una instrucción XOR que es un tipo de OR pero exclusivo. Es decir, no puede considerarse el caso en que tenga la misma nota en los dos exámenes, solo en uno de los dos. Algoritmos en PSeInt 2.2.1.2 ESTRUCTURAS ANIDADAS. Recordemos que las estructuras no secuenciales se pueden anidar, es decir, pueden contener otras adentro, pero la estructura contenida debe comenzar y finalizar dentro de la contenedora. En la solución de problemas encontramos numerosos casos en los que luego de tomar una decisión y marcar el camino correspondiente a seguir, es necesario tomar otra decisión. Dicho proceso puede repetirse numerosas veces. En los problemas en donde un bloque condicional incluye otro bloque condicional se dice que un bloque está anidado dentro del otro [2] [16]. Ejemplo 8. Determinar la cantidad de dinero que recibirá un trabajador por concepto de las horas extras trabajadas en una empresa, sabiendo que cuando las horas de trabajo exceden de 40, el resto se consideran horas extras y que éstas se pagan al doble de una hora normal cuando no exceden de 8; si las horas extras exceden de 8 se pagan las primeras 8 al doble de lo que se paga por una hora normal y el resto al triple. Solución. Lo primero que hay que determinar es si el trabajador trabajó horas extras o no. Encontrar las horas extras de la siguiente forma: Horas extras = horas trabajadas - 40 En caso que sí trabajó horas extras: Si horas extras>8 entonces a horas extras excedentes de 8=horas extras-8 y pago por horas extras = pago por hora normal * 2 * 8 + pago por hora normal * 3 * horas extras excedentes de 8 De otra forma (sólo horas al doble) pago por horas extras = pago por hora normal * 2 * horas extras. Finalmente, pago total que recibirá el trabajador será: Pago = pago * hora normal * 40 + pago por horas extras. Si no trabajó horas extras tendremos: Pago = pago por hora normal * horas trabajadas. Datos de salida: Pago. Datos de entrada: número de horas trabajadas y pago por hora normal. Definición de variables: ht = horas trabajadas het = horas extras que exceden de 8 ph = pago por hora normal phe = pago por horas extras he = horas extras pt = pago que recibe el trabajador 36 Jéfferson Beltrán Morales 37 Pseudocódigo: Proceso Algoritmo8 Definir he, het, ht Como Entero; //variable de horas trabajadas Definir ph, phe, pt Como Real; //variables para pago Escribir "Ingrese las horas trabajadas: "; Leer ht; Escribir "Ingrese el valor por hora normal: "; Leer ph; Si ht > 40 entonces //determinamos las horas extras he <- ht - 40; Si he > 8 entonces //determinamos las horas a pagar al triple het <- he - 8; //las 8 se pagan al doble y el resto al triple phe <- ph * 2 * 8 + ph * 3 * het; Sino //se pagan al doble phe <- ph * 2 * he; FinSi //pago horas normales más horas extras pt <- ph * 40 + phe; Sino //no hay horas extras Algoritmos en PSeInt pt <- ph * ht; FinSi Escribir "El valor a pagar es: ", pt; FinProceso Diagrama de flujo: 38 39 Jéfferson Beltrán Morales Ejemplo 9. Dados los datos A, B y C que representan números enteros diferentes, construir un algoritmo para escribir estos números en forma descendente. Este es un ejemplo de los algoritmos conocidos como de Lógica Pura, ya que poseen muchas decisiones y muchas bifurcaciones. Algoritmos en PSeInt Observación: Para ordenar números no es un algoritmo eficiente, pero nos sirve para ejemplarizar estructuras anidadas. Solución: Salida: A, B y C ordenados descendentemente. Entradas: A, B y C. La dinámica del problema es comparar dos números a la vez para conocer cuál es el mayor. Pseudocódigo: Proceso Algoritmo9 //ordenar 3 números de mayor a menor Definir a,b,c Como Entero; Escribir "Ingrese 3 números: "; Leer a,b,c; Escribir "Los números ordenados descendentemente son: "; Si a>b Entonces Si a>c Entonces Si b>c Entonces Escribir a," ",b," ",c; Sino Escribir a," ",c," ",b; FinSi Sino Escribir c," ",a," ",b; FinSi Sino 40 Jéfferson Beltrán Morales 41 Si b>c Entonces Si a>c Entonces Escribir b," ",a," ",c; Sino Escribir b," ",c," ",a; FinSi Sino Escribir c," ",b," ",a; FinSi FinSi FinProceso Diagrama de flujo: 2.2.2 ESTRUCTURA DE SELECCIÓN MÚLTIPLE “SEGÚN”. Con frecuencia es necesario que existan más de dos elecciones posibles. Este problema se podría resolver por estructuras selectivas anidadas o en cascada, Algoritmos en PSeInt pero si el número de alternativas es grande puede plantear serios problemas de escritura y de legibilidad. Usando la estructura de decisión múltiple se evaluará una expresión que podrá tomar n valores distintos, 1, 2, 3,…,n y según que elija uno de estos valores en la condición, se realizará una de las n acciones o lo que es igual, el flujo del algoritmo seguirá sólo un determinado camino entre los n posibles [2] [16]. Esta estructura se representa por un selector el cual si toma el valor 1 ejecutará la acción A, si toma el valor 2 ejecutará la acción B, si toma el valor N realizará la acción N [14]. La estructura de selección múltiple se implementa con la sentencia Según, cuyo diagrama de flujo y pseudocódigo son [14]: Diagrama de flujo [14]: Pseudocódigo [14]: Segun <variable> Hacer <número1>: <instrucciones> <número2>,<número3>: <instrucciones> <...> De Otro Modo: <instrucciones> FinSegun Reglas [14]: o Esta instrucción permite ejecutar opcionalmente varias acciones posibles (distintas entre si), dependiendo del valor almacenado en una variable de tipo numérico. o Al ejecutarse, se evalúa el contenido de la variable y se ejecuta la secuencia de instrucciones asociada con dicho valor. 42 Jéfferson Beltrán Morales 43 o Cada opción está formada por uno o más números separados por comas, dos puntos y una secuencia de instrucciones. o Si una opción incluye varios números, la secuencia de instrucciones asociada se debe ejecutar cuando el valor de la variable es uno de esos números. o Opcionalmente, se puede agregar una opción final, denominada De Otro Modo, cuya secuencia de instrucciones asociada se ejecutará sólo si el valor almacenado en la variable no coincide con ninguna de las opciones anteriores. Ejemplo 10: Escribir un algoritmo tal que si opc=1 realice la suma, si opc=2, el producto, si opc=3, la división y 0 en cualquier otro caso, despliegue el resultado: Pseudocódigo: //Ejemplo de estructura Segun Proceso Algoritmo10 Definir num1, num2, opc Como Entero; Definir resp Como Real; resp <-0; Escribir "Ingrese una opción: 1 (Suma), 2 (Producto), 3 (División) :"; Leer opc; Escribir "Ingrese los dos números a operar: "; Leer num1, num2; Segun opc Hacer 1: resp <- num1+num2; 2: resp <- num1*num2; 3: Si num2=0 Entonces Algoritmos en PSeInt Escribir "El denominador no puede ser 0"; Sino resp <- num1/num2; FinSi De Otro Modo: resp <- 0; FinSegun Escribir "El resultado es : ",resp; FinProceso Diagrama de Flujo: Ejemplo 11. Dados como datos la categoría y el sueldo de un trabajador, calcule el aumento correspondiente teniendo en cuenta la siguiente tabla. Imprimir la categoría del trabajador y el nuevo sueldo. 44 Jéfferson Beltrán Morales 45 Incrementos Categoría Aumento 1 15% 2 10% 3 8% 4 7% Pseudocódigo: Proceso Algoritmo11 //ejemplo de estructura Segun Definir cat Como Entero; Definir sueldo, inc, nuevoSueldo Como Real; Escribir "Ingrese la categoría: "; Leer cat; Escribir "Ingrese el sueldo: "; Leer sueldo; Segun cat Hacer 1: inc <- sueldo*0.15; 2: inc <- sueldo*0.1; 3: inc <- sueldo*0.08; 4: inc <- sueldo*0.07; Algoritmos en PSeInt De Otro Modo: inc<- 0; FinSegun nuevoSueldo <- sueldo + inc; Escribir "Categoría: ",cat,", nuevo sueldo: ",nuevoSueldo; FinProceso Diagrama de Flujo: 2.3 ESTRUCTURAS REPETITIVAS O ITERATIVAS. Las computadoras están especialmente preparadas para ejecutar tareas repetidamente. Los cálculos simples o la manipulación de pequeños conjuntos de datos se pueden realizar fácilmente a mano, pero las tareas grandes o repetitivas son realizadas con mayor eficiencia por una computadora. Las estructuras de control repetitivas son aquellas en las que una sentencia o grupos de sentencias se repiten muchas veces. Este conjunto de sentencias se denomina bucle, ciclo, lazo, o loop. Las acciones que se repiten en un bucle 46 Jéfferson Beltrán Morales 47 constituyen el cuerpo del bucle, y cada repetición del cuerpo del bucle se denomina iteración [2] [16]. Las estructuras repetitivas básicas son [2] [16]: Estructura Para. Estructura Mientras. Estructura Repetir. 2.3.1 ESTRUCTURA REPETITIVA “PARA”. En numerosas ocasiones se puede necesitar un bucle que se ejecute un número determinado de veces, y cuyo número se conozca por anticipado. Para aplicaciones de este tipo se utiliza la sentencia Para. Esta sentencia requiere que conozcamos por anticipado el número de veces que se ejecutarán las sentencias del interior del bucle [2] [16]. En la siguiente figura se muestra el diagrama de flujo de la estructura Para [14]: El pseudocódigo es [14]: Para <variable> <- <inicial> Hasta <final> (Con Paso <paso>) Hacer <instrucciones> FinPara Al ejecutarse la sentencia Para la primera vez, el valor <inicial> se asigna a la variable <variable>, denominada variable de control, y a continuación se ejecuta las sentencias de instrucciones que forman el cuerpo del bucle. Al llegar al final del bucle (FinPara), se incrementa la variable <variable> en <paso> unidades y se verifica si el valor almacenado en <variable> es mayor que el valor <final>; en caso negativo se vuelve a ejecutar todas las sentencias del interior del bucle hasta que la variable de control <variable> sea mayor que el valor final <final>. Algunos lenguajes permiten definir lo que se llama paso, que es la cantidad de unidades en las que se incrementará o decrementará la variable de control en Algoritmos en PSeInt cada iteración. Si se omite la cláusula Con Paso <paso>, la variable <variable> se incrementará en uno [14]. Es común usar en las estructuras repetitivas los contadores y acumuladores que los describimos a continuación: 2.3.1.1 CONTADOR. Un contador es una variable cuyo valor se incrementa o decrementa en una cantidad constante cada vez que se produce un determinado suceso o acción. Los contadores se utilizan con la finalidad de contar sucesos o acciones internas de un bucle; deben realizar una operación de inicialización y posteriormente las sucesivas de incremento o decremento del mismo. La inicialización consiste en asignarle al contador un valor. Se situará antes y fuera del bucle [15]. Representación: <nombre del contador> <- <nombre del contador> + <valor constante>; Si en vez de incremento es decremento se coloca un menos en lugar del más. Ejemplo: i <- i + 1; 2.3.1.2 ACUMULADOR O TOTALIZADOR. Es una variable que suma sobre sí misma un conjunto de valores para de esta manera tener la suma de todos ellos en una sola variable. La diferencia entre un contador y un acumulador es que mientras el primero va aumentando un valor constante, el acumulador va aumentando en una cantidad variable [15]. Representación: <Nombre del acumulador> <- <nombre del acumulador> + <valor variable>; Ejemplo 12. Calcular la suma de los cuadrados de los primeros 100 enteros y escribir el resultado. Se desea resolver el problema usando estructura Para. Pseudocódigo: Proceso Algoritmo12 //Ejemplo estructura Para 48 Jéfferson Beltrán Morales 49 Definir i,suma Como Entero; //inicializo el acumulador suma <- 0; Para i<-1 Hasta 100 Con Paso 1 Hacer suma <- suma + i*i; FinPara Escribir "La suma de los 100 primeros enteros es: ",suma; FinProceso Diagrama de flujo: Ejemplo 13. Calcular la promedio de N números enteros y escribir el resultado. Pseudocódigo: // Calcula el promedio de una lista de N datos Algoritmos en PSeInt Proceso Algoritmo13 Definir i, numDatos Como Entero; Definir acum, promedio, dato Como Real; acum <- 0; Escribir "Ingrese la cantidad de datos:"; Leer numDatos; Para i<-1 Hasta numDatos Hacer Escribir "Ingrese el dato ",i,": "; Leer dato; acum <- acum + dato; FinPara promedio <- acum / numDatos; Escribir "El promedio es: ",promedio; FinProceso Diagrama de flujo: 50 51 Jéfferson Beltrán Morales 2.3.2 ESTRUCTURA ITERATIVA “MIENTRAS”. La estructura iterativa Mientras es aquella en la que el número de iteraciones no se conoce por anticipado y el cuerpo del bucle se repite mientras se cumple una determinada condición. Por esta razón, se lo conoce como bucle condicional [2] [16]. El diagrama de flujo y pseudocódigo de la estructura iterativa mientras son: Algoritmos en PSeInt Diagrama de flujo [14]: Pseudocódigo [14]: Mientras <condición> Hacer <instrucciones> FinMientras Cuando la sentencia mientras se ejecuta, lo primero que sucede es la evaluación de la expresión lógica <condición>. Si se evalúa como falsa, ninguna acción se realiza y el programa sigue en la siguiente sentencia después del bucle. Si la expresión lógica se evalúa como verdadera, entonces se ejecuta el cuerpo del bucle y se evalúa de nuevo la expresión lógica <condición>, es decir, se puede ejecutar el bucle, cero o más veces. Este proceso se repite mientras la expresión lógica sea verdadera. Después de cada iteración la expresión lógica se evalúa y se verifica de nuevo; si cambia de verdadera a falsa la sentencia mientras finaliza. Mientras que la condición sea verdadera el bucle se ejecutará. Esto significa que el bucle se ejecutará indefinidamente a menos que algo en el interior del mismo modifique la condición haciendo que su valor pase a falso. Si la expresión nunca cambia de valor, entonces el bucle no termina nunca y se denomina bucle o loop infinito (en general, esta situación no es deseable), a fin de evitarlo, las instrucciones del cuerpo del bucle deben contener alguna instrucción que modifique la o las variables involucradas en la expresión lógica <condición>, de modo que sea falsa en algún momento y así finalice la ejecución del ciclo [14]. Ejemplo 14. Calcular la suma de los cuadrados de los primeros 100 números enteros y escribir el resultado. Solución. Como recordarás, resolvimos este ejercicio en la lección anterior pero utilizando la estructura Para. Ahora lo haremos con la estructura Mientras. 52 Jéfferson Beltrán Morales 53 En este caso, se necesita un contador (un índice), para llevar la cuenta de las veces que entramos al cuerpo del bucle. También es importante notar que esta variable se debe inicializar antes de entrar al cuerpo del ciclo y dentro del cuerpo se incrementará en uno cada vez que ingrese a él. Esta variable además nos sirve para compararla con el valor dado en la condición, cuando no se cumple la condición, se sale del ciclo. Pseudocódigo: //Ejemplo estructura Mientras Proceso Algoritmo14 Definir i,suma Como Entero; //inicializo el acumulador y el contador suma <- 0; i <- 1; Mientras i<=100 Hacer //sumo al acumulador suma <- suma + i*i; //incremento el contador i <- i+1; FinMientras Escribir "La suma de los 100 primeros enteros es: ",suma; FinProceso Diagrama de flujo: Algoritmos en PSeInt En las estructuras cíclicas condicionales es común controlar los ciclos con centinelas y banderas. 2.3.2.1 CENTINELAS. En un bucle mientras controlado por tarea, la condición mientras especifica que el cuerpo del bucle debe continuar ejecutándose mientras la tarea no haya sido completada, esto puede ser controlado por centinelas [15]. Centinelas son variables que toman valores adecuados para suspender el ingreso a un bucle. Por ejemplo, si se tienen las calificaciones de un test (comprendida entre 0 y 100); un valor centinela en esta lista puede ser -1, ya que nunca será una 54 Jéfferson Beltrán Morales 55 calificación válida y cuando aparezca este valor se terminará de ejecutar el bucle. Si la lista de datos son números positivos, un valor centinela puede ser un número negativo [15]. Ejemplo 15. Suponga que debemos obtener la suma de los gastos que hicimos en nuestro último viaje, pero no sabemos exactamente cuántos fueron. Solución: Si definimos gasto1, gasto2, gasto3, ...., -1 donde gastoi es el gasto número i y sumaGasto es el acumulador de gastos efectuados. -1 es el centinela de fin de datos. Pseudocódigo: //Ejemplo de centinelas Proceso Algoritmo15 Definir gasto, sumaGasto Como Real; sumaGasto <- 0; Escribir "Ingrese -1 para salir"; Escribir "Ingrese el gasto realizado: "; //leemos gasto fuera del bucle mientras Leer gasto; Mientras gasto <> -1 Hacer sumaGasto <- sumaGasto + gasto; Escribir "Ingrese el gasto realizado: "; Leer gasto; FinMientras Escribir "El gasto total es: ",sumaGasto; FinProceso Diagrama de flujo: Algoritmos en PSeInt 2.3.2.2 BANDERAS. Conocidas también como interruptores, switch, flags o conmutadores, son variables que pueden tomar solamente dos valores durante la ejecución del 56 Jéfferson Beltrán Morales 57 programa, los cuales pueden ser 0 ó 1, o bien los valores booleanos True o False. Se les suele llamar interruptores porque cuando toman los valores 0 ó 1 están simulando un interruptor abierto/cerrado o encendido/apagado [15]. Ejemplo 16: Leer un número entero N y calcular el resultado de la siguiente serie: 1 - 1/2+ 1/3 - 1/4+.... +/- 1/N. Pseudocódigo: //Ejemplo de banderas Proceso Algoritmo16 Definir bandera Como Logico; Definir serie Como Real; Definir i, num Como Entero; serie <- 0; i <- 1; Escribir "Ingrese el valor de N: "; Leer num; //inicializamos la bandera bandera <- Verdadero; Mientras i <= num Hacer Si bandera = Verdadero Entonces serie <- serie + 1/i; bandera <- Falso; Sino serie <- serie - 1/i; bandera <- Verdadero; FinSi Algoritmos en PSeInt i <- i+1; FinMientras Escribir "La suma de la serie es: ",serie; FinProceso Diagrama de flujo: 58 59 Jéfferson Beltrán Morales 2.3.3 ESTRUCTURA ITERATIVA “REPETIR”. Algoritmos en PSeInt La estructura iterativa Repetir es una bucle condicional como lo es también la estructura Mientras. Se usa cuando el número de iteraciones no se conoce por anticipado (lo cual si se conoce en la estructura Para) [2] [16]. La diferencia entre ambas es que la condición se sitúa al inicio (Mientras) o al final (Repetir) de la secuencia de instrucciones. Entonces, en el primero, el bucle continúa mientras la condición es verdadera (la cual se comprueba antes de ejecutar la acción, es decir se ejecuta cero o más veces) y en el segundo, el bucle continúa hasta que la condición se hace verdadera (la condición se comprueba después de ejecutar la acción, es decir, se ejecutará al menos una vez) [2] [16]. El diagrama de flujo de la estructura Repetir es el siguiente [14]: El pseudocódigo de la estructura Repetir es [14]: Repetir <instrucciones> Hasta Que <condición> Al ejecutarse esta instrucción, la secuencia de instrucciones que forma el cuerpo del ciclo se ejecuta una vez y luego se evalúa la condición <condición>. Si la condición es falsa, el cuerpo del ciclo se ejecuta nuevamente y se vuelve a evaluar la condición. Esto se repite hasta que la condición sea verdadera [14]. Note que, dado que la condición se evalúa al final, las instrucciones del cuerpo del ciclo serán ejecutadas al menos una vez [14]. Además, a fin de evitar ciclos infinitos, el cuerpo del ciclo debe contener alguna instrucción que modifique la o las variables involucradas en la condición de modo que en algún momento la condición sea verdadera y se finalice la ejecución del ciclo [14]. Un ciclo Repetir controlado por centinela, por ejemplo, es cuando el usuario digita una letra para salir como ‘S’ o ‘N’ para indicar si desea continuar o no. El bucle debe repetirse hasta que la respuesta del usuario sea ‘n’ o ‘N’. (Observe que los 60 Jéfferson Beltrán Morales 61 centinelas solamente pueden usarse con las estructuras Mientras y Repetir, no con estructuras Para). Ejemplo 17: Calcular la suma de los cuadrados de los primeros 100 números enteros y escribir el resultado. Solución. Nuevamente resolveremos el ejercicio de las dos lecciones anteriores, ahora utilizando la estructura Repetir. Como sabemos, en los bucles condicionales podemos usar una variable contador que debe inicializarse antes del ciclo e incrementarse dentro del bucle para controlar el número de veces que se ingresará al mismo. A diferencia de la estructura Mientras, la condición ahora estará colocada al final del bucle para que primero se ejecute la instrucción y luego se verifique si la condición se cumple. Esto quiere decir, que en esta estructura el bucle se realizará por lo menos una vez. También podrás observar que la condición está al revés, porque el bucle se repite hasta que la condición se cumpla. En el bucle Mientras, la condición se evaluaba mientras era verdadera. Pseudocódigo: //Ejemplo estructura Repetir Proceso Algoritmo17 Definir i,suma Como Entero; //inicializo el acumulador y el contador suma <- 0; i <- 1; Repetir //sumo al acumulador suma <- suma + i*i; //incremento el contador i <- i+1; Hasta Que i>100 Algoritmos en PSeInt Escribir "La suma de los 100 primeros enteros es: ",suma; FinProceso Diagrama de flujo: Ejemplo 18. Se desea calcular la suma de N números ingresados por el usuario, hasta que él decida no ingresar más números. Pseudocódigo: //Ejemplo estructura Repetir con centinelas Proceso Algoritmo18 Definir suma, num Como Real;; 62 Jéfferson Beltrán Morales 63 Definir resp Como Caracter; //inicializo acumulador suma <- 0; //inicializo la centinela Repetir Escribir "Ingrese un número: "; Leer num; //sumo al acumulador suma <- suma + num; //pregunto se desea continuar Escribir "Desea ingresar otro número (S/N): "; // leo la respuesta Leer resp; Hasta Que (resp='N' | resp='n') Escribir "La suma es: ",suma; FinProceso Diagrama de flujo: Algoritmos en PSeInt 2.4 ESTRUCTURA DE DATOS Y ARREGLOS. Todas las variables que se han considerado hasta ahora son de tipo simple. Una variable de tipo simple consiste de una sola localidad o “caja” de memoria y sólo puede contener un valor a la vez. Una variable de tipo estructurado consiste en toda una colección de casillas de memoria. Los tipos de datos estudiados: entero, real, carácter, lógico son considerados como datos de tipo simple, puesto que una variable que se define con alguno de estos tipos sólo puede almacenar 64 Jéfferson Beltrán Morales 65 un valor a la vez, es decir, existe una relación de uno a uno entre la variable y el número de elementos (valores) que es capaz de almacenar. En cambio un dato de tipo estructurado, puede almacenar más de un elemento (valor) a la vez, con la condición de que todos los elementos deben ser del mismo tipo, es decir, que se puede tener un conjunto de datos enteros, reales, etc. [2] [16]. 2.4.1 ESTRUCTURA DE DATOS. Estructura de Datos es una colección de datos que se caracterizan por su organización y las operaciones que se definen en ella. Las estructuras de datos tienen en común que un identificador o nombre, puede representar a múltiples datos individuales [2] [16]. Los datos de tipo estándar pueden ser organizados en diferentes estructuras de datos: estáticas y dinámicas. Estructura de Datos Estáticas. Son aquellas en las que se asigna una cantidad fija de memoria cuando se declara la variable y no puede ser modificada durante la ejecución del programa. Corresponden a este tipo los arreglos y registros [2] [16]. Estructuras de Datos Dinámicas. Son aquellas en las que el espacio ocupado en memoria puede ser modificado en tiempo de ejecución. Corresponden a este tipo las listas, árboles y grafos. Estas estructuras no son soportadas en todos los lenguajes. La elección de la estructura de datos idónea dependerá de la naturaleza del problema a resolver y, en menor medida, del lenguaje [2] [16]. 2.4.2 ARREGLOS. Un arreglo es una estructura de datos en la que se almacena una colección de datos del mismo tipo (por ejemplo, los sueldos de los empleados de una empresa) [2] [16]. Dicho de otra forma, un arreglo y es una lista de un número finito n de elementos del mismo tipo que se caracteriza por: Almacenar sus elementos en posiciones de memoria contiguas Tener un único nombre de variable (por ejemplo, salarios) que representa a todos los elementos Permitir acceso directo o aleatorio a sus elementos individuales, para ello se utiliza un índice que especifique la posición relativa en el arreglo. En resumen, un arreglo es una colección finita, homogénea y ordenada de elementos [2] [16]. Algoritmos en PSeInt Finita. Todo arreglo tiene un límite, es decir, debe determinarse cuál será el número máximo de elementos que podrán formar parte del arreglo. Homogénea. Todos los elementos del arreglo deben ser del mismo tipo. Ordenada: Se puede determinar cuál es el primer elemento, el segundo, el tercero,...., y el n-ésimo elemento. Los arreglos se clasifican de acuerdo con el número de dimensiones que tienen. Así se tienen los arreglos [2] [16]: Unidimensionales (vectores). Bidimensionales (tablas o matrices). Multidimensionales (tres o más dimensiones). 66 Jéfferson Beltrán Morales 67 Ejemplo. Suponga que se desea desarrollar un programa para: 1. 2. 3. 4. Leer una lista de calificaciones de un examen. Encontrar su media. Escribir una lista de las calificaciones mayores que la media. Ordenar la lista de las calificaciones en orden ascendente. Supongamos también que hay 100 calificaciones. Con lo estudiado hasta el momento, deberíamos utilizar 100 variables diferentes nota1, nota2, ...., nota100, de ese modo son 100 direcciones diferentes de memoria para almacenar las calificaciones del examen. Se imagina declarar las 100 variables, ¿cuántas instrucciones involucra? Definir nota1, nota2, nota3,.........nota100 Como Entero; (En la declaración real de un algoritmo en pseudocódigo no pueden usarse puntos suspensivos, por lo tanto serán 100 veces). En la fase de lectura de datos, serán también 100 veces las instrucciones para ir leyendo cada valor. Leer nota1, nota2, nota3,........., nota100; Para calcular la media: media (nota1+nota2+.......+nota100)/100; Para la lista de calificaciones mayores que la media, deberá también compararse una por una: Si nota1 > media Entonces Escribir nota1; FinSi Si nota2 > media Entonces Escribir nota2; FinSi … Si nota100 > media Entonces Escribir nota100; FinSi Y después de muchísimas líneas de código..... ¡Todavía falta ordenar la lista de calificaciones en orden ascendente! Algoritmos en PSeInt Los arreglos nos permiten ahorrar instrucciones, porque es fácil recorrer toda la lista de notas con unas pocas instrucciones. En el caso anterior, cuando el acceso a la información es secuencial, sólo se puede acceder a un elemento buscando desde el principio de la lista, y esto es algo lento. Lo que se necesita es una estructura de acceso directo que permita almacenar y recuperar los datos directamente especificando su posición en la estructura, de esa manera se requerirá el mismo tiempo para acceder al elemento de la posición 100 que el de la posición 5. Queremos también que esta estructura se almacene en memoria principal para que su almacenaje y recuperación sea más rápida. Es por ello que existen los arreglos, que están organizados en una secuencia de elementos, todos del mismo tipo y se puede acceder a cada elemento directamente especificando su posición en esta secuencia. Para poder utilizar un arreglo, debemos indicar su tipo, un nombre único y su dimensionamiento, es decir, declarar los rangos de sus índices, lo cual determina cuantos elementos se almacenarán y como se accederá a los mismos. Al declarar un arreglo, se debe inicializar sus elementos antes de utilizarlos Definición y Dimensionamiento. La instrucción Dimension permite declarar un arreglo, indicando sus dimensiones [14]. Dimesion <identificador> (<max1>,...,<maxN>); Esta instrucción define un arreglo con el nombre indicado en <identificador> y N dimensiones. Los N parámetros indican la cantidad de dimensiones y el valor máximo de cada una de ellas. La cantidad de dimensiones puede ser una o más, y la máxima cantidad de elementos debe ser una expresión numérica positiva [14]. Se pueden definir más de un arreglo en una misma instrucción, separándolos con una coma (,) [14]. Dimension <ident1> (<max11>,..,<max1N>),..,<identM>(<maxM1>,..,<maxMN>); Es importante notar que es necesario definir un arreglo antes de utilizarlo. Ejemplo: Definir vector, matriz Como Entero; Dimension vector[100], matriz[10,20]; 68 69 2.4.2.1 Jéfferson Beltrán Morales ARREGLO UNIDIMENSIONAL. Un arreglo de una dimensión (también conocido como vector) es un tipo de datos estructurado compuesto de un número de elementos finitos, tamaño fijo y elementos homogéneos que se almacenan bajo un mismo nombre, y se diferencian por la posición que tiene cada elemento dentro del arreglo de datos. Finitos indica que hay un último elemento, tamaño fijo significa que el tamaño del arreglo debe ser conocido en tiempo de compilación y homogéneo significa que todos sus elementos son del mismo tipo [2] [16]. Los elementos del arreglo se almacenan en posiciones contiguas de memoria, a cada una de las cuales se puede acceder directamente mediante un número entero denominado índice del arreglo, que identifica la posición del elemento dentro del conjunto [2] [16]. Generalmente los índices de los elementos de los arreglos inician en 0, es decir, si declaramos un arreglo de 100 elementos, los índices de elementos válidos van de 0 a 99 (aunque existen lenguajes que inician los índices de los arreglos en 1). vector[0] vector[1] vector[2] ……….. vector[97] vector[98] vector[99] 2.4.2.1.1 PARTES DE UN ARREGLO. Los componentes. Hacen referencia a los elementos que forman el arreglo, es decir, a los valores que se almacenan en cada una de las casillas del mismo [2] [16]. Los índices. Permiten hacer referencia a los componentes del arreglo en forma individual, especifican cuántos elementos tendrá el arreglo y además, de qué modo podrán accederse esos componentes [2] [16]. Algoritmos en PSeInt valoro valor1 valor2 ………... valor97 valor98 valor99 70 Componentes Definición y Dimensionamiento. Ejemplo. Definir un arreglo llamado vector de 100 elementos de tipo Real. Definir vector Como Real; Dimension vector[100]; 2.4.2.1.2 OPERACIONES CON ARREGLOS. Las operaciones que se pueden realizar con vectores durante el proceso de resolución de un problema son [2] [16]: o Lectura/ Escritura, o Asignación. o Actualización (inserción, eliminación, modificación). o Recorrido (acceso secuencial). o Ordenación. o Búsqueda. Lectura. El proceso de lectura de un arreglo consiste en leer y asignar un valor a cada uno de sus elementos. Normalmente se realizan con estructuras repetitivas. Usamos los índices para recorrer los elementos del arreglo [2] [16]. Ejemplo: Para i <- 0 Hasta 99 Con Paso 1 Hacer Leer vector[i]; FinPara Escritura. Es similar al caso de lectura, sólo que en lugar de leer el componente del arreglo, lo escribimos [2] [16]. Jéfferson Beltrán Morales 71 Ejemplo: Para i <- 0 Hasta 99 Con Paso 1 Hacer Escribir vector[i]; FinPara Asignación. No es posible asignar directamente un valor a todo el arreglo; sino que se debe asignar el valor deseado en cada componente. Podemos recorrer el arreglo con una estructura repetitiva y asignar un valor a todos los elementos del arreglo [2] [16]. Ejemplo: vector[1] <- 5; //asigna el valor 5 a la posición 1 del arreglo vector vector[10] <- vector[1] / 2; //asigna una operación al arreglo vector //Se puede asignar un valor constante a todos los elementos del vector Para i <- 0 Hasta 99 Con Paso 1 Hacer vector[i] <- 10; FinPara Inicialización. Siempre es importante inicializar las variables y por lo tanto también los arreglos. Por ejemplo, inicializar con 0 cada elemento del arreglo [2] [16]. Ejemplo: Para i <- 0 Hasta 99 Con Paso 1 Hacer vector[i] <- 0; FinPara Acceso Secuencial. (Recorrido). El acceso a los elementos de un vector puede ser para leer en él o para escribir (visualizar su contenido). Recorrido del vector es la acción de efectuar una acción general sobre todos los elementos de ese vector [2] [16]. Actualización. Incluye añadir (insertar), borrar o modificar algunos de los ya existentes. Se debe tener en cuenta si el arreglo está o no ordenado. Añadir Algoritmos en PSeInt datos a un vector consiste en agregar un nuevo elemento al final del vector, siempre que haya espacio en memoria (recordemos que el tamaño de un arreglo es fijo) [2] [16]. Observación. Las partes de un arreglo unidimensional y las operaciones son las mismas que en los arreglos multidimensionales (pueden extenderse), salvo que se trata de varias dimensiones y no sólo de una. Ejemplo19. Se desea ingresar una lista de n valores numéricos y calcular la suma, el promedio, los números mayor y menor de dicha lista Pseudocódigo: //Ejemplo de arreglos Proceso Algoritmo19 Definir lista, i, mayor, menor, suma, n Como Entero; Definir promedio como Real; //supongamos que n es 10 n<-10; //defino el arreglo y su tamaño Dimension lista[n]; //inicializo variables suma<-0; //inicializo el arreglo Para i<-0 Hasta n-1 Con Paso 1 Hacer lista[i]<-0; FinPara //leo los elementos del arreglo Para i<-0 Hasta n-1 Con Paso 1 Hacer Escribir "Ingrese el valor ",(i+1)," de la lista: "; 72 Jéfferson Beltrán Morales 73 Leer lista[i]; FinPara //inicializo los números mayor y menor con el primer //elemento del arreglo mayor<-lista[0]; menor<-mayor; //calculo la suma recorriendo el arreglo Para i<-0 Hasta n-1 Con Paso 1 Hacer suma<-suma+lista[i]; FinPara //calculo el promedio promedio<-suma/n; //calculo los números mayor y menor de la lista Para i<-1 Hasta n-1 Con Paso 1 Hacer Si lista[i]>mayor Entonces mayor<-lista[i]; FinSi Si lista[i]<menor Entonces menor<-lista[i]; FinSi FinPara //presento los resultados Escribir "Suma: ", suma, ", Promedio: ", promedio; Escribir "Número mayor: ", mayor, ", Número menor: ", menor; Algoritmos en PSeInt FinProceso Diagrama de flujo: 74 75 Jéfferson Beltrán Morales Algoritmos en PSeInt Ejemplo 20. Ingresar una lista de nombres (la lista termina cuando se ingresa un nombre en blanco), no se permite ingresar nombres repetidos y luego ordenar y presentar la lista. Pseudocódigo: //Ordenamiento de un arreglo Proceso Algoritmo20 Definir nombre,lista,aux Como Cadenas; Definir seRepite Como Logico; Definir cant,i,j,posMenor Como Enteros; Dimension lista[200]; Escribir "Ingrese un nombre (enter en blanco para terminar):"; // leer la lista cant <- 0; Leer nombre; Mientras nombre<>"" Hacer Escribir "Ingrese un nombre: "; lista[cant] <- nombre; cant <- cant+1; Repetir // leer un nombre y ver que no esté ya en la lista Leer nombre; seRepite <- Falso; //no es "" el nombre no se necesita buscar un repetido Si nombre<>"" Entonces Para i <- 0 Hasta cant-1 Hacer Si nombre=lista[i] Entonces 76 Jéfferson Beltrán Morales 77 seRepite <- Verdadero; //salgo del bucle pues es ya //repetido y no necesito seguir buscando i <- cant; Escribir "Nombre repetido"; Escribir "Ingrese un nombre: "; FinSi FinPara FinSi Hasta Que ~ seRepite FinMientras // ordenamos la lista Para i <- 0 Hasta cant-2 Hacer // busca el menor entre i y cant posMenor <- i; Para j <- i+1 Hasta cant-1 Hacer Si lista[j] < lista[posMenor] Entonces posMenor <- j; FinSi FinPara // intercambia el que estaba en i con el menor que encontró aux <- lista[i]; lista[i] <- lista[posMenor]; lista[posMenor] <- aux; FinPara Algoritmos en PSeInt // mostrar como queda la lista Escribir "La lista ordenada en forma ascendente es:"; Para i <- 0 Hasta cant-1 Hacer Escribir " ",lista[i]; FinPara FinProceso Diagrama de flujo: 78 79 Jéfferson Beltrán Morales Algoritmos en PSeInt 2.4.3 ARREGLO BIDIMENSIONAL. Hasta ahora hemos visto cómo se puede manipular información con una sola columna o lista de entrada con los llamados vectores, o arreglos de una dimensión. Sin embargo, en numerosas ocasiones es necesario trabajar con datos que tengan más de una dimensión (se representan por ejemplo como tablas de doble entradas, cubos, etc.) [2] [16]. Un arreglo bidimensional (matriz o tabla) es un arreglo con dos índices. Para localizar o almacenar un valor en el arreglo se deben especificar dos posiciones (dos subíndices), uno para la fila y otro para la columna. Internamente en memoria se reservan MxN posiciones consecutivas para almacenar todos los elementos del arreglo [2] [16]. Declaración de una matriz [14]. Ejemplo: Definir una arreglo bidimensional de 10 filas y 20 columnas de números enteros llamado matriz. Definir matriz Como Entero; Dimension matriz[10,20]; Una matriz se representa como una tabla, donde: 1≤I≤M y 1≤J≤N (observe que al igual que en los vectores, las filas y columnas de las matrices inician en 0). 1 2 J N 1 matriz[0,0] matriz[0,1] ….. matriz[0,j] matriz[0,n-1] 2 matriz[1,0] matriz[1,1] ….. matriz[1,j] matriz[1,n-1] ….. I matriz[i,0] matriz[i,1] ….. matriz[i,j] matriz[i,n-1] matriz[mmatriz[mmatriz[m-1,nM 1,0] matriz[m-1,1] ….. 1,j] 1] Ejemplo 21. Se desea realizar la suma de dos matrices, ingresar las dimensiones por teclado. Pseudocódigo: //Ejemplo de matrices Proceso Algoritmo21 Definir i, j, m, n, matriz1, matriz2, matriz3 Como Entero; 80 Jéfferson Beltrán Morales 81 Escribir "Ingrese el número de filas: "; Leer m; Escribir "Ingrese el número de columnas: "; Leer n; //defino la dimensión de las matrices Dimension matriz1[m,n], matriz2[m,n], matriz3[m,n]; Limpiar Pantalla; //ingreso la primera matriz Escribir "Ingrese la matriz A:"; Para i <- 0 Hasta m-1 Con Paso 1 Hacer Para j <- 0 Hasta n-1 Con Paso 1 Hacer Escribir "Ingrese el valor A[",i+1,",",j+1,"]"; Leer matriz1[i,j]; FinPara FinPara Limpiar Pantalla; //ingreso la segunda matriz Escribir "Ingrese la matriz B:"; Para i <- 0 Hasta m-1 Con Paso 1 Hacer Para j <- 0 Hasta n-1 Con Paso 1 Hacer Escribir "Ingrese el valor B[",i+1,",",j+1,"]"; Leer matriz2[i,j]; FinPara FinPara Algoritmos en PSeInt Escribir "Presione enter..."; Esperar Tecla; Limpiar Pantalla; //sumo las matrices y las presento en pantalla Escribir "La suma de A+B es:"; Para i <- 0 Hasta m-1 Con Paso 1 Hacer Para j <- 0 Hasta n-1 Con Paso 1 Hacer matriz3[i,j] <- matriz1[i,j] + matriz2[i,j]; Escribir "C[",i+1,",",j+1,"]=",matriz3[i,j]; FinPara FinPara FinProceso Diagrama de flujo: 82 83 Jéfferson Beltrán Morales Algoritmos en PSeInt 2.5 MODULARIDAD. Una estrategia para la resolución de problemas complejos con computadoras es la división o descomposición del problema en otros problemas más pequeños y fáciles de resolver. Estos sub problemas se implementan mediante módulos o subprogramas. Los subprogramas son una herramienta importante para el desarrollo de algoritmos y programas, de modo que normalmente un proyecto de programación se compone de un programa principal y un conjunto de subprogramas, con las llamadas a los mismos dentro del programa principal. Un subprograma realiza una tarea concreta que se describe con una serie de instrucciones y que, idealmente, debería ser independiente de otros subprogramas. El descomponer un programa en módulos independientes más simples se conoce también como el método de "Divide y vencerás" [2]. ¿Cuándo es útil la modularización? Este enfoque de segmentación o modularización es útil en dos casos [2]: 1. Cuando existe un grupo de instrucciones o una tarea específica que deba ejecutarse en más de una ocasión. 2. Cuando un problema es complejo o extenso, la solución se divide o segmenta en módulos que ejecutan partes o tareas específicas. Ventajas de la Programación Modular. Las ventajas más sobresalientes de utilizar subprogramas o módulos son [2]: o El uso de subprogramas facilita el diseño descendente y modular, que permite descomponer un problema complejo en sub problemas hasta que éstos sean concretos y fáciles de resolver. o Los procedimientos se pueden ejecutar más de una vez en un programa y en diferentes programas, ahorrando en consecuencia tiempo de programación. Un subprograma, en esencia, se puede ver como una caja negra (encapsulamiento) que ejecuta una tarea en particular en un programa, acepta entradas y produce ciertas salidas. Una vez que el módulo se ha escrito y comprobado, se puede utilizar en otros programas eliminando la duplicación innecesaria de código (reutilización de código). o Aumenta la facilidad de depuración y búsqueda de errores en un programa ya que éstos se pueden aislar fácilmente depurándose sus errores individualmente antes de su inclusión en bibliotecas independientes y de ser llamados en el programa principal. 84 85 Jéfferson Beltrán Morales o El uso de subprogramas facilita la división de tareas de programación entre un equipo de programadores. o El uso de módulos facilita la proyección y la comprensión de la lógica subyacente para el programador y el usuario. 2.5.1 PROCEDIMIENTOS Y FUNCIONES. Los subprogramas en programación estructurada se clasifican en procedimientos y funciones, que son unidades de programas diseñados para ejecutar una tarea específica [2]. Las funciones normalmente devuelven un sólo valor a la unidad de programa (programa principal u otro subprograma) que los referencia (que los llama o invoca). Los procedimientos pueden devolver cero, uno o varios valores [2]. Las funciones se dividen en estándares y definidas por el usuario [2]. Estándar. Son funciones proporcionadas por lenguaje de programación. Definidas por el usuario. Son funciones definidas el programador con el propósito de ejecutar alguna función específica, y que por lo general se usan cuando se trata de hacer algún cálculo que será requerido en varias ocasiones en la parte principal del algoritmo. Las funciones y procedimientos están compuestos por un grupo de sentencias a las que se asigna un nombre (identificador) y constituyen una unidad de programa a la que se puede invocar desde el programa principal u otra función o procedimiento [2]. Una de las características importantes y diferenciadoras de los subprogramas es la posibilidad de comunicación entre el programa principal y los subprogramas (o entre dos subprogramas). Esta comunicación se realiza a través de una lista de parámetros. Un parámetro es un medio para pasar información – valores a variables – del programa principal a un subprograma y viceversa. No es obligatorio que un subprograma utilice parámetros, ya que éstos no siempre son necesarios [2]. Un parámetro es, prácticamente, una variable cuyo valor debe ser o bien proporcionado por el programa principal al subprogramas o ser devuelto desde el subprogramas al programa principal. Por consiguiente, hay dos tipos de parámetros: parámetros de entrada y parámetros de salida. Los de entrada son aquellos cuyos valores deben ser proporcionados por el programa principal, mientras que los de salida son aquellos cuyos valores se calcularán en el Algoritmos en PSeInt subprograma y se deben devolver al programa principal para su proceso posterior [2]. Visibilidad de las variables. Las variables que intervienen en un programa con subprogramas pueden ser de dos tipos: variables locales y variables globales. Una variable local es una variable que está declarada dentro de un subprograma, y se dice que es local al subprograma. Una variable local sólo está disponible durante el funcionamiento del mismo, y su valor se pierde una vez que el subprograma termina. Dos variables locales pueden tener el mismo nombre siempre que estén declaradas en funciones o procedimientos diferentes [2]. Las variables declaradas en el programa principal se denominan variables globales. A diferencia de las variables locales, cuyos valores se pueden utilizar sólo dentro del subprograma en el que están declaradas, las variables globales pueden ser utilizadas en el programa principal y en todos los subprogramas, permanecen activas durante toda la ejecución del programa [2]. Hay que tener especial precaución al trabajar con variables globales, ya que al ser recursos compartidos todos los subprogramas pueden tener acceso simultáneo a ellas y se pueden producir errores lógicos debidos a la concurrencia. Por lo general, es una buena práctica evitar el uso de variables globales desde subprogramas a menos que sea estrictamente necesario. Las variables también pueden ser declaradas dentro de un bloque o estructura de control, y se comportarán como variables locales únicamente dentro de dicho bloque o estructura. Si dos variables, una global y una local, tienen el mismo nombre, la local prevalecerá sobre la global dentro del módulo en que ha sido declarada. 2.5.1.1 IMPLEMENTACIÓN DE PROCEDIMIENTOS Y FUNCIONES. En PSeInt el pseudocódigo para la implementación de procedimientos y funciones se realiza de la siguiente manera [14]: SubProceso variable_de_retorno argumento_2, ... ) <- nombre_de_la_funcion(argumento_1, 86 Jéfferson Beltrán Morales 87 acción 1; acción 1; . . . acción n; FinSubproceso Comienza con la palabra clave SubProceso (o Función, son equivalentes) seguida de la variable de retorno, el signo de asignación, el nombre del subproceso, y finalmente, la lista de argumentos entre paréntesis, en este caso el SubProceso se denomina una función. Si el SubProceso no retorna ningún valor, se denomina procedimiento, entonces pueden omitirse el identificador variable_de_retorno y el signo de asignación, es decir, colocar directamente el nombre y los argumentos a continuación de la palabra clave SubProceso. Si el subproceso no recibe ningún valor pueden colocarse los paréntesis vacíos u omitirse, finalizando la primer línea con el nombre del subproceso. Las reglas para los nombres de subprocesos, variables de retorno y argumentos son las mismas que para cualquier identificador en pseudocódigo [14]. Además, opcionalmente pueden agregarse las palabras claves Por Valor o Por Referencia para indicar el tipo de paso de variables en cada argumento. Si no se indica, los arreglos se pasan por referencia, las demás expresiones por valor. El paso por referencia implica que si el SubProceso modifica el argumento, se modificará en realidad la variable que se utilizó en la llamada, mientras que el paso por valor implica que el SubProceso opera con una copia de la variable (o el resultado de la expresión) que se utilizó en la llamada, por lo que las modificaciones que aplique el SubProceso no se verán reflejadas fuera de la misma [14]. Para invocar o llamar a un SubProceso (función o procedimiento) se debe utilizar su nombre y entre paréntesis los parámetros, que podrán ser expresiones sólo si el tipo de pasaje es por referencia. Una llamada puede ser en sí una instrucción, pero si la función retorna algún valor, también puede utilizarse como operando dentro de una expresión [14]. Ejemplo 22. Realizar una función que acepte dos números como parámetros y devuelva la suma de dichos números. Algoritmos en PSeInt Solución. Crearemos una función llamada sumarNumeros que será llamada en el programa principal. Pseudocódigo: Subproceso suma <- sumarNumeros(parametro1 Por Valor, parametro2 Por Valor) definir suma como Real; suma<-parametro1+parametro2; FinSubproceso Proceso Algoritmo22 Definir suma, num1, num2 como Real; suma<-0; num1<-0; num2<-0; Escribir "Ingrese el primer número"; Leer num1; Escribir "Ingrese el segundo número"; Leer num2; suma<-sumarNumeros(num1,num2); Escribir "La suma es: ", suma; FinProceso En este ejemplo, vemos la definición (declaración) de una función en pseudocódigo para calcular la suma de dos números reales, los cuales son pasados al subprograma como parámetros de entrada Por Valor. La función “sumarNumeros” calcula la suma y la devuelve como un parámetro de salida y se asigna en la variable suma. Esta función la podemos invocar desde el programa principal u otra función o procedimiento. Nótese que los nombres de los parámetros en la definición 88 89 Jéfferson Beltrán Morales (parametro1 y parametro2) no necesariamente tienen que ser los mismos que los utilizados en la invocación (num1 y num2). Nótese también que el compilador chequeará previamente que el tipo de dato del parámetro de salida de la función (en este caso un número real) pueda ser asignado a la variable suma según su tipo. Al nombre de la función junto con la lista ordenada de sus parámetros de entrada se lo conoce como firma de la función. En general, no puede haber dentro del mismo programa dos funciones con la misma firma. A primera vista, las funciones parecen dificultar la escritura de un programa. Sin embargo, no sólo no es así, sino que la organización de un programa en funciones y/o procedimientos lo hace más fácil de escribir y depurar, además que se reutiliza código en cada llamada a una función o procedimiento. Diagrama de flujo: Observe como dentro del programa principal se llama o invoca a la función “sumarNumeros”, se le pasa num1 y num2 como parámetros por valor y el resultado se asigna en la variable suma. Algoritmos en PSeInt Ejemplo 23. Se desea crear un procedimiento que de la bienvenida al usuario que ingresa al programa. SubProceso bienvenida (nombre) Escribir "Bienvenido " +nombre; FinSubProceso 90 Jéfferson Beltrán Morales 91 Proceso Algoritmo23 Definir nombre Como Caracter; Escribir "Ingrese su nombre"; Leer nombre; //llamada al procedimiento bienvenida(nombre); FinProceso Al nombre del procedimiento junto con la lista ordenada de sus parámetros de entrada se lo conoce como firma del procedimiento. En general, no puede haber dentro del mismo programa dos procedimientos con la misma firma. A primera vista, los procedimientos parecen dificultar la escritura de un programa. Sin embargo, no sólo no es así, sino que la organización de un programa en funciones y/o procedimientos lo hace más fácil de escribir y depurar, además que se reutiliza código al llamar a las funciones o procedimientos haciendo más eficiente la escritura de un programa y el mantenimiento del mismo. Diagrama de flujo: Algoritmos en PSeInt Note como en el programa principal se llama o invoca al procedimiento llamado “bienvenida”. Ejemplo 24. Realizar un algoritmo que calcule las operaciones matemáticas básicas (suma, resta, multiplicación y división) de dos números. Use funciones y procedimientos. Pseudocódigo: SubProceso suma <- sumar (num1, num2) Definir suma como Real; suma <- num1 + num2; FinSubProceso SubProceso multiplicacion <- multiplicar (num1, num2) Definir multiplicacion como Real; multiplicacion <- num1 * num2; FinSubProceso 92 Jéfferson Beltrán Morales 93 SubProceso resta <- restar (num1, num2) Definir resta como Real; resta <- num1 - num2; FinSubProceso SubProceso division <- dividir (num1, num2) Definir division como Real; division <- num1 / num2; FinSubProceso Subproceso calculadora (num1, num2) Escribir "El resultado de la suma es: ", sumar(num1,num2); Escribir "El resultado de la resta es: ", restar(num1,num2); Escribir "El resultado del producto es: ", multiplicar(num1,num2); Si num2!=0 Entonces Escribir "El resultado de la división es: ", dividir(num1,num2); Sino Escribir "El resultado de la división es: Denominador igual a 0"; FinSi FinSubProceso Proceso Algoritmo24 Definir num1,num2 como Real; num1<-0; num2<-0; Escribir "CALCULADORA"; Escribir "Ingrese el primer número: "; Algoritmos en PSeInt Leer num1; Escribir "Ingrese el segundo número: "; Leer num2; calculadora(num1,num2); FinProceso En el programa principal se orquesta (invoca) al resto de funciones o procedimientos, armando de esta manera el programa deseado; el procedimiento calculadora imprime en pantalla el resultado de las operaciones matemáticas básicas calculadas con funciones dentro del procedimiento. Note que en el programa principal no existe lógica de negocio, solamente debe haber escritura, lectura, validación de datos de entrada y llamadas a funciones y/o procedimientos. Diagrama de flujo: 94 95 Jéfferson Beltrán Morales Algoritmos en PSeInt Ejemplo 25. Realice la suma de dos matrices usando funciones y/o procedimientos, ingresar las dimensiones por teclado. Pseudocódigo: //Ejemplo de matrices con procedimientos y paso de valores por referencia //(matrices) y por valor //los arreglos por defecto se pasan por referencia //las variables por defecto se pasan por valor 96 Jéfferson Beltrán Morales 97 SubProceso leerMatriz (matriz Por Referencia, m Por Valor, n Por Valor ) Definir i, j Como Entero; Para i<-0 Hasta m-1 Con Paso 1 Hacer Para j<-0 Hasta n-1 Con Paso 1 Hacer Escribir "Ingrese el valor M[",i+1,",",j+1,"]"; Leer matriz[i,j]; FinPara FinPara FinSubProceso SubProceso escribirMatriz (matriz , m , n) Definir i, j Como Entero; Para i<-0 Hasta m-1 Con Paso 1 Hacer Para j<-0 Hasta n-1 Con Paso 1 Hacer Escribir sin Saltar "C[",i+1,",",j+1,"]=",matriz[i,j]," "; FinPara Escribir ""; FinPara FinSubProceso SubProceso sumarMatrices (matriz1, matriz2, matriz3 por referencia, m, n ) Definir i,j Como Entero; Para i<-0 Hasta m-1 Con Paso 1 Hacer Para j<-0 Hasta n-1 Con Paso 1 Hacer matriz3[i,j]<-matriz1[i,j]+matriz2[i,j]; FinPara Algoritmos en PSeInt FinPara FinSubProceso Proceso Algoritmo25 Definir i,j,m,n,matriz1,matriz2,matriz3 Como Entero; Escribir "Ingrese el número de filas: "; Leer m; Escribir "Ingrese el número de columnas: "; Leer n; //defino la dimensión de las matrices Dimension matriz1[m,n], matriz2[m,n], matriz3[m,n]; Limpiar Pantalla; //ingreso la primera matriz Escribir "Ingrese la matriz A:"; leerMatriz(matriz1,m,n); Limpiar Pantalla; //ingreso la segunda matriz Escribir "Ingrese la matriz B:"; leerMatriz(matriz2,m,n); Escribir "Presione enter..."; Esperar Tecla; Limpiar Pantalla; //sumo las matrices y las presento en pantalla sumarMatrices(matriz1, matriz2, matriz3, m, n); Escribir "La suma de matrices es:"; 98 Jéfferson Beltrán Morales 99 escribirMatriz(matriz3 , m , n); FinProceso En uno de los ejercicios anteriores, habíamos realizado el mismo ejercicio, note como ahora al usar funciones y/o procedimientos se hace más fácil de leer el programa, además de ser eficiente la escritura del mismo al no reescribir por ejemplo el mismo código cuando se lee cada matriz. Los arreglos por defecto se pasan por referencia y las variables por defecto se pasan por valor. Diagrama de flujo: Algoritmos en PSeInt 100 101 Jéfferson Beltrán Morales Algoritmos en PSeInt 102 Ejemplo 26. Realizar un algoritmo que ingrese una cadena de caracteres y cuente el número de vocales. Pseudocódigo: SubProceso cantVocales <- cuentaVocales ( frase ) Definir cantVocales, i, j como Entero; Definir vocales como Caracter; // pasamos toda la frase a minúsculas para no tener que preguntar 2 //veces por cada vocal frase<-Minusculas(frase); // lista de letras que vamos a buscar vocales<-"aeiouáéíóúü"; cantvocales<-0; // compara todas las letras de frase con las de vocales y contar //coincidencias Para i<-0 hasta Longitud(frase)-1 Hacer Para j<-0 hasta Longitud(vocales)-1 Hacer Si Subcadena(frase,i,i)=Subcadena(vocales,j,j) Entonces cantVocales<-cantVocales+1; FinSi FinPara FinPara FinSubProceso Proceso Algoritmo26 Definir frase,vocales Como Caracter; Definir i,j,cantVocales Como Entero; Jéfferson Beltrán Morales 103 Escribir "Ingrese una frase"; Leer frase; cantVocales<-cuentaVocales(frase); Escribir "La frase contiene ",cantVocales," vocales."; FinProceso En este ejercicio se usa las funciones propias de PSeInt para el manejo de cadenas de caracteres, además se crea una función para contar el número de vocales de una frase. Diagrama de flujo: Algoritmos en PSeInt 104 2.6 RECURSIVIDAD. Recursividad, es el proceso de resolver un problema reduciéndolo a uno o más sub problemas que son idénticos en su estructura al problema original y más simples de resolver [2]. Una vez que se ha subdividido el problema original, se utiliza la misma técnica de descomposición para subdividir cada uno de estos sub problemas en otros que son menos complejos, hasta que los sub problemas llegan a ser tan simples que se pueden resolver sin realizar más subdivisiones, y la solución general del problema se obtiene juntando todos los componentes resueltos [2]. Se dice que un proceso es recursivo si forma parte de sí mismo, es decir, que se define en función de sí mismo. En otras palabras, un método es recursivo si contiene invocaciones a sí mismo [2]. La recursión aparece en la vida diaria, en problemas matemáticos, en estructuras de datos y en muchos otros problemas. Es un proceso extremadamente potente, por lo que hay que saber cuándo y cómo aplicarla. Es una poderosa herramienta de programación como alternativa a algoritmos iterativos (cuando es “casi” imposible resolver con estructuras iterativas). Jéfferson Beltrán Morales 105 En resumen, la recursividad consiste en [2]: En el cuerpo de sentencias del SubPrograma (función) se invoca al propio SubPrograma (función) para resolver “una versión más pequeña” del problema original. Habrá un caso (o varios) tan simple que pueda resolverse directamente sin necesidad de hacer otra llamada recursiva. En general el pseudocódigo de un algoritmo recursivo es el siguiente [14]: SubProceso variable_de_retorno <- nombre_funcion_recursiva(argumento_1, argumento_2, ... ) acción 1; acción 2; . acción n; variable_de_retorno argumento_2, ... ) <- nombre_funcion_recursiva(argumento_1, FinSubproceso Las condiciones que debe cumplir un algoritmo recursivo son: Asegurar que existe una condición de salida, en la que no se producen llamadas recursivas (caso base). Asegurar que se cubren todos los posibles casos entre el caso base y los casos no base. Cada llamada, en el caso no base, conduce a problemas cada vez más pequeños que terminarán en el caso base. Ejemplo 27. Calcular el factorial de un número entero usando un algoritmo recursivo. Solución. Recordemos la definición recursiva del factorial: n! = n * (n-1)! , si n > 1 1! = 1 Por lo tanto, tenemos los casos: Si n = 1 (caso base) Retornar 1 Algoritmos en PSeInt Si n > 1 Retornar n * factorial(n – 1) Sea factorial(n) la función que calcula el factorial de un número entero n, sea n=4, entonces: factorial (4) = 4 * factorial (3) = 4 * 6 = 24 factorial (3) = 3 * factorial (2) = 3 *2 = 6 factorial (2) = 2 * factorial (1) = 2 * 1 = 2 factorial (1) = 1 Pseudocódigo: //Ejemplo de recursividad Subproceso fact <- factorial (n) Definir fact como Entero; Si n>1 Entonces fact <- n*factorial(n-1); Sino fact <- 1; FinSi FinSubproceso Proceso Algortimo27 Definir n como Entero; n<-0; Escribir "Factorial de un número entero"; Escribir "Ingrese en valor de n: "; Leer n; Escribir "El factorial de ",n," es ", factorial(n); FinProceso Diagrama de flujo: 106 107 Jéfferson Beltrán Morales A veces, podemos encontrar una solución iterativa simple, que haga que el algoritmo sea más eficiente. Algoritmos en PSeInt 108 Ejemplo 28. Calcular el factorial de un número entero usando un algoritmo iterativo. Solución. Se define el factorial como: n! = n * (n-1)! , si n > 1 1! = 1 De acuerdo con esta definición, se tiene que: n! = n * (n-1)!, si n>1 (n-1)! = (n-1) * (n-2)! (n-2)! = (n-2) * (n-3)! ……. (2)! = (2) * (1)! De donde: n!=n*(n-1)*(n-2)*…*1, que es la definición iterativa de factorial. Sea factorial(n) la función que calcula el factorial de un número entero n, sea n=4, entonces: factorial (4) = 4*3*2*1 = 24 Pseudocódigo: Subproceso fact <- factorial (n) Definir fact, i como Entero; fact<-1; Para i <- 1 Hasta n Con Paso 1 Hacer fact<-fact*i; FinPara FinSubProceso Proceso Algortimo28 Definir n como Entero; n<-0; Escribir "Factorial de un número entero"; Jéfferson Beltrán Morales 109 Escribir "Ingrese en valor de n: "; Leer n; Escribir "El factorial de ",n," es ", factorial(n); FinProceso Diagrama de flujo: Algoritmos en PSeInt 110 Ventajas. La principal ventaja es la simplicidad de comprensión y su gran potencia, favoreciendo la resolución de problemas complejos de manera natural, sencilla y elegante; y facilidad para comprobar y convencerse de que la solución del problema es correcta. El valor de la recursividad reside en el hecho de que se puede usar para resolver problemas sin fácil solución iterativa. Desventajas. El principal inconveniente es la ineficiencia tanto en tiempo como en memoria, dado que para permitir su uso es necesario transformar el programa recursivo en otro iterativo, que utiliza bucles y pilas para almacenar las variables. Una simple llamada puede generar un gran número de llamadas recursivas. (factorial(n) genera n llamadas recursivas). Observación. La recursividad se debe usar cuando sea realmente necesaria, es decir, cuando no exista una solución iterativa simple. 2.7 ESTILO DE PROGRAMACIÓN. 111 Jéfferson Beltrán Morales El buen estilo de programación es, sin lugar a dudas, una de las características más importantes que debe tener un buen programador. Un programa con buen estilo es más fácil de leer, de corregir y de mantener. Aunque la experiencia proporciona el estilo, existen una serie de reglas que se recomiendan seguir desde el principio del aprendizaje de la programación [17]. Comentarios. La legibilidad de los programas aumenta considerablemente utilizando comentarios. Un comentario es un texto explicativo más o menos largo, situado en el programa e ignorado por el compilador. Los comentarios son considerados parte del código fuente por más que sean ignorados en las etapas de compilación y ejecución, y tienen importancia primordial en las fases de análisis, puesta a punto y mantenimiento. Los comentarios son una parte fundamental de la documentación de un programa, ya que ayudan al programador y a otras personas a la comprensión del mismo. No es raro encontrar programas en los cuales los comentarios ocupan más lugar, incluso, que las propias instrucciones. Elección de nombres significativos para variables, funciones y procedimientos. Las variables, constantes, nombres de subprogramas y nombres de programas deben ser significativos para orientar al usuario o a otros programadores sobre lo que representan: X, Y, JJ no son identificadores significativos. Identación. Aunque no suele ser requerido por los compiladores de los lenguajes de alto nivel, es una práctica habitual realizar una identación (o tabulación) en cada uno de los bloques y unidades de programas fundamentales para mejorar la legibilidad del código fuente. Espacios y Líneas en Blanco. Con el mismo fin de mejorar le legibilidad y comprensibilidad del programa es recomendado utilizar espacios en blanco antes y después de un operador, así como también dejar una línea en blanco entre partes importantes o que estén lógicamente separadas. Validación. Los programas no pueden considerarse correctos hasta que han sido validados utilizando un rango amplio de datos de prueba para contemplar todas las posibles direcciones que el flujo de control puede tomar en tiempo de ejecución. Algoritmos en PSeInt 112 BIBLIOGRAFÍA. 1 “Fundamentos de Informática”. Luis A. Ureña y otros. Editorial Alfaomega rama, 1999. 2 “Fundamentos de Programación”. Libro de Problemas. Luis Joyanes Aguilar, Rodríguez Baena y Fernández Azuela. 1996. 3 “Pascal y Turbo Pascal enfoque práctico”. Luis Joyanes Aguilar. Editorial Mc Graw Hill, 1996. 4 “Lógica Simbólica”. Luis Joyanes Aguilar y Angel Hermoso. Editorial Mc Graw Hill. 5 “Programación en Pascal”. Irving M. Copi. Editorial C.E.C.S.A., 4ª Edición. 6 “Cómo programar en C/C++”. Sanford Leestma y Larry Nyhoff. Editorial Prentice Hall, 1999. 7 “Introducción a la Ciencia de las Computadoras”. Deitel/Deitel. Editorial Prentice Hall. 8 “Aprenda Visual C++ Ya”. Mark Andrews. Editorial Mc Graw Hill. 9 “Visual C++ 6.0 Manual de Referencia”. Chris Pappas y William H. Murray. Editorial Mc Graw Hill. 10 “Computación. Metodología, Lógica Computacional y Programación”. Ma. del Rosario Bores Rangel y Román Rosales Becerril. 1ª Edición, Editorial Mc Graw Hill, 1993. 11 “Programación en C”. Byron S. Gottfried. 1ª Edición, Editorial Mc Graw Hill, 1991. 12 “Metodología de la Programación. Algoritmos, Diagramas de Flujo y programas”. Tomos I y II. Osvaldo Cairó Battistutti. 1a Edición Editorial Alfaomega, 1995. 13 “Enciclopedia del Lenguaje C”. Francisco Javier Ceballos .1ª Edición, Editorial Alfaomega ra-ma, 1999. 14 http://pseint.sourceforge.net/ 15 “Tópicos de Programación”. Luis Eduardo Aponte. 2009. http://programandoenc.over-blog.es/article-28746669.html 16 “Metodología de la Programación”. Ana Mercedes Cáceres. 2006. http://rabanit06rm.tripod.com/metodologia.doc 17 “Estrella 0 - Fundamentos de la Programación”. Programa Microsoft Desarrollador 5 Estrellas. 2005. http://slideplayer.es/slide/122676/