Subido por Lucas

Algoritmica y programacion-apunte

Anuncio
DEPARTAMENTO DE INFORMÁTICA TRELEW
ALGORÍTMICA
Y
PROGRAMACIÓN I
APUNTES DE CÁTEDRA
Profesor: Dr. Diego Andrés Firmenich
Autoras: Mg. Lic. Alicia Beatriz Paur, Mg. Zulema Beatriz Rosanigo
Universidad Nacional de la Patagonia San Juan Bosco
Facultad de Ingeniería - Sede Trelew
Departamento Informática Trelew
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Algorítmica y Programación I
RESOLUCIÓN DE PROBLEMAS ............................................................................................................................ 3
No es lo mismo programar que codificar. ............................................................................................................ 3
ALGORITMO .......................................................................................................................................................... 6
¿Cómo expresamos un algoritmo? ................................................................................................................. 6
Diagrama de Flujo ......................................................................................................................................... 7
Diagrama Nassi-Schneiderman (N-S) o Diagrama de Chapin.......................................................................... 8
Pseudocódigo................................................................................................................................................ 9
Algunas definiciones ............................................................................................................................................... 9
Constantes y variables .......................................................................................................................................... 10
Estructura del algoritmo ........................................................................................................................................ 10
Declaración de constantes y variables .......................................................................................................... 11
Datos y tipos de datos........................................................................................................................................... 11
Tipos Numéricos.......................................................................................................................................... 11
Tipo Lógico.................................................................................................................................................. 13
Tipo Caracter............................................................................................................................................... 13
Tipo Enumerado .......................................................................................................................................... 13
Tipo Subrango ............................................................................................................................................. 14
Tipo cadena de caracteres ........................................................................................................................... 14
Expresiones.......................................................................................................................................................... 15
Las comparaciones entre valores de tipo real .................................................................................................... 16
Estructuras de control ........................................................................................................................................... 17
Primitivas de control o composición .............................................................................................................. 18
Combinación de estructuras de control ......................................................................................................... 23
Especificación de algoritmos ................................................................................................................................. 23
Metodología de resolución de problemas ............................................................................................................... 25
Un primer programa ..................................................................................................................................... 26
Otro programa: variante del problema anterior .............................................................................................. 28
Variables de uso particular .................................................................................................................................... 30
Prueba del algoritmo ............................................................................................................................................. 30
Validación de datos de entrada ............................................................................................................................. 31
Cómo finalizar la entrada de un conjunto de datos ................................................................................................. 31
Descomposición de problema: Funciones y Procedimientos................................................................................... 34
Tipos de acciones con nombre ......................................................................................................................... 36
Funciones.................................................................................................................................................... 36
Procedimientos ............................................................................................................................................ 40
Recursión ............................................................................................................................................................. 44
Ambito: variables locales y globales ...................................................................................................................... 51
Procedimientos anidados.................................................................................................................................. 52
Efectos laterales............................................................................................................................................... 52
Métodos de paso de parámetros ........................................................................................................................... 54
Estructuras de datos ............................................................................................................................................. 55
Arreglos ........................................................................................................................................................... 56
Recorridos en el arreglo ............................................................................................................................... 57
Accesos aleatorios en el arreglo................................................................................................................... 58
Definición del rango del índice...................................................................................................................... 58
Llenado de datos de un arreglo .................................................................................................................... 58
Algoritmos básicos con arreglos ................................................................................................................... 58
Ejemplos de problemas que requiere la utilización de arreglos. ..................................................................... 63
Cadenas de caracteres..................................................................................................................................... 63
Registros ......................................................................................................................................................... 65
Registros con variante ................................................................................................................................. 66
Conjuntos ........................................................................................................................................................ 68
Archivos........................................................................................................................................................... 69
Estructuras de datos en memoria principal ................................................................................................... 69
Clasificación de archivos.............................................................................................................................. 69
Organización de archivos. ............................................................................................................................ 69
Modos de acceso a los registros de un archivo ............................................................................................. 70
Operaciones sobre archivos ......................................................................................................................... 70
Entorno de programación mínimo para trabajar con archivos secuenciales. ................................................... 71
Archivos de texto ......................................................................................................................................... 71
Ejemplos de algoritmos con archivo de texto ................................................................................................ 72
Archivos de un determinado tipo de datos .................................................................................................... 73
Otras operaciones útiles para cualquier tipo de archivo ................................................................................. 75
Ejemplos de algoritmos con archivos de datos de un tipo determinado .......................................................... 75
Actualización de un archivo .......................................................................................................................... 79
Ejemplo de actualización en línea ................................................................................................................ 80
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 2
Algorítmica y Programación I
Ordenamiento, búsqueda e intercalación ............................................................................................................... 83
Métodos de ordenamiento ................................................................................................................................ 83
Método de selección .................................................................................................................................... 83
Método de intercambio o burbuja ................................................................................................................. 84
Método de inserción o de la baraja ............................................................................................................... 85
Método de ordenamiento rápido o quicksort.................................................................................................. 86
Ordenamiento en archivos ........................................................................................................................... 86
Métodos de Búsqueda...................................................................................................................................... 88
Búsqueda lineal ........................................................................................................................................... 88
Búsqueda binaria......................................................................................................................................... 89
Búsqueda en archivos.................................................................................................................................. 91
Análisis de los algoritmos de búsqueda............................................................................................................. 92
Intercalación .................................................................................................................................................... 92
Fusión de archivos....................................................................................................................................... 94
Cortes de control .................................................................................................................................................. 97
Listados - Consideraciones Generales .............................................................................................................. 97
Estructura de un algoritmo de listado ................................................................................................................ 98
Formato genérico de listados con subtotales ................................................................................................... 100
Ejemplos ........................................................................................................................................................ 100
Estructuras de datos dinámicas ........................................................................................................................... 105
Ejemplo de lista dinámica ............................................................................................................................... 106
Lenguaje Pascal ................................................................................................................................................. 109
Estructura de un Programa en Pascal ............................................................................................................. 109
Paralelo entre el seudocódigo y Pascal ........................................................................................................... 110
Precedencia de operadores en Pascal ............................................................................................................ 112
Compiladores de Pascal ................................................................................................................................. 114
Archivos en Turbo Pascal y en Dev Pascal ..................................................................................................... 114
Operaciones para archivos de texto en TurboPascal y Dev Pascal .................................................................. 115
Operaciones para archivos tipados en TurboPascal y Dev Pascal ................................................................... 115
Manejo de errores de entrada salida en TurboPascal y Dev Pascal ................................................................. 116
Bibliografía ......................................................................................................................................................... 118
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 3
Algorítmica y Programación I
RESOLUCIÓN DE PROBLEMAS
Un problema es un enunciado del que se conocen ciertos datos y que se
trata de averiguar el modo de obtener un resultado o solución.
Problema del
mundo real
Existen métodos sistemáticos y métodos científicos para resolver
problemas. Cualquier persona puede aprender algo de estos métodos e
incrementar su capacidad para resolver problemas.
Se pueden considerar cuatro aspectos en la solución de problemas:
1. Formulación de problemas: un problema debe estar formulado en
forma correcta, completa y precisa.
2. Expresión rigurosa de la solución o "algoritmo". Un algoritmo es
una enunciación sistemática y precisa del modo de resolver un
problema formulado correctamente. Implica el desarrollo de un
modelo "matemático" que represente el problema. Se debe
identificar las variables pertinentes e importantes del problema y
las relaciones existentes entre dichas variables y la solución del
problema.
Modelo
Matemático
Programa de
computadora
3. Formas de expresar algoritmos: mediante gráficos o diagramas de flujo, narraciones,
pseudocódigo.
4. Representación óptima de la información para el procesamiento sistemático o de computadora
Si tenemos un problema y hemos encontrado la manera de resolverlo por medio de una computadora, es
obvio que debemos tener métodos para indicar a la misma cómo llevar a cabo el proceso. Esto es lo que
se denomina "programación de computadoras".
No es lo mismo programar que codificar.
Programar es un proceso mental complejo dividido en varias etapas. Su finalidad es comprender
claramente el problema que se va a simular o resolver mediante la computadora, y entender también con
detalle cuál será el procedimiento mediante el cual la máquina llegará a la solución deseada.
La codificación es una etapa posterior a la programación y consiste en describir en un lenguaje de
programación la solución ya encontrada mediante la programación.
Fases de creación de un programa:
1. Entender el problema: Esta fase está dada por el enunciado del problema, el cual requiere una
definición clara y precisa. Implica hacer un mapa mental del problema y abarcarlo como un todo.
No hay reglas ni métodos para esta fase, pero si no se comprende bien el problema la solución
puede no ser la correcta. Es importante entender con claridad antes de abocar recursos a su
solución.
2. Análisis del problema: Una vez que se ha comprendido lo que se desea de la computadora, es
necesario definir:
 Los datos de entrada y posibles restricciones para sus valores.
 Cuál es la información que se desea producir (salida)
 Los métodos y fórmulas que se necesitan para procesar los datos.
Implica armar el modelo del problema y abstraerlo en sus distintas estructuras. Este modelo no
puede existir sin que se hayan especificado con claridad todos y cada uno de los componentes
estructurales del sistema.
3. Programar el modelo de solución propuesto (diseño del algoritmo): Una vez comprendido el
problema se trata de determinar qué pasos o acciones tenemos que realizar para resolverlo. La
razón de este paso es de disponer de un programa que puede ser probado mentalmente para
averiguar si en principio es correcto, y para determinar en qué grado considera todo el análisis
hecho anteriormente.
Un programa está constituido por dos tipos de componentes: estructuras de datos y estructuras
de control. Las estructuras de control son las distintas formas que existen para dirigir el flujo de
acciones que el procesador efectuará sobre los datos que manejan en un programa. Una
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 4
Algorítmica y Programación I
filosofía a la hora de diseñar algoritmos es el refinamiento por pasos, y es partir de una idea
general e ir concretando cada vez más esa descripción hasta que tengamos algo concreto para
resolver. Pasamos de lo más complejo a lo más simple.
Si el problema es complejo, lo mejor es dividirlo en partes más pequeñas e intentar resolverlas
por separado. Esta metodología de “divide y vencerás” también se conoce con el nombre de
diseño descendente. Al dividir el problema en módulos o partes se comprende más fácilmente y
también resulta más sencillo mantenerlo. En cuanto a los resultados, se probarán mucho mejor
comprobando si cada módulo da el resultado correcto que si intentamos probar de un golpe todo
el programa ya que si se produce un error sabemos en qué módulo ha sido.
Los métodos usuales para representar un algoritmo son:
- Formas gráficas: diagrama de flujo y diagramas estructurados N-S (NassiSchneiderman).
- Lenguaje de especificación de algoritmos: pseudocódigo
4. Codificación: Como un programa en pseudocódigo o mediante diagramas no es ejecutable en
una computadora, se debe traducir a un lenguaje de programación. La codificación es la
operación de escribir la solución del problema (de acuerdo a la lógica del diagrama de flujo o
pseudo código), en una serie de instrucciones detalladas, en un código reconocible por la
computadora, la serie de instrucciones detalladas se le conoce como código fuente, el cual se
escribe en un lenguaje de programación. Ejemplos de lenguajes de programación son Pascal, C,
java, Ada, Basic, etc.
5. Ejecución y ajuste: Los errores humanos dentro de la programación de computadoras son
muchos y aumentan considerablemente con la complejidad del problema. El proceso de
identificar y eliminar errores, para dar paso a una solución sin errores se le llama depuración.
Existen dos tipos de fallas que es posible encontrar en un programa ya codificado: errores de
sintaxis/semántica y errores de lógica de programación. Un error de lógica apunta claramente a
omisiones y errores en el modelado que se está tratando de hacer de la realidad, generalmente
se debe a un deficiente análisis o a una programación en pseudocódigo incompleta y
apresurada. Los errores de sintaxis/semántica son fácilmente encontrados por el traductor del
lenguaje de programación, ya sea intérprete o compilador, y se trata de formulaciones
incorrectas de las expresiones del lenguaje, como por ejemplo ausencia de un punto y coma,
falta de declaración de un identificador, mal uso de una primitiva, etc.
6. Documentación: Es la guía o comunicación escrita en sus variadas formas, ya sea en
enunciados, procedimientos, dibujos o diagramas. A menudo un programa escrito por una
persona, es usado por otra. Por ello la documentación sirve para ayudar a comprender o usar un
programa o para facilitar futuras modificaciones (mantenimiento).
La documentación abarca tres partes:
o Documentación Interna: Son los comentarios o mensaje que se añaden al código
fuente para hacer más claro el entendimiento de un proceso.
o Documentación Externa: Se define en un documento escrito los siguientes puntos:
Descripción del Problema
Nombre del Autor, fecha de realización
Algoritmo (diagrama de flujo o pseudo código)
Diccionario de Datos
Código Fuente (programa)
o Manual del Usuario: Describe en detalle la manera cómo funciona el programa, con el
fin de que el usuario obtenga el resultado deseado.
7. Mantenimiento: Se lleva a cabo después de terminado el programa y se realiza a lo largo de su
vida útil, cuando se detecta que es necesario hacer algún cambio, ajuste o complementación al
programa para que siga trabajando de manera correcta. Para poder realizar este trabajo se
requiere que el programa este correctamente documentado. Hay que ser capaces de hacer
alteraciones no estructurales al sistema con un costo mínimo de análisis y programación.
Cualquiera sea el tamaño de un programa tenemos el compromiso de hacerlos claros y flexibles
para que admitan mejoras o sugerencias posteriores.
Un programa es la suma del algoritmo y las especificaciones necesarias para que éste emita un
resultado. En términos formales:
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 5
Algorítmica y Programación I
Programa = estructuras de datos + estructuras de control
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 6
Algorítmica y Programación I
ALGORITMO
La palabra algoritmo se deriva de la traducción al latín de la palabra árabe Al-Khowarîzmî, nombre del
matemático y astrónomo árabe Mohammed ibn-Musa Al-Khowarizmi que escribió un tratado sobre
la manipulación de números y ecuaciones en el siglo IX. Su obra se empezó a conocer como "de AlJuarizmi" y por deformación en la traducción llegó hasta "algoritmo".
Actualmente el término algoritmo significa procedimientos operativos que permiten resolver cualquier
problema de un determinado tipo. Básicamente, un algoritmo es un método para resolver un problema
mediante una serie de pasos. Las características de un buen algoritmo son:
 Debe tener un punto particular de inicio.
 Debe ser definido, no debe permitir dobles interpretaciones. Si se aplica partiendo de la
misma situación inicial, debe obtenerse siempre el mismo resultado.
 Debe ser general, es decir, soportar la mayoría de las variantes que se puedan presentar en
la definición del problema.
 Debe ser finito en tamaño y tiempo de ejecución.
Ejemplo: Algoritmo para armar la lista de aprobados y desaprobados de un examen escrito.
Tomar una hoja en blanco y dibujar dos columnas: Una para aprobados y otra para desaprobados.
Traer la pila de exámenes corregidos.
Mientras la pila no esté vacía
Tomar el examen de la cima de la pila.
Si el examen está aprobado, anotarlo en la columna de aprobados sino, anotarlo en la
columna de desaprobados.
Colocar el examen en la pila de exámenes pasados
Publicar la hoja con las dos columnas que se acaba de completar.
En esta descripción de pasos, podemos distinguir:
o
Objetos que deben existir para que el algoritmo se pueda realizar, como la pila de exámenes
corregidos y la hoja para anotar.
o
Condiciones o enunciados lógicos que pueden ser verdaderos o falsos, como “la pila no esté
vacía”, “el examen está aprobado”
o
Secuencias de acciones: “tomar una hoja en blanco”, “dibujar dos columnas”.
o
o
Alternativas de acción: “anotar en aprobados” o “anotar en desaprobados” pero no ambas.
Repetición de acciones según sea cierta una condición: “mientras la pila no está vacía” se hacen
un conjunto de acciones.
Todos los objetos que son necesarios para que el algoritmo se pueda desarrollar conforman el
“ambiente” del algoritmo. Pueden mantenerse constantes en toda la ejecución o pueden ir variando su
valor o estado.
El lenguaje con que se expresan los pasos debe ser claro y entendible por cualquier persona o entidad
que lo deba interpretar.
¿Cómo expresamos un algoritmo?
Se dispone de una notación algorítmica que permite:
o
o
o
Describir las operaciones puestas en juego (acciones).
Describir los objetos manipulados por el algoritmo (datos/informaciones).
Controlar la realización de las acciones, indicando el modo de organización de estas acciones en
el tiempo (mediante las primitivas de composición o de control).
Debe subrayarse que la notación algorítmica no es en ningún caso un lenguaje de programación. Lo
esencial de la notación algorítmica es que las acciones elegidas sean las necesarias y suficientes para
expresar todo algoritmo. En este sentido, autores como E. Dijkstra, C.A.R. Hoare y otros consolidaron a
finales de los años 60 los fundamentos de la Programación Estructurada, mediante la proposición del
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 7
Algorítmica y Programación I
uso de un conjunto de construcciones lógicas (secuencia, decisión e iteración) con las que se podría
escribir cualquier programa.
La notación algorítmica puede realizarse mediante diagramas de flujo, diagramas estructurados NassiSchneiderman y lenguaje de especificación de algoritmo o pseudocódigo.
Diagrama de Flujo de Datos
Es una herramienta gráfica de descripción de algoritmos. Se caracteriza por utilizar un conjunto de
símbolos gráficos normalizados y expresar de forma clara los posibles caminos de ejecución de las
acciones (secuencia, decisión e iteración).
El principal inconveniente que plantea el diagrama de flujo es que permite realizar cualquier tipo de
construcción algorítmica, independientemente de que ésta sea correcta o no desde el punto de vista de
la programación estructurada.
Los elementos básicos son: rectángulos con puntas redondeadas para indicar el comienzo o el final del
algoritmo, rectángulos para indicar acciones, rombos para indicar condiciones que permiten tomar
caminos alternativos, y flechas.
Ejemplo de diagrama de
flujo: Algoritmo para armar
la lista de aprobados y
desaprobados
de
un
examen escrito.
Inicio
Tomar una hoja en blanco
Dibujar dos columnas:
aprobados y desaprobados.
Traer la pila de exámenes
corregidos
no
pila no
vacía
si
Tomar el examen de la cima de
la pila
no
Anotarlo en la columna de
desaprobados
examen
aprobado
si
Anotarlo en la columna de
aprobados
Colocar el examen en la pila de
exámenes pasados
Publicar la hoja con las dos
columnas
Fin
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 8
Algorítmica y Programación I
Diagrama Nassi-Schneiderman (N-S) o Diagrama de Chapin
El diagramas N-S de Nassi-Schneiderman, también conocido como diagrama de Chapin, es un método
gráfico para la descripción de algoritmos que combina la descripción textual, propia del pseudocódigo,
con la representación gráfica del diagrama de flujo. Cuenta con un conjunto limitado de símbolos gráficos
y palabras reservadas para representar los pasos del algoritmo. El símbolo básico utilizado es el
rectángulo, por lo que también se lo conoce como diagrama de cajas.
El modelo de diagramas fue desarrollado por Isaac Nassi y Ben Shneiderman, publicándose en el año
1973. Corresponden a las normas DIN 66261 e ISO/IEC 8631:1989. Dado que muestran las estructuras
de un programa, también se denominan "estructogramas".
Los diagramas proporcionan toda la lógica necesaria para que programadores puedan escribir
programas estructurados en cualquier lenguaje de programación o a los efectos de documentar
procedimientos específicos. Cada bloque (o secuencia de acciones) se representa dentro de un
rectángulo; cada estructura utiliza una forma de representación particular, pudiendo a su vez encerrar
otros bloques. Existen seis estructuras: Secuencial, Decisión (si condicional), Selección, Para, Mientras y
Repetir
Presentan una serie de ventajas:

Dada su forma de construcción es imposible representar algoritmos incorrectos desde el punto
de vista de la programación estructurada.
 El ámbito de cada estructura está perfectamente definido.
Y también una serie de inconvenientes:
 Cada diagrama sólo refleja la estructura de un único módulo.
 No pueden modificarse fácilmente. Es necesario construirlos de nuevo, aunque sea para realizar
pequeños cambios.
Ejemplo de diagrama NS: Algoritmo para armar la lista de aprobados y desaprobados de un examen
escrito.
Tomar una hoja en blanco
Dibujar dos columnas: aprobados y desaprobados.
Traer la pila de exámenes corregidos
Mientras pila no vacía
Tomar el examen de la cima de la pila
examen aprobado
si
no
Anotar en Aprobados
Anotar en Desaprobados
Colocar el examen en la pila de exámenes pasados
Publicar la hoja con las dos columnas
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 9
Algorítmica y Programación I
Pseudocódigo
El pseudocódigo es una notación algorítmica textual caracterizada por:
 Permitir la declaración de los datos (constantes y/o variables) manipulados por el algoritmo.
 Disponer de un conjunto pequeño y completo de palabras reservadas que permitan expresar: las
acciones elementales, las primitivas de composición de acciones y la definición de acciones con
nombre (funciones y procedimientos).
El pseudocódigo es un lenguaje neutro y completo, es decir, denota independencia respecto a alguna
máquina en particular y tiene poder suficiente para expresar cualquier idea computacional desde la
perspectiva de los procedimientos estructurados.
Cuando se escribe un pseudocódigo, lo que se hace es indicar las acciones que se tienen que realizar
para dar solución a un determinado problema, en español o en inglés estructurado permitiéndonos
escribir de forma natural, como "relatando" los pasos que se tienen que seguir.
Utilizar un pseudocódigo tiene la ventaja de que el programador no se preocupa por los detalles que
exigen los lenguajes de programación, permitiendo una mayor concentración en la lógica de la resolución
del problema.
Existen palabras específicas, denominadas metapalabras que tienen un significado y objetivo específico
dentro del pseudocódigo. Las metapalabras que utilizaremos son las siguientes, su significado y uso se
irá viendo a lo largo del curso:
Abrir
Cadena
Conjunto
Entero
Falso
Hasta
Lógico
Ó o OR
Registro
Sino
Verdadero o Verd
Algoritmo
Carácter
Constantes o Cons
Entonces
Fin
Ingresar
Mientras
Paso
Repetir
Tipo
Y o And
Archivo
Cerrar
Crear
Eof
Función
Inicio
Mostrar
Procedimiento
Según
Variables o Var
Arreglo
Con
Desde
Escribir
hacer
Leer
No
Real
Si
Veces
Algunas definiciones
Ambiente del algoritmo: es el conjunto de todos los recursos para la ejecución de un trabajo.
Acción: es un evento o acontecimiento que modifica el ambiente. Una acción es primitiva o simple si
su enunciado es suficiente para que pueda ejecutarse sin información adicional. Por el contrario, una
acción es no-primitiva o compuesta si para ejecutar su enunciado es necesario descomponerla en
acciones primitivas.
Nos interesarán solamente aquellos acontecimientos que tienen lugar durante un período de tiempo finito
y producen un resultado bien definido y previsible.
El hecho de que una acción dure un tiempo finito significa que es posible hallar un instante de inicio de la
acción (t0) y un instante de finalización de la acción (t1). Para que sea posible reconocer el resultado es
preciso observar en el sistema una serie de atributos o características que cambien su valor en ese
intervalo de tiempo.
Proceso: Conjunto de acontecimientos organizados en el tiempo y concebido como activo. En nuestro
contexto informático, describir un proceso es interpretar un acontecimiento como un conjunto de
acciones más elementales cuyo efecto acumulativo es el mismo que el producido por el acontecimiento
completo.
Estado (de un sistema para un proceso determinado) en un instante dado t: Conjunto de los valores de
los distintos atributos o características de interés en el instante t del desarrollo del proceso.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 10
Algorítmica y Programación I
El estado en el instante t0 de una acción define los datos de la acción, mientras que el estado en el
instante t1 define los resultados de la acción (o también datos de entrada y datos de salida,
respectivamente). Por otra parte, el hecho de que el resultado de la acción sea previsible significa que si
se conoce el estado del sistema en el instante t0 entonces se puede decir con total precisión cuál será el
estado en el instante t1, incluso antes de que la acción en cuestión se produzca.
Condición: es una afirmación lógica sobre el estado del problema que puede ser verdadera o falsa en el
momento de observación.
Constantes y variables
Constante: es un objeto cuyo valor no puede variar. Puede tener un nombre que la identifique o no, si
no lo llevan, se llaman literales. La ventaja de usar constantes con nombre es que en cualquier lugar
donde quiera que vaya la constante, basta con poner su nombre y luego el compilador lo sustituira por su
valor. Por ejemplo, PI es el nombre de la constante 3.14159...
Variable: es un objeto cuyo valor puede variar. Debe tener un nombre que la identifique y debe indicarse
el tipo de dato que guardará. Los atributos de una variable son: nombre o identificador, valor, tipo,
dirección de memoria.
Elección de nombres: Los nombres permiten identificar a los distintos objetos del ambiente. Deben ser
significativos, es decir, deben dar idea de lo que representan. Los nombres no pueden tener espacios,
deben comenzar con alguna letra del alfabeto y puede ser seguida de otras letras, dígitos y el guión
subrayador. Dependiendo del lenguaje de programación, las reglas de nombres pueden ser sensibles a
mayúsculas y minúsculas, es decir, dos nombres que suenan iguales pero uno en mayúsculas y otro en
minúsculas como N y n son identificadores distintos, mientras que otros lenguajes los toman como
iguales. Nosotros adoptaremos la última forma.
Ejemplo:
Identificadores válidos
MiCasa, este_ejemplo, total1
no válidos
mi casa, 1dia, $
Estructura del algoritmo
Cabecera: Debe indicarse el nombre del algoritmo, y la lista de parámetros si los hubiera. Generalmente
se la acompaña de un comentario que sirva de documentación.
Declaración del ambiente: Se detallan los recursos que se utilizarán, indicando si se trata de
declaraciones de constantes, variables, tipos, etc.
Cuerpo: Es el conjunto de acciones que deberán realizarse. Van encerradas entre las metapalabras
Inicio y fin
Cabecera
Declaración del
ambiente
Cuerpo
Algoritmo armarListaAprobadosDesaprobados
Ambiente
Pila_Examenes
Hoja
Examen
Inicio
Tomar una hoja en blanco y dibujar dos columnas: Una para aprobados y otra
para desaprobados.
Traer la pila de exámenes corregidos.
Mientras la pila no esté vacía
Tomar el examen de la cima de la pila.
Si el examen está aprobado, anotarlo en la columna de aprobados sino,
anotarlo en la columna de desaprobados.
Colocar el examen en la pila de exámenes pasados
Publicar la hoja con las dos columnas que se acaba de completar.
Fin
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 11
Algorítmica y Programación I
Inclusión de comentarios: en cualquier parte del algoritmo, se pueden incluir comentarios que sirvan
para clarificar la solución del problema. El comentario es un texto libre encerrado entre secuencias de (*
y *). También podemos utilizar // para indicar el comienzo de un comentario de línea, todo lo que sigue
al // y hasta el final del renglón es un comentario
Ejemplo: (* esto es un comentario *) // y este otro también
Declaración de constantes y variables
Dentro del ambiente del algoritmo, distinguiremos una sección para la declaración de constantes y otra
para la declaración de variables.
La sección de constantes será precedida por la metapalabra Constantes o Const, y a continuación se
declararán las constantes que se utilizarán siguiendo la sintaxis: <identificador> = <valor>
Ejemplo
Const
PI = 3.14159
Tope = 100
Universidad = “U.N.P.S.J.B.”
El tipo de una constante se deduce de su valor. PI es una constante real, Tope es entera y Universidad
es de tipo cadena de caracteres.
La sección de variables será precedida por la metapalabra Variables o Var, y a continuación se
declararán las variables que se utilizarán siguiendo la sintaxis: <identificador>: <tipo>
Ejemplo
Var
Cantidad: entero
Superficie: real
EsEstudioso: lógico
Datos y tipos de datos
Los datos que maneja un programa pertenecen a un tipo determinado. El tipo define:
o
o
o
el conjunto de valores posibles (rango),
el conjunto de operaciones posibles (lo que podríamos llamar su álgebra),
su representación interna.
Podemos clasificarlos en simples y compuestos (o estructurados). Los primeros denotan un solo valor,
los segundos representan un conjunto de valores. Los datos pueden ser variables o constantes.
Simple
Ejemplo
Estructurado
Ejemplo
entero
real
lógico
carácter
enumerados
subrango
290, -21
32.17, -2.002
verdadero, falso
‘A’ , ‘5’, ‘+’
cadena
arreglo
registro
archivo
conjunto
“hola a todos”
{3, 5, 7, 3, 15}
{“Ana”, 15, verdadero}
Tipos Numéricos
Permiten representar valores escalares de forma numérica, esto incluye a los números enteros y los
reales y realizar operaciones aritméticas comunes.
Entero: tipo simple ordinal que se utiliza para representar valores numéricos enteros, en un rango finito
dado por la implementación.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 12
Algorítmica y Programación I
Variantes de enteros: De acuerdo con la cantidad de bytes de la implementación pueden variar en:
Entero corto: Utiliza uno o dos bytes para su implementación.
Entero normal: Utiliza dos o cuatro bytes
Entero largo: Utiliza cuatro o seis bytes.
n-1
n-1
El rango de valores de cada caso, será -2 .. 2 -1, donde n es la cantidad de bits de la representación.
Por ejemplo, un entero corto de un byte, tendrá un rango que va desde –128 a 127.
Ejemplos:
12
919
(número de mes)
(páginas de un libro)
35.000.000 (habitantes de Argentina)
Real: tipo simple que se utiliza para representar valores numéricos del conjunto de números reales.
Pueden ser de simple y doble precisión. Utiliza implementación de punto flotante.
Representación
Bit de signo
exponente mantisa
Recordar que existe un rango de valores reales cuya representación es la misma debido al límite finito de
dígitos de la mantisa y que números reales con un número finito de dígitos decimales puede tener una
mantisa con una serie infinita de números binarios, como por ejemplo el valor decimal 0.10, por lo que es
muy importante tener presente estas cuestiones en el momento de comparar valores.
Ejemplos:
3.14159
(pi)
9.8
(aceleración de la gravedad)
PuntoFijo: Algunos lenguajes brindan un tipo especial de dato para representar montos de dinero. Ya
que la cantidad de decimales de un monto de dinero es fija (generalmente es dos, por los centavos), es
conveniente utilizar un tipo de dato de punto fijo en lugar de punto flotante. En este caso, se debe aclarar
en su definición con cuántos decimales se fija. De no poseer este tipo, magnitudes como el dinero
pueden ser representadas en un entero largo en centavos o miliavos, y evitar de esta manera los errores
propios del redondeo y truncamiento de números reales.
Operaciones numéricas
Operación
Operador
Enteros
Reales
PuntoFijo
Suma
Resta
Producto
División entera
División real
Resto de la división entera
+
*
div
/
mod
Sí
Sí
Sí
Sí
No
Sí
Sí
Sí
Sí
No
Sí
No
Sí
Sí
Sí
No
Sí
No
Funciones
Argumento
Resultado
Ejemplo
Siguiente
Anterior
Valor absoluto
Redondear
Truncar
Raiz cuadrada
Valor absoluto
Trigonométricas
Entero
Entero
Entero o real
Real
Real
Real
Real o entero
Real
entero
Entero
Entero o real
Real
Entero
Real
Real o entero
Real
Logaritmo natural
Real
Real
Sig(n)
Ant(n)
Abs(n)
Redondear(x)
Trunc(x)
RC(x)
Abs (x)
Seno(x)
Coseno(x)
Tg(x)
Ln(x)
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 13
Algorítmica y Programación I
Exponencial
Real
Real
Exp(x)
Tipo Lógico
Lógico: tipo simple ordinal que se utiliza para representar valores booleanos (verdadero, falso).
Representan el resultado de una comparación entre otros datos (numéricos o alfanuméricos). Alcanza un
bit pero la menor unidad de direccionamiento es el byte por lo que se utiliza un byte.
Ejemplos:
verdadero
falso
(soy alumno de la Facultad de Ingeniería)
(estoy volando por las nubes)
Operaciones lógicas
Operación
Negación
Conjunción
Disyunción
Operador
No (not)
Y (and)
O (or)
Ejemplo
No esBueno
Estudia Y esBueno
Estudia O esBueno
Tipo Carácter
Carácter: tipo simple ordinal que se utiliza para representar los distintos caracteres empleados para
formar palabras, expresiones aritmético-lógicas o para graficación. Abarca letras mayúsculas, letras
minúsculas, dígitos y caracteres especiales. Este conjunto de valores se normalizó, por un estándar
llamado ASCII (American Standard Code for Information Interchange), el cual permite establecer un
orden de precedencia entre los mismos. Los valores constantes de este tipo se escriben entre apóstrofos
o comillas simples.
Ejemplos:
‘B'
'#'
(be mayúscula)
(numeral)
''
'2'
(asterisco)
(dígito 2)
Operaciones con caracteres
Funciones
Argumento
Resultado
Ejemplo
Sig(carácter) da el siguiente carácter carácter
de la tabla
carácter
Sig(‘B’) da la letra ‘C’
Ant(carácter) da el carácter anterior carácter
en la tabla
carácter
Ant(‘C’) da la ‘B’
Ord(carácter) da el número de orden carácter
en la tabla de caracteres
entero
Ord(‘A’) da 65
Chr(entero) da el carácter que se Entero (0-255)
encuentra en un determinado lugar
de la tabla.
carácter
Chr(65) da ‘A’
Tipo Enumerado
Son aquellos cuyos valores se pueden definir por extensión. Abarcan a los caracteres, enteros y lógicos.
También es posible definir nuevos tipos de datos por enumeración de sus valores. En este caso se
define un nuevo nombre de tipo y se lo asocia con el conjunto de valores dado por enumeración. Cada
nuevo valor tiene un nombre no utilizado hasta el momento y el sistema le asocia una representación
interna. La posibilidad de definir nuevos tipos de datos contribuye a dar mayor claridad al problema.
Ejemplo: crearemos un nuevo tipo de datos llamado colorPrimario con los valores de esos colores. Para
ello en el ambiente, habrá una sección de definición de tipos.
Tipo
ColorPrimario = (rojo, amarillo, azul)
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 14
Algorítmica y Programación I
Var
Color: ColorPrimario
La variable color podrá tomar cualquiera de los valores detallados para su tipo.
Operaciones con tipos enumerados
Funciones
Argumento
Resultado
Ejemplo
Siguiente(enumerado) da el Enumerado
siguiente enumerado de su lista
de definición
Enumerado
Sig(rojo) da el valor
amarillo
Anterior(enumerado)
da
el Enumerado
previo enumerado de su lista de
definición
Enumerado
Ant(amarillo)
valor rojo
da
el
Tipo Subrango
Se pueden crear nuevos tipos de datos por subrango de valores de los tipos enumerados.
Ejemplos:
Tipo
ColorPrimario = (rojo, amarillo, azul)
Digito = 0..9
// Subrango de enteros
Incognita = ‘X’.. ‘Z’
// subrango de caracteres
ColorBoca = amarillo..azul
// subrango de ColorPrimario
Una variable de tipo Digito podrá tener valores entre 0 y 9 y podrá aparecer en cualquier contexto de tipo
entero, una de tipo Incógnita podrá tener valor ‘X’, ‘Y’ o ‘Z’ y podrá utilizarse en cualquier contexto que
espere un carácter. Una variable de tipo ColorBoca podrá tener valor amarillo o azul y podrá utilizarse en
el contexto que espere un tipo ColorPrimario. Observar que para poder definir el subrango ColorBoca,
previamente tuvo que estar definido un tipo donde los valores inicio y fin del subrango estén definidos, en
este caso se trata del tipo ColorPrimario.
Las operaciones permitidas para los tipos subrangos son aquellas del tipo al que corresponden los
extremos del subrango el cual debe ser de algún tipo enumerado (entero, carácter, lógico o enumerado
creado por el usuario).
Tipo cadena de caracteres
Cadena: tipo compuesto por una secuencia de caracteres que es utilizado para expresar palabras. Es
una secuencia de caracteres alfanuméricos que permiten representar valores identificables de forma
descriptiva, esto incluye nombres de personas, direcciones, etc. Es posible representar números como
alfanuméricos, pero estos pierden su propiedad matemática, es decir no es posible hacer operaciones
con ellos. Las constantes de este tipo de datos se representan encerradas entre comillas.
Ejemplos:
“Mi perro”
“No”
“” (la cadena vacía)
“S” (cadena unitaria)
“32.324.123”
Operaciones con cadenas de caracteres
Funciones
Argumento
Resultado
Ejemplo
Longitud(cadena) da la cantidad Cadena
de caracteres actual en la
cadena
entero
Long(“yo” ) es 2
Concatena(cadena1,cadena2)
Dos cadenas
da como resultado una cadena
formada por las recibidas como
cadena
concatena(“Hola”,
“
a
todos”) da como resultado
“Hola a Todos”
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 15
Algorítmica y Programación I
argumentos
Subcadena(cadena, pos, n): da 1 cadena, 2 cadena
la subcadena de n caracteres a enteros
partir de la posición pos
Acceso a un elemento de la cadena de caracteres
Subcadena(“Hola”,3,2) da
“la”
Para acceder a un elemento de la cadena (un carácter), se tiene el operador [] . Se debe indicar el índice
del carácter al que se quiere acceder, el índice es un valor entero que podrá variar entre 1 y la longitud
de la cadena. Ejemplo: Sea nombre de tipo cadena y nombre contiene el valor “Juan”.
Nombre[1] nos dará la ‘J’ y nombre[4] nos dará la ‘n’
Expresiones
Las expresiones son combinaciones de constantes, variables, símbolos de operación, paréntesis y
nombres de funciones especiales. Por ejemplo:
a + (b + 3) / c
Cada expresión toma un valor que se determina tomando los valores de las variables y constantes
implicadas y la ejecución de las operaciones indicadas.
Una expresión consta de operadores y operandos. Según sea el tipo de datos que manipulan, se
clasifican las expresiones en:
o
o
o
Aritméticas
Relacionales
Lógicas
Operadores Aritméticos: Los operadores aritméticos permiten la realización de operaciones
matemáticas. Pueden ser utilizados con tipos de datos enteros o reales, constantes o variables. Si
ambos son enteros, el resultado es entero; si alguno de ellos es real, el resultado es real.
+
*
div
Ejemplos:
Expresión
Resultado
Suma
Multiplicación
División entera
7.6 / 2
3.8
/
Mod
15 mod 4
3
Resta
División real
Modulo (residuo de la división entera)
14 + 2 * 5
24
Prioridad de los Operadores Aritméticos
1. Todas las expresiones entre paréntesis se evalúan primero. Las expresiones con paréntesis
anidados se evalúan de dentro a fuera, el paréntesis más interno se evalúa primero.
2. Dentro de una misma expresión los operadores se evalúan en el siguiente orden.
a. *, /, div, mod Multiplicación, división real, división entera, módulo.
b. +, Suma y resta.
3. Los operadores en una misma expresión con igual nivel de prioridad se evalúan de izquierda a
derecha.
Ejemplos:
14 + 2 * 5 = 24
203 * 2 div 5 = 81
30 + 5 * (10 - (21 + 4)) = -45
12.2 / 2 + 5 mod 2 = 7.1
46.5 / 5.0 = 9.3
13 mod 5 + 5 * (10 - 6) = 23
Operadores Relacionales: Se utilizan para establecer una relación entre dos valores. Compara estos
valores entre sí y esta comparación produce un resultado de certeza o falsedad (verdadero o falso).
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 16
Algorítmica y Programación I
Los operadores relacionales comparan valores del mismo tipo (numéricos, carácter, enumerados o
cadenas).
Tienen el mismo nivel de prioridad en su evaluación pero tienen menor prioridad que los aritméticos.
>
Mayor que
<
Menor que
> = Mayor o igual que
< = Menor o igual que
< > Diferente
= Igual
Ejemplos: Si a vale 1, b vale 2, c vale 3, nomb1 vale “Ana” y nomb2 vale “Amalia”
a+b>c
“Juan” <> nomb2
“Mara” < “Amalia”
Falso
Verdadero
Falso
a - b <= c
nomb1[1] = ‘A’
nomb1 < nomb2
Verdadero
Verdadero
Falso
Ejemplos no válidos:
a<b<c
nomb1 < 30
porque compara más de dos elementos a la vez
porque compara operandos de distinto tipo
Operadores Lógicos: Estos operadores se utilizan para establecer relaciones entre valores lógicos, los
cuales pueden ser resultado de una expresión relacional.
No Negación
Tablas de verdad
Y
Conjunción
O
Op1
Op2
Op1 y Op2
Op1 o Op2
V
V
F
F
V
F
V
F
V
F
F
F
V
V
V
F
Ejemplos:
(10 < 20)
V
Y
(20 > 30)
F
(10 < 20)
V
F
O
Disyunción
No Op1
V
F
(20 > 30)
F
V
Prioridad de los Operadores Lógicos
No
Y
O
Prioridad de los Operadores en General: Aunque cada lenguaje de programación establece el orden
de prioridad, nosotros tomaremos el siguiente orden:
1.
2.
3.
4.
5.
6.
7.
()
*, /, Div, Mod
+, No
Y
O
>, <, > =, < =, < >, =
Las comparaciones entre valores de tipo real
Los datos de tipo real se almacenan en formato de punto flotante. Como la cantidad de dígitos
significativos de un número real puede ser matemáticamente infinita y la representación en la máquina
es finita, se provocan redondeos y truncamiento, pudiendo resultar que dos números reales
matemáticamente distintos tengan igual representación interna.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 17
Algorítmica y Programación I
También puede darse el caso que dos valores matemáticamente iguales tengan representación
computacional distinta: el resultado de dos operaciones sea matemáticamente idéntico pero por surgir de
representaciones distintas antes del cálculo, den resultados computacionalmente diferente.
Por ejemplo: 5.4 – 3.1 es matemáticamente igual a 308.93 – 1006.80 + 700.17. Si el resultado de esas
operaciones se almacenan respectivamente en variables R1 y R2 de tipo real, es muy probable que al
hacer la comparación R1 = R2 dé el valor falso.
Se llama épsilon de la máquina al valor del intervalo entre la representación de 1 y el menor número
mayor que 1 (pero distinto de 1). Los valores que disten de 1 la mitad de épsilon o menos tendrán igual
representación que el 1 y aunque sean matemáticamente distintos, se verán iguales.
Para evitar errores en las comparaciones por problemas de redondeo se debe utilizar un valor pequeño
DELTA positivo (fijado por la precisión del problema o el épsilon de la máquina) haciendo:
Hacer
En lugar de:
Abs (R1 –R2) <= DELTA
R1 = R2
Abs (R1 –R2) > DELTA
R1 <> R2
R1 > R2 + DELTA
R1 > R2
R1 < R2 – DELTA
R1 < R2
R1 > R2 – DELTA
R1 >= R2
R1 < R2 + DELTA
R1 <= R2
Gráfico
Rango de reales con
igual representación
interna que R2
Menores
R2-DELTA
R2
R2+DELTA
Mayores
Estructuras de control
Las estructuras de control son construcciones lógicas que dirigen el flujo de acciones que efectuará el
procesador sobre los datos que maneja un programa.
Para que un programa funcione de manera que satisfaga plenamente los requisitos para los cuales fue
creado, debe permitir:
o
o
o
Ejecutar una serie de acciones o instrucciones.
Poder repetir un conjunto de acciones o instrucciones, según se satisfaga o no una
condición.
El empleo de acciones alternativas para poder elegir entre una acción o un grupo de
acciones cuando la situación así lo requiera.
Para poder dar cabida a estos requisitos, existen estructuras de control perfectamente definidas:
o Secuenciación: ejecutar las acciones indicadas, una después de otra.
o Selección: Evaluar cierta condición lógica, si el resultado es verdadero ejecutar la acción 1,
en otro caso ejecutar la acción 2.
o Iteración condicional: Evaluar cierta condición lógica, cuando el resultado es verdadero,
ejecutar una acción y continuar así mientras la condición siga siendo verdadera.
En este sentido, en mayo de 1966, Böhm y Jacopini demostraron que un programa puede ser escrito
utilizando solamente estos tres tipos de estructuras de control.
Secuencia
Cuando se deben ejecutar sucesivamente distintas acciones, se escribirá la lista de dichas acciones en
el orden en el que deban ser ejecutadas.
Pseudocódigo
E1
E2
E3
Diagrama de flujo
E1
E2
Diagrama N-S
E1
E2
E3
E3
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 18
Algorítmica y Programación I
Selección
Construcción que evalúa una expresión, y en función de los valores de ésta, dirige el flujo del proceso.
Pseudocódigo
Si
C
entonces
E1
Sino
Diagrama de flujo
V
Diagrama N-S
F
C
¿C?
Si
E2
E1
Fin Si
E2
No
E1
E2
Iteración condicional
Construcción que indica la repetición de un grupo de enunciados de acuerdo al valor de determinada
condición.
Pseudocódigo
Mientras C hacer
E1
Fin Mientras
Diagrama de flujo
F
Diagrama N-S
Mientras C
C
V
E1
E1
Primitivas de control o composición
Acción elemental: ejecución de un acontecimiento elemental. Considerando que en última instancia es
una computadora la máquina que ejecutará las instrucciones de un programa, las acciones elementales
vendrán determinadas por las tareas que puede realizar una computadora. Básicamente estas tareas
son:
o
realizar una serie de operaciones (aritméticas o lógicas) sobre unos operandos.
o
posibilidad de obtener dichos operandos a través de un dispositivo de entrada o de la memoria
de la computadora.
posibilidad de almacenar los resultados de dichas operaciones en memoria o enviarlos hacia un
dispositivo de salida.
o
Instrucción de Asignación: Operación mediante la cual se da valor a una variable. Utilizaremos el
símbolo <- para indicar que la variable del primer miembro toma el valor de la expresión del segundo
miembro. Algunos lenguajes de programación adoptan el símbolo := y otros el símbolo =.
Sintaxis: <var> <- <expresión>
Ejemplo: Cant <- 0
Instrucción de entrada de datos: Operación mediante la cual se lee un dato de la entrada estándar y
se lo asigna a una variable. Hay una asignación implícita. La variable debe ser de un tipo simple o
cadena. No puede ser de tipo enumerado creado por el usuario ni de otro tipo compuesto.
Sintaxis: Ingresar(<var>)
Ejemplo: Ingresar(cant)
Instrucción de salida: Operación mediante la cual se muestran datos en la salida estándar. La
expresión debe ser de un tipo simple o cadena. No puede ser de tipo enumerado creado por el usuario.
Sintaxis: Mostrar(<expresión> [,<expresión>])
Ejemplo: Mostrar(“Mes =”, mes)
Selección simple o composición condicional: sentencia que evalúa una condición, y si es cierta,
realiza la acción propuesta. Si la condición es falsa, dicha acción no es realizada.
¿C?
V
acción
Diagrama de flujo
Cond
F
Si
acción
No
seguir
Diagrama NS
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 19
Algorítmica y Programación I
Sintaxis:
Si (<condición>) entonces
<acción>
Fin Si
Ejemplo:
Si ( a < b) entonces
Dif <- b – a
Fin Si
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 20
Algorítmica y Programación I
Selección dicotómica o composición alternativa: sentencia que evalúa una condición, y en función de
ella realiza una u otra acción: si es la condición es verdadera, se realiza accionV y si es falsa la accionF.
Sintaxis:
Si (<condición>) entonces
<accionV>
Sino
<accionF>
Fin Si
Ejemplo:
Si ( a < b) entonces
Dif <- b - a
Sino
Dif <- a - b
Fin Si
V
F
Cond
accionV
accionF
Diagrama de flujo
¿C?
Si
No
AccionV AccionF
Diagrama NS
Selección múltiple o composición selectiva: enunciado formado por una expresión discreta (entera,
carácter, enumerado), cuyo valor pertenece a un conjunto de opciones, y según el cual se ejecuta uno
sólo de los enunciados alternativos.
Sintaxis:
Según <expr> hacer
expr
<valor1> : <accion1>
v1 v2
... vN otro
<valor2> : <accion2>
Diagrama de flujo
...
<valorN> : <accionN> Accion1
Accion2
[Sino <accionD>]
AccionD
Fin Según
Accion2
Ejemplo:
Según mes hacer
expr
1 : Mostrar (“enero”)
2 : Mostrar (“febrero”)
Valor1 ... ValorN
Otro
...
Acción1 ... acciónN acciónD
12 : Mostrar (“diciembre”)
Diagrama NS
Sino Mostrar(“incorrecto”)
Fin Según
Esta estructura se introduce para expresar de una forma cómoda las diversas condiciones y su relación
con cada una de las acciones. La ejecución de esta primitiva de control consiste en la ejecución de
aquella acción cuyo valor asociado coincida con el valor observado del indicador que aparece en la
primitiva. Para que esté bien construida esta estructura debe ejecutar una y sólo alternativa de entre
todas las que se enumeran.
Si ( mes = 1) entonces
Observaciones:
mostrar (“enero”)
o Se requiere que el indicador tenga, en el
Sino
momento de su evaluación, un valor del
Si ( mes = 2) entonces
conjunto enumerado en la composición.
mostrar (“febrero”)
o Opcionalmente, se puede indicar una acción
Sino
por defecto que se realizará en el caso de que
Si (mes = ...)
el indicador tome un valor distinto de los N
entonces
valores explícitamente enumerados en la
...
composición.
Sino
...
Esta estructura de decisión puede ser reemplazada
Fin
si
por estructuras de selección dicotómica anidadas.
Fin si
Fin si
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 21
Algorítmica y Programación I
Notar que la comparación que se realiza entre la expresión (mes) y el valor constante es por igual.
Estructuras de repetición
Se distinguen dos tipos: basadas en condición y basadas en contador. Las basadas en condición pueden
ser con pretesteo: la condición se verifica antes de realizar la acción, como en la Iteración mientras, o
con postesteo donde se realiza la acción y luego se verifica la condición, como en el caso de la iteración
Repetir-hasta.
Las iteraciones basadas en contador, permiten repetir acciones un número finito de veces, tal es el caso
del “Repetir X veces” o la iteración Desde.
Iteración mientras: La condición es evaluada antes de ejecutar por primera vez la acción y ésta se
ejecuta mientras la condición sea verdadera. Si el valor lógico de la condición es inicialmente falso, la
acción no se ejecutará.
Si la condición es siempre cierta, se produce un ciclo infinito y el algoritmo no finalizaría. Alguna de las
variables que intervienen en la condición debieran ser modificadas durante la ejecución de las acciones
encerradas en el mientras, para dar la posibilidad que alguna vez la condición se haga falsa y el bucle
finalice.
Sintaxis:
Mientras <condición> hacer
<acción>
Fin Mientras
F
Mientras C
Cond
V
acción
acción
Ejemplo:
Mientras cant > 0 hacer
cant <- cant -1
Fin Mientras
Diagrama de flujo
Diagrama NS
Cuidado con los ciclos infinitos!!!!
Si en el ejemplo precedente, cant partiera de un valor negativo, la condición sería falsa y nunca entraría
al bucle; si parte con valor positivo, la condición será cierta y en cada iteración disminuye su valor por lo
que en un tiempo finito dejará de ser positiva, y el bucle finaliza. Pero si la acción a realizar en lugar de
disminuir el valor, lo aumentara, si cant fue inicialmente positiva, se produciría un ciclo infinito y el
programa nunca terminaría, dando la apariencia que la máquina está “bloqueada”.
Para evitar ciclos infinitos, es necesario pero no suficiente que en las acciones del cuerpo del bucle se
modifique alguno de los elementos que compone la condición, pues si no ocurriera, y la condición fue
inicialmente cierta, seguiría siéndolo indefinidamente.
Iteración Repetir-hasta: La acción se realiza al menos una vez y luego se evalúa la condición, si ésta
es falsa, se vuelve a repetir la acción, sino se prosigue con la sentencia siguiente.
Si la condición es siempre falsa, se produce un ciclo infinito y el algoritmo no finalizaría. Alguna de las
variables que intervienen en la condición debe ser modificada durante la ejecución de las acciones
encerradas en el repetir, para dar la posibilidad que alguna vez la condición se haga cierta y el bucle
finalice.
Sintaxis:
Repetir
<acción>
hasta <condición>
Ejemplo:
Repetir
Ingresar (valor)
hasta valor > 0 y valor < 100
Cuidado con los ciclos infinitos!!!!
Acción
acción
F
Repetir hasta <cond>
Cond
Diagrama NS
V Diagrama de flujo
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 22
Algorítmica y Programación I
Si en el ejemplo precedente, se ingresa un valor entre los límites 0 y 100, la condición se hace cierta y la
iteración finaliza; si el valor no cumple con la condición, se vuelve a ingresar un nuevo valor y
nuevamente será evaluado y es de esperar que en un tiempo finito se cumplirá la condición y el bucle
finaliza. Pero si la acción a realizar en lugar de ingresar un valor, lo mostrara, si inicialmente este valor
no cumple la condición, se produciría un ciclo infinito y el programa nunca terminaría, dando la
apariencia que la máquina está “bloqueada”.
Para evitar ciclos infinitos, es necesario pero no suficiente que en las acciones del cuerpo del bucle se
modifique alguno de los elementos que compone la condición, pues si no ocurriera, y la condición fue
inicialmente falsa, seguiría siéndolo indefinidamente.
Repetir un número fijo de veces: En este caso, la estructura maneja un contador interno y la acción se
realiza la cantidad de veces establecida.
Sintaxis:
Repetir <valorEnteroPositivo> veces
<acción>
Fin repetir
Ejemplo:
Repetir 5 veces
mostrar(“Muestra 5 veces lo mismo”)
Fin repetir
Iteración Desde: En este caso, la estructura maneja un contador y tiene como parámetros el valor inicial
con que se parte, el valor final y el incremento a realizar en cada paso.
Sintaxis:
Desde <var> <- <valor_inicial> hasta <valor_final>
[con paso = <valor_paso>] hacer
<acción>
Fin Desde
Ejemplo: Muestra cada carácter del nombre y luego lo vuelve hacer en sentido inverso.
Desde i <- 1 hasta long(nombre) hacer
Mostrar (nombre[i])
Fin Desde
Desde i <- long(nombre) hasta 1 con paso = -1 hacer
Mostrar (nombre[i])
Fin Desde
El número de iteraciones a realizar está dado por las veces que es posible incrementar a la variable que
oficia de contador, en el valor del paso sin que supere el valor final, habiendo partido del valor inicial.
Existe la posibilidad de realizar 0 iteraciones, cuando el valor inicial es
mayor que el final y el paso es positivo. Cuando no se indica paso, el
Var <- valInicial
incremento es 1.
Funciona de la siguiente manera:
1. se inicializa la variable con el valor inicial
2. se compara el valor de la variable con el valor final. Si el paso
es positivo, la comparación es por menor o igual, y si es
negativo, se compara por mayor o igual.
F
Var <= valFinal
V
acción
Si el resultado de la comparación da falso,
3. se finaliza el bucle.
Si da verdadero,
4. se ejecutan las acciones del bucle.
5. se incrementa la variable en el valor del paso.
Var <- var + paso
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 23
Algorítmica y Programación I
6. se vuelve al paso 2
Acción con nombre: La notación algorítmica permite incorporar nuevas acciones y operaciones al
repertorio. Para ello debemos proporcionar:
Un nuevo nombre para la acción u operación.
Una especificación de la acción (qué hace).
El cuerpo de la acción, es decir, el conjunto de acciones más elementales que
constituyen la acción.
De esta forma la notación algorítmica permite agrupar un conjunto de acciones e identificarlas mediante
un nombre, de manera que la aparición de dicho nombre en un algoritmo provoca la ejecución de todas
las acciones contenidas en el cuerpo de la acción con nombre. Este tipo de acciones se estudiará en
detalle cuando veamos Funciones y procedimientos.
o
o
o
Combinación de estructuras de control
Es posible combinar las estructuras de control de secuenciación, selección e iteración condicional,
utilizando para ello la secuenciación, la selección y la iteración condicional.
Esto dio origen al concepto de programación estructurada. Un programa estará bien construido si está
formado por estructuras de control válidas, de acuerdo con la regla recursiva:
Base: la secuenciación, la selección y la iteración condicional son estructuras válidas de control que
pueden ser consideradas como enunciados primitivos.
Regla: las estructuras de control que se pueden formar combinando de manera válida la secuenciación,
selección e iteración condicional también serán válidas.
Ejemplos:
Decisiones anidadas
Si ( a < b) entonces
Dif <- b – a
Sino
Si ( a < c) entonces
Dif <- c – a
Sino
Dif <- c – b
Fin Si
Fin Si
Iteraciones anidadas
Mientras ( a < b) hacer
b <- b – abs(a)
Repetir
c <- c + b
mostrar(“c”, c )
hasta c > b
Fin Mientras
Iteraciones anidadas
Desde i <- 1 hasta 10 hacer
Desde j <- 1 hasta 10 hacer
Mostrar(“i=”, i ,“j=”, j)
Fin Desde
Mostrar (“Fin desde j”)
Fin Desde
Mostrar (“Fin desde i”)
Combinación de secuencia, decisión e iteración
Mientras ( a < b) hacer
Si ( a < c) entonces
Si ( a < 0) entonces
b <- c – a
Sino
a <- a – 1
Fin Si
Sino
a <- c – b
b <- b - 1
Fin Si
Fin Mientras
Especificación de algoritmos
Especificación de un algoritmo, proceso o acción es la definición precisa de su efecto. Una posibilidad
para especificar el significado de un objeto (proceso, algoritmo o acción) sería utilizar el lenguaje natural,
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 24
Algorítmica y Programación I
pero las definiciones de este tipo suelen incorporar ambigüedades. Cuando se busca precisión es
preferible utilizar especificaciones formales, para lo cual existen múltiples métodos, uno de ellos es el de
la semántica axiomática.
Hemos visto que la ejecución de una acción (no trivial) supone un cambio en el estado del sistema. Se
puede especificar un proceso describiendo la relación entre el estado inicial y el estado final del mismo.
La semántica axiomática consiste en formalizar esta relación mediante el uso de predicados.
Un predicado es una proposición lógica, es un enunciado cuyo valor es verdadero o es falso.
Precondición: Es el predicado o condición que debe ser cierta antes de ejecutar una acción.
Poscondición: Es el predicado o condición que debe ser cierta después de ejecutar una acción.
En la precondición se expresan las condiciones que deben cumplir los datos de la acción para garantizar
un funcionamiento correcto de la misma, y la poscondición expresa la relación que debe existir entre los
datos y los resultados de la acción.
Informalmente, el significado de que una acción S cumple la especificación dada por la precondición P y
la poscondición Q, es el siguiente: si los datos de la acción S cumplen la precondición P, la acción ha de
calcular unos resultados que cumplan la poscondición Q.
Observación: Si no se cumple la precondición no se puede garantizar nada acerca de los resultados de
la acción. Además, este hecho no influye en la corrección de la acción. Ejemplo: la especificación de
una computadora indica que el equipo funciona correctamente en un rango de temperaturas
comprendido entre 0 ºC y 40 ºC. Si en algún momento el equipo se pone a funcionar fuera de este rango
de temperaturas no se puede garantizar nada sobre si el mismo funciona o no correctamente. Además,
bajo estas circunstancias, si el equipo no funciona correctamente no podremos responsabilizar al
fabricante del mal funcionamiento del mismo.
Un algoritmo S satisface una especificación con precondición P y poscondición Q, denotada {P}S{Q}, si
al ser ejecutado sobre unas variables que cumplen P, el algoritmo termina, y lo hace de forma que dichas
variables cumplen Q.
Ejemplo 1: La especificación:
lado, sup: real
{ lado = L  L > 0}
superficie cuadrado
{ sup = L2}
{P}
S
{Q}
puede interpretarse como que dada una variable lado que tiene un valor L positivo, al realizar el
algoritmo o acción denominado “superficie cuadrado” se obtiene una variable sup que es igual al
cuadrado de L.
Ejemplo 2: Dadas las siguientes especificaciones,
x, y: entero
{x=X y=Y}
intercambiar
{x=Y  y=X}
{P}
S
{Q}
puede interpretarse como que dada una variable x que tiene un valor X y una variable y con valor Y, al
realizar el algoritmo o acción denominado “intercambiar” la variable x queda con el valor Y y la variable y
queda con el valor X.
Ejemplo 3: La especificación de un algoritmo que determinase si un entero es potencia de 2 podría ser
la siguiente:
x: entero
es_potencia: booleano
{x=X}
{P}
potencia?
S
{ x = X  es_potencia =  n X = 2n }
{Q}
puede interpretarse como que dada una variable x que tiene un valor X al realizar el algoritmo “potencia“
la variable x queda con el mismo X y la variable es_potencia toma el valor de la existencia o no de un
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 25
Algorítmica y Programación I
número n>=1 tal que X=2n. En esta especificación aparece tanto en la precondición como en la
poscondición que x = X con lo que se quiere indicar que el valor de la variable x es el mismo al principio
y al final del proceso.
Observar que en todos los casos que requerimos denotar que una variable tiene un valor, lo hacemos
expresándolo a través de un valor simbólico.
¿Cuál es la diferencia entre la especificación de un algoritmo y un algoritmo? Un algoritmo
describe cómo solucionar un problema, ya que el algoritmo no es más que la expresión (en una notación
algorítmica) de una sucesión de acciones (no ambiguas, predecibles y que se realizan en un tiempo
finito) para resolverlo. Por el contrario, la especificación de un algoritmo (o en general, de una acción)
describe qué hace el algoritmo (o acción). Esta descripción se realiza indicando la relación existente
entre los datos de la acción (o situación inicial previa a la realización de la acción) y los resultados de la
misma (o situación final alcanzada tras la realización de la acción). Es importante resaltar que podemos
utilizar un predicado para describir la situación (inicial y final) de un algoritmo porque podemos ver al
predicado como un conjunto de estados (situación), y viceversa.
Metodología de resolución de problemas
Una metodología de resolución de problemas es el diseño TOP DOWN, como medio para desarrollar
simultáneamente algoritmos y estructuras de datos. Ambos comienzan como "cajas negras". Cada caja
negra especifica qué hace pero no cómo lo hace. A medida que el proceso de resolución avanza, las
cajas negras son refinadas gradualmente, hasta que sus acciones no se pueden seguir
descomponiendo. El nivel de descomposición al que se llega depende de los conocimientos de quien va
a implementar la solución (obviamente, el nivel de detalle al que puede arribar un experto no es el mismo
que al que llegaría un novato).
Esta metodología, también conocida como diseño descendente, consiste en establecer una serie de
niveles de mayor a menor complejidad (arriba-abajo) que den solución al problema y en efectuar una
relación entre las etapas de la estructuración, de forma que una etapa jerárquica y su inmediato inferior
se relacionen mediante entradas y salidas de información. Es decir que con esta metodología, resolver el
problema original se reduce a resolver una serie de problemas más simples o subproblemas. En cada
paso del proceso de resolución cada subproblema es refinado, hasta llegar a un punto en el que está
compuesto de acciones tan simples que ya no tiene sentido seguir refinando. Podemos decir que en el
diseño Top Down vamos de lo general a lo particular. Una regla muy importante en este proceso de
resolución de problemas es que ninguna caja negra necesita "saber" cómo otra realiza su tarea, sólo
conoce cuál es la tarea.
La utilización de la técnica de diseño Top-Down tiene los siguientes objetivos básicos:
o
Simplificación del problema y de los subprogramas de cada descomposición.
o
Las diferentes partes del problema pueden ser programadas de modo independiente e incluso
por diferentes personas. También permite modificar una parte de la solución sin afectar en forma
significativa a las otras partes.
o
El programa final queda estructurado en forma de bloque o módulos lo que hace más sencilla su
lectura y mantenimiento.
En contraposición a esta metodología Top Down está la BOTTOM UP. En ésta, vamos de lo particular a
lo general. Es decir que se comienza por lo que serían las ramas terminales del árbol y se van juntando
las componentes hasta que se llega a obtener la solución del problema original.
Gráficamente, podemos verlo como un árbol de la siguiente manera:
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 26
Algorítmica y Programación I
problema
Bottom Up
Top Down
De los
subproblemas
al problema
Del problema a
los
subproblemas
subproblemas
Cuando nos enfrentamos a la resolución de un problema, en general encontramos que no es posible
para la mente humana atender todo a la vez. La modularidad es una solución a esto.
Qué ventaja provee la modularización? A medida que la complejidad de un problema crece, las tareas
de resolución también crecen. La modularización es un medio para disminuir la velocidad de crecimiento
de la dificultad.
Un primer programa
Se desea un programa para calcular el monto bruto, el monto del impuesto y el monto a pagar por la
compra de cierta cantidad de unidades de un mismo tipo de producto cuyo costo por unidad es conocido.
La compra está sujeta a un impuesto del 18%.
Análisis del problema
De la lectura del problema encontramos los siguientes datos de entrada y datos de salida:
Datos de entrada
Número de unidades adquiridas: unidades
Costo unitario del producto: costoUnitario
Impuesto: 18%
unidades
Datos de salida
Monto bruto: montoBruto
Monto del impuesto: montoImpuesto
Monto a pagar: montoPago
montoBruto
costoUnitario
montoImpuesto
impuesto = 18
montoPago
Restricciones
Unidades y costo unitario deben ser positivos
Observar que el número de unidades adquiridas y el costo unitario del producto son dependientes de
cada compra mientras que el porcentaje del impuesto ya tiene valor preestablecido, por lo tanto este
último puede tratarse como una constante que no requiere ser ingresada.
Diseño del algoritmo
Primer diseño descendente
En un primer momento el problema puede descomponerse de manera bastante general en obtener datos
de entrada, calcular montos y mostrar resultados.
Inicio
1. ingresar unidades y costoUnitario válidos
2. cálculo de montos
3. mostrar montoBruto, montoImpuesto, montoPago
Fin
Primer refinamiento
Como las unidades y el costo unitario no pueden ser valores negativos, debiéramos controlar esto antes
de calcular los montos. Es decir, debemos asegurarnos que los valores ingresados sean positivos, caso
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 27
Algorítmica y Programación I
contrario, no tendría sentido el problema. Esto se puede hacer con una estructura de repetición que siga
pidiendo los datos hasta que sean correctos.
Luego de un refinamiento en el subproblema Cálculo de montos llegamos al siguiente diseño:
Inicio
1. Repetir
ingresar unidades y costoUnitario
hasta unidades > 0 y costoUnitario > 0
2. Cálculo de montos
a. Calcular montoBruto
b. Calcular montoImpuesto
c. Calcular montoPago
3. mostrar montoBruto, montoImpuesto, montoPago
Fin
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 28
Algorítmica y Programación I
Segundo refinamiento
Finalmente llegamos al algoritmo definitivo:
Algoritmo Facturacion
Const
Impuesto = 18
Var
unidades: entero
costoUnitario, montoBruto, montoImpuesto, montoPago : puntoFijo(2)
Inicio
// Ingreso de datos válidos
Repetir
Mostrar (“Ingrese unidades y costo unitario”)
Ingresar (unidades, costoUnitario)
hasta unidades > 0 y costoUnitario > 0
// Cálculo de montos
montoBruto <- unidades * costoUnitario
montoImpuesto <- montoBruto * Impuesto / 100
montoPago <- montoBruto + montoImpuesto
// Salida de resultados
Mostrar (montoBruto, montoImpuesto, montoPago)
Fin
Otro programa: variante del problema anterior
Se desea un programa que lleve la cuenta de la cantidad de compras realizadas y la sumatoria de
montos brutos al final del día, además de calcular e informar para cada compra el monto bruto, el monto
del impuesto y el monto a pagar por la compra de cierta cantidad de unidades de un mismo tipo de
producto cuyo costo por unidad es conocido. La compra está sujeta a un impuesto del 18%.
Análisis del problema
Datos de entrada
Número de unidades adquiridas: unidades
Costo unitario del producto: costoUnitario
Impuesto: 18%
Datos de salida
Monto bruto: montoBruto
Monto del impuesto: montoImpuesto
Monto a pagar: montoPago
Cantidad de compras: cantCompras
MontoTotal: montoBrutoTotal
montoBruto
unidades
montoImpuesto
costoUnitario
montoPago
impuesto = 18
montoBrutoTotal
cantCompras
El número de unidades adquiridas y el costo unitario del producto son dependientes de cada compra
mientras que el porcentaje del impuesto ya tiene valor preestablecido, por lo tanto este último puede
tratarse como una constante que no requiere ser ingresada.
Diseño del algoritmo
Primer diseño descendente
En un primer momento el problema puede descomponerse de manera bastante general en repetir el
proceso de obtener datos de entrada, calcular montos y mostrar resultados de cada compra e ir
contando las compras realizadas y acumulando los montos brutos hasta finalizar el día. Luego de este
proceso repetitivo, nos queda como tarea, mostrar la cantidad y el monto bruto total.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 29
Algorítmica y Programación I
Inicio
Repetir
ingresar unidades y costoUnitario válidos
Cálculo de montos y cantidad de compras
mostrar montoBruto, montoImpuesto, montoPago
hasta finalizar el día
mostrar cantidad y montoBrutoTotal
Fin
Primer refinamiento
Debemos plantearnos cómo interpretaremos que finaliza el día. Una manera podría ser que preguntemos
después de cada compra si se trata de la última y en ese caso, finalizamos la repetición. Esto requiere
que mostremos un mensaje con la pregunta al usuario, tomemos su respuesta y de acuerdo al valor de
ella decidir si continuamos o finalizamos.
Otra manera, podría ser que se ingrese un valor especial de cantidad de unidades que signifique que ya
no hay más compras. Para este caso, debiera existir un valor que no fuera válido para dato de entrada
del problema, como un valor negativo, por ejemplo -1. Podemos informarle al usuario que ingrese la
cantidad de unidades o –1 si ya no hay más compras. Si el valor es –1 no se debe hacer ningún cálculo y
se debe finalizar el proceso.
Para este ejemplo vamos a adoptar un criterio similar al primer caso, le indicaremos después de cada
compra que oprima “F” si quiere finalizar o cualquier tecla para continuar. Luego de un refinamiento
también en el subproblema Cálculo de montos y cantidad llegamos al siguiente diseño:
Inicio
Repetir
// ingreso de datos válidos
Repetir
ingresar unidades y costoUnitario
hasta unidades > 0 y costoUnitario > 0
//Cálculo de montos y cantidad
Calcular montoBruto
Calcular montoImpuesto
Calcular montoPago
Contar compra
Acumular montoBruto
mostrar (montoBruto, montoImpuesto, montoPago)
mostrar (“Oprima F para finalizar o cualquier tecla para terminar”)
ingresar (tecla)
hasta tecla = “F”
mostrar (cantidad, montoBrutoTotal)
Fin
Segundo refinamiento
Contar compra significa sumar 1 a la cantidad hasta el momento, para ello antes tuvo que ser inicializada
en cero. Acumular montoBruto significa sumarle el montoBruto recientemente calculado al
montoBrutoTotal hasta el momento, para ello antes tuvo que inicializarse en cero. Finalmente llegamos al
algoritmo definitivo:
Algoritmo FacturacionDiaria
Const
Impuesto = 18
Var
unidades, cantCompras: entero
costoUnitario, montoBruto, montoImpuesto, montoPago : puntoFijo(2)
tecla: caracter
Inicio
cantCompras <- 0
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 30
Algorítmica y Programación I
montoBrutoTotal <- 0
Repetir
// Ingreso de datos válidos con mensajes aclaratorios
Repetir
Mostrar (“Ingrese unidades y costo unitario”)
Ingresar (unidades, costoUnitario)
hasta unidades > 0 y costoUnitario > 0
// Cálculo de montos y unidades
montoBruto <- unidades * costoUnitario
montoImpuesto <- montoBruto * Impuesto / 100
montoPago <- montoBruto + montoImpuesto
cantCompras <- cantCompras + 1
montoBrutoTotal <- montoBrutoTotal + montoBruto
// Salida de resultados por compra
Mostrar (montoBruto, montoImpuesto, montoPago)
// Determinar si finaliza el dia o no
Mostrar(“Oprima F para finalizar o cualquier tecla para continuar”)
Ingresar (tecla)
hasta tecla = ‘F’
Mostrar (cantCompras, montoBrutoTotal)
Fin
Variables de uso particular
Contador: es una variable que se utiliza para contar. La variable cantCompras del ejemplo anterior es
un contador. Requiere que se la inicialice en cero y luego se utiliza dentro de una estructura de repetición
de la forma contador <- contador + valor_cte, donde valor_cte es un incremento o
decremento fijo.
Acumulador de suma: es una variable que se utiliza para acumular la suma de valores. La variable
montoBrutoTotal del ejemplo anterior es un acumulador. Requiere que se la inicialice en cero y luego
se utiliza dentro de una estructura de repetición de la forma acumulador <- acumulador + valor,
donde valor es un incremento o decremento variable. Toda vez que encontremos una fórmula en la
que interviene el símbolo de sumatoria,, utilizaremos un acumulador de suma.
Acumulador de producto: es una variable que se utiliza para acumular el producto de valores.
Requiere que se la inicialice en uno y luego se utiliza dentro de una estructura de repetición de la forma
acumulador <- acumulador * valor, donde valor es un factor variable. Toda vez que
encontremos una fórmula en la que interviene el símbolo de productoria, , utilizaremos un acumulador
de producto.
Bandera: es una variable que sirve para indicar un estado el cual será consultado para tomar o no una
decisión. Puede ser de tipo lógico si la consulta es por sí o no, o enumerado si el estado puede tomar
más de dos valores.
Prueba del algoritmo
La depuración es el proceso por el cual descubrimos las causas de los problemas y los resolvemos. Las
pruebas deben considerar los detalles de diseño así como en los requerimientos generales. Una manera
de probar si el algoritmo funciona o de encontrar errores, es realizando una traza, es decir ejecutando “a
mano”, usando datos representativos y anotando los valores que van tomando los objetos, acción tras
acción. Todo programa debe ejecutarse muchas veces y con diversas entradas con el fin de detectar
posibles errores.
Si probamos con todos los valores posibles que pueden tomar los datos de entrada y funciona
correctamente, podemos asegurar que el algoritmo es correcto. Si al probar la ejecución con un valor, el
algoritmo falla, debemos corregirlo y volver a probar.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 31
Algorítmica y Programación I
Ejemplo probar que el siguiente algoritmo almacena y muestra el menor de dos valores ingresados.
Cuando son iguales almacena uno de ellos.
Algoritmo MuestraMenor
Var
Nro1, nro2, menor : entero
Inicio
1 Mostrar (“Ingrese un par de números”)
2 Ingresar (nro1, nro2)
3 Si nro1 < nro2 entonces
4
menor <- nro1
Sino
5
menor <- nro2
Fin Si
6 Mostrar (menor)
Fin
Prueba 1: ingresa 10
Traza
Nro1 Nro2
1
2
10
12
3
10
12
4
10
12
6
10
12
y 12
menor
10
10
Prueba 2: ingresa 10
Traza
Nro1 Nro2
1
2
10
10
3
10
10
5
10
10
6
10
10
y 10
menor
10
10
Prueba 3: ingresa 25
Traza
Nro1 Nro2
1
2
25
15
3
25
15
5
25
15
6
25
15
y 15
menor
15
15
Validación de datos de entrada
Es de suma importancia que los datos de entrada sean válidos para el problema a resolver. Si deben
cumplir algún tipo de condición, esto debe ser controlado antes de ser usado en el cálculo. Es necesario
pedirlos una vez, y si son incorrectos volverlos a pedir. El esquema de validación puede ser realizado de
las siguientes formas:
Mostrar (“ingrese dato válido”)
Ingresar (dato)
Mientras dato no es válido hacer
Mostrar (“dato no válido, reingrese”)
Ingresar (dato)
Fin Mientras
Repetir
Mostrar (“ingrese dato válido”)
Ingresar (dato)
Hasta dato es válido
Las condiciones de validez dependerán de cada problema en cuestión y el mensaje a mostrar deberá
orientar al usuario para que sepa cuáles son los valores esperados. Hay que tener en cuenta que a
pesar que se indique con un mensaje que el dato a ingresar debe cumplir ciertas condiciones, puede ser
que se ingrese un dato que no las cumpla, por eso la estructura de repetición se encarga de reingresarlo
hasta que sea correcto.
El primer esquema permite un mensaje especial para cuando es inválido. El segundo esquema no
distingue el primer ingreso (obligatorio) de los que se realizan por no cumplir con la validación.
Cómo finalizar la entrada de un conjunto de datos
El criterio para finalizar el ingreso de datos lo establece el programador, en función del modelo de
solución que esté programando. En general se presentan alguna de las siguientes situaciones:
o Se conoce la cantidad de datos a ingresar en tiempo de diseño del algoritmo.
o Se conoce la cantidad de datos a ingresar en tiempo de ejecución del algoritmo.
o El usuario informa la finalización a través de un valor especial de alguno de los datos de entrada.
o El usuario informa la finalización a través de contestar a una pregunta expresa, por ejemplo
contesta ‘N’ a la pregunta “Desea continuar?”
o Se cumple una cierta condición con un valor calculado del problema.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 32
Algorítmica y Programación I
Si se conoce la cantidad de datos a ingresar en tiempo de diseño del algoritmo, el esquema de
solución a este proceso repetitivo es utilizar una estructura de repetición del tipo Desde 1 hasta la
cantidad de veces (indicada como una constante con nombre).
Ejemplo: Mostrar la suma de cien números enteros ingresados por el usuario
Algoritmo CienVeces
Const
Datos de entrada:
Tope=100
números a sumar: nro
Var
i, nro, suma: entero
cantidad de números: 100
Inicio
Datos de salida: suma
Suma<-0
Desde i<-1 hasta Tope hacer
Ingresar (nro)
suma <- suma + nro
Fin Desde
Mostrar ( suma)
Fin
Si se conoce la cantidad de datos a ingresar en tiempo de ejecución del algoritmo, el esquema de
solución a este proceso repetitivo es utilizar una estructura de repetición del tipo Desde 1 hasta la
cantidad de veces (previamente ingresada).
Ejemplo: Mostrar la suma de una cierta cantidad, dada por el usuario, de números enteros ingresados
por el usuario.
Datos de entrada:
cantidad de números a
ingresar: tope
números a sumar: nro
Datos de salida: suma
Tener en cuenta que el tope
de números a ingresar es
conocido por el usuario, por
lo tanto primero debemos
solicitar
este
valor
y
debemos validar que sea
positivo.
Algoritmo XVeces
Var
i, nro, suma, tope: entero
Inicio
suma<-0
Repetir
Mostrar (“Ingrese cantidad de números a sumar”)
Ingresar (tope)
Hasta Tope > 0
Desde i<-1 hasta tope hacer
Ingresar (nro)
suma <- suma + nro
Fin Desde
Mostrar (suma)
Fin
Si el usuario informa la finalización a través de un valor especial de alguno de los datos de
entrada, debe existir un valor que no pueda ser utilizado como válido para el cálculo y entonces ser
utilizado como indicación de que ya no hay más datos. Esta solución es aplicable si no se conoce la
cantidad de datos y hay un dato que puede tomar un valor que sirva para indicar la finalización. Tenemos
que tener presente que una variable puede tener valores de un solo tipo, si el dato a ingresar fuera un
número, no podemos esperar que ingrese un carácter para indicar la finalización. Si el número debiera
ser positivo, podríamos elegir un valor negativo como –1 para indicar la finalización. En cambio, si todos
los valores fueran aceptables para el problema en cuestión, no habría forma de aplicar este esquema.
El esquema de solución consiste en una estructura de repetición en la que se solicita el dato y se lo
valida (puede tomar valores de los casos válidos del problema o el valor adoptado para finalización) Si
no es el caso de finalización, realizar las acciones correspondientes a la solución del problema. La
estructura de repetición finaliza cuando el dato toma el valor elegido para finalización.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 33
Algorítmica y Programación I
Ejemplo: Mostrar la suma de una cierta cantidad de números enteros ingresados por el usuario. Cuando
el usuario ingresa el valor cero, finalizar el proceso y mostrar la suma.
Datos de entrada:
números a sumar: nro
valor de nro para terminar = 0
Datos de salida: suma
Se puede utilizar una estructura Repetir o una estructura Mientras. Esta última solución resulta más
eficiente que la anterior y es la que utilizaremos para esta situación.
Algoritmo Veces0r
Var
nro, suma: entero
Inicio
suma<-0
Repetir
Ingresar (nro)
Si nro <> 0 entonces
suma <- suma + nro
Fin Si
Hasta nro = 0
Mostrar ( suma)
Fin
Algoritmo Veces0m
Var
nro, suma: entero
Inicio
suma<-0
Ingresar (nro)
Mientras nro <> 0 hacer
suma <- suma + nro
Ingresar (nro)
Fin Mientras
Mostrar ( suma)
Fin
Si el usuario informa la finalización a través de contestar a una pregunta expresa, por ejemplo
contesta ‘N’ a la pregunta “Desea continuar?”, se realiza un proceso repetitivo de ingresar el dato y
procesarlo, mostrar la pregunta y tomar la respuesta y validarla, hasta que la respuesta signifique No.
Ejemplo: Mostrar la suma de una cierta cantidad de números enteros ingresados por el usuario hasta
que el usuario no desee continuar.
Datos de entrada:
números a sumar: nro
repuesta del usuario: resp
Datos de salida: suma
Algoritmo VecesNS
Var
nro, suma: entero
resp: caracter
Inicio
suma<-0
Repetir
Ingresar (nro)
suma <- suma + nro
Repetir
Mostrar (“Desea continuar? S/N”)
Ingresar (resp)
Hasta resp = ‘n’ o resp = ‘N’ o resp = ‘S’ o resp = ‘s’
Hasta resp = ‘n’ o resp = ‘N’
Mostrar ( suma)
Fin
Si se debe cumplir una cierta condición con un valor calculado del problema, el esquema de
solución consiste en una estructura de repetición del tipo repetir-hasta, en la que se ingresa el dato y se
lo procesa.
Ejemplo: Mostrar la suma de una cierta cantidad de números enteros ingresados por el usuario. El
proceso finaliza cuando la suma supera el valor 1000.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 34
Algorítmica y Programación I
Datos de entrada:
valor tope para la suma = 1000
números a sumar: nro
Datos de salida: suma
Algoritmo VecesCond
Const
ValorTope=1000
Var
nro, suma: entero
Inicio
suma<-0
Repetir
Ingresar (nro)
suma <- suma + nro
Hasta suma > ValorTope
Mostrar ( suma)
Fin
Descomposición de problema: Funciones y Procedimientos
En general un problema complejo puede ser resuelto de manera más fácil y eficiente si se divide en
problemas más pequeños. Esto implica que el gran problema original será resuelto por medio de varios
módulos, cada uno de los cuales se encarga de resolver un subproblema determinado. Esos módulos
pueden ser representados por “acciones con nombres” que están asociadas a un subalgoritmo con la
misión de resolver un subproblema en particular.
La notación algorítmica permite agrupar bajo un nombre (identificador de la acción) a un conjunto de
acciones. Este mecanismo permite ampliar el repertorio de acciones disponibles en la notación
algorítmica (hasta ahora, compuesto por acciones elementales (asignación, lectura y escritura) y por
primitivas de composición (secuencia, decisión y repetición)) mediante la definición de una acción con
nombre. La definición de una acción con nombre consiste en expresar convenientemente el subalgoritmo
que desempeña la tarea específica requerida a dicha acción (abstracción procedimental).
Una vez definida una acción con nombre, un algoritmo puede hacer uso de ésta mediante una llamada o
invocación a la misma. La llamada o invocación a una acción con nombre es una acción válida en la
notación algorítmica que se EXPRESA mediante el nombre asociado a la acción, y cuyo SIGNIFICADO
es la ejecución de las acciones encerradas en su definición.
Los subalgoritmos se escriben sólo una vez, luego es posible hacer referencia a ellos, "llamarlos" o
“invocarlos” a través del nombre desde diferentes puntos de un pseudocódigo. La principal ventaja es
que nos permite reutilización y evita la duplicación de código. Además, los subalgoritmos son
independientes entre sí, en el sentido de que se puede escribir y verificar cada módulo en forma
separada sin preocuparse por los demás módulos, simplemente asumiendo que los módulos que utiliza
hacen lo que tienen que hacer en forma correcta. Por ello, es menos complicado localizar un error y
también, cuando se necesita modificarlo por alguna razón (cambiaron las reglas de negocio, se prefiere
una solución más eficiente, etc.), la modificación y el testeo se concentra en ese módulo, sin tener que
variar o rehacer en todos los lugares donde se hubiera repetido ese código.
No existe un criterio fijo para determinar el tamaño, ni muy grandes ni muy pequeños, la idea
fundamental es que realicen una única cosa y muy concreta.
En todo algoritmo o programa existirá un módulo o programa principal que es al que se transfiere el
control cuando comienza la ejecución del programa, y luego desde él, se va llamando al resto de los
subprogramas. Un subprograma puede, a su vez, invocar o llamar a otros subprogramas, inclusive
puede llamarse a sí mismo (esto se conoce como recursividad y será visto más adelante). Cuando se
invoca a un subprograma, se transfiere el control al mismo, el cual se comienza a ejecutar desde el
principio, y al finalizar retorna el control al subprograma que lo llamó.
No existen limitaciones en cuanto a las acciones que pueda ejecutar un subalgoritmo, puede realizar las
mismas acciones que el algoritmo principal: aceptar datos, realizar cálculos, devolver resultados. Los
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 35
Algorítmica y Programación I
subalgoritmos pueden recibir valores del algoritmo que los invoca, trabajar con ellos y devolver un
resultado al algoritmo invocante.
Los módulos o subprogramas reciben diferentes nombres según el lenguaje de programación y según su
tipo. Se llaman procedimientos y funciones (Pascal, C), subrutinas (Basic, Fortran), secciones (Cobol).
Parámetros de una acción con nombre
Una acción con nombre al igual que cualquier otra acción, en general actuará sobre unos datos y así
habrá que expresarlo, tanto en su definición como en su invocación.
En la definición de una acción, los datos genéricos sobre los que debe actuar la misma se expresarán
mediante la lista de parámetros formales.
En la invocación de una acción, los datos concretos sobre los que actúa la acción se expresarán
mediante la lista de parámetros reales.
Los parámetros reales de una invocación se asocian con los parámetros formales de su definición de
acuerdo a un criterio posicional, es decir, el 1er parámetro formal se corresponde con el 1er parámetro
real, el 2º parámetro formal con el 2º real, y así sucesivamente. Este hecho obliga también a que el
número de parámetros coincidan y que el tipo de dato de cada asociación parámetro formal-real también
coincidan.
Según la relación de valores entre los parámetros formales y reales, se puede hablar de tres tipos de
parámetros: Entrada, Salida y Entrada/Salida.
Parámetros de Entrada. El parámetro real de la invocación SÍ tiene un valor definido, que además es el
valor que tomará inicialmente el correspondiente parámetro formal. Al finalizar la acción NO interesa que
las posibles modificaciones del parámetro formal se reflejen sobre el parámetro real asociado. En este
caso el parámetro deberá definirse como de entrada. Este tipo de parámetro se puede usar para aportar
un dato ya definido a un subalgoritmo para que éste lo utilice. Por ejemplo el tipo de parámetro de la
operación primitiva mostrar es de entrada.
Parámetros de Salida. El parámetro real de la invocación NO tiene un valor definido. Al finalizar la
acción SÍ interesa que las posibles modificaciones del parámetro formal se reflejen sobre el parámetro
real asociado. En este caso el parámetro deberá definirse como de salida. Este tipo de parámetro se
puede usar para que un subalgoritmo devuelva un dato definido en su interior. Por ejemplo el tipo de
parámetro de la operación primitiva ingresar es de salida.
Parámetros de Entrada/Salida. El parámetro real de la invocación SÍ tiene un valor definido, que
además es el valor que tomará inicialmente el correspondiente parámetro formal. Al finalizar la acción SÍ
interesa que las posibles modificaciones del parámetro formal se reflejen sobre el parámetro real
asociado. En este caso el parámetro deberá definirse como de entrada/salida.
Este tipo de parámetro se puede usar para que un subalgoritmo modifique un dato en su interior que ya
tuviera valor previamente.
E
Podemos ver al subalgoritmo como un módulo que realiza un
S
Cliente
Servidor
servicio, es un módulo “servidor”. Los módulos que lo invocan son
E/S
módulos “clientes”, son los que solicitan y utilizan el servicio. La
clasificación de los parámetros Entrada, Salida y Entrada/Salida es
vista desde el servidor según le llegue información (entrada), devuelva información (salida) o ambas
cosas (entrada-salida). La relación cliente-servidor puede verse como un contrato de servicio donde el
cliente deberá cumplir las precondiciones para dicho servicio y el servidor deberá garantizar que se
cumplan las poscondiciones.
En la lista de parámetros formales se aclarará si es un parámetro de entrada, salida o entrada-salida.
Cada lenguaje de programación tiene su forma de hacerlo, por ejemplo en Pascal se precede con la
palabra clave VAR a aquellos parámetros que pueden ser de salida o entrada-salida. En el lenguaje Ada
se utilizan las palabras in, out e in-out para ese fin. Nosotros utilizaremos E, S o E/S precediendo al
parámetro formal o flechas entrantes, salientes o de ambos sentidos para aclarar que es de entrada,
salida o entrada-salida.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 36
Algorítmica y Programación I
Los argumentos reales correspondientes a parámetros de entrada, deben ser valores constantes o
variables inicializadas (no serán modificadas), los correspondientes a entrada-salida deben ser variables
inicializadas y los de salida deben ser variables sin importar si está inicializada o no.
Tipos de acciones con nombre
Los subalgoritmos, también llamados subrutinas o subprogramas, pueden ser dos tipos: Funciones y
Procedimientos.
Funciones
Por Función se entenderá una acción con nombre similar al concepto de función matemática. La mayoría
de los lenguajes de programación permiten definir nuevas funciones y utilizarlas de forma similar a las
predefinidas. Se define como función a aquella acción con nombre que:
o
o
o
tenga 1 o más parámetros exclusivamente de entrada,
tenga 1 solo valor exclusivamente de salida
y tal que todos los parámetros de entrada sean necesarios y suficientes para determinar el valor
de salida
Un algoritmo de tipo función es un algoritmo auxiliar (subalgoritmo) que encapsula la forma en que se
resuelve el proceso para obtener un resultado, cuyo valor es retornado cuando la función es invocada
desde otro algoritmo. Al igual que en las predefinidas, posee un nombre y debe establecerse la cantidad
y tipo de parámetros que recibe y el tipo de su resultado. El nombre elegido debe ser significativo, indicar
qué hace.
La definición de una función es similar a la de cualquier otro algoritmo: Tiene una cabecera, una
declaración del ambiente y un cuerpo. La sintaxis de la cabecera es:
Funcion <nombre> ( lista de parámetros formales con sus respectivos tipos ): tipo del resultado.
Por ejemplo, la cabecera de la función predefinida seno(x) sería:
Funcion seno( x: real): real.
Los nombres elegidos para los parámetros formales deberán ser significativos para lo que están
representando dentro de la función. Es la manera de darle un nombre y poder referirse a los argumentos
con que será invocada. Por ejemplo, en y  seno(PI) , el parámetro formal x se refiere al argumento
actual PI.
En el ambiente se detallan las variables o constantes auxiliares que pudieran necesitarse, y en el cuerpo,
las sentencias necesarias para resolver el problema. Para poder indicar el valor que asume la función
cuando retorna, algunos lenguajes de programación como C y java, utilizan la sentencia return (valor del
resultado) y otros como Pascal, la asignación del valor del resultado sobre el nombre de la función:
<nombre>  valor del resultado. En este caso, el nombre de la función actúa como una variable en el
lado izquierdo de la asignación. Nosotros utilizaremos esta última forma.
La función debe devolver un valor sea cual sea el camino de ejecución seguido. No se permiten
funciones que no devuelvan nada. Cuando se utiliza la acción Return supone la finalización de la
ejecución de la función al alcanzar dicha sentencia, devolviendo el resultado de evaluar la expresión que
acompaña al return, mientras que cuando se utiliza la asignación, como utilizaremos nosotros, la función
finaliza cuando se llega al final.
La declaración de una función es equivalente a la definición de una operación: los operandos son los
parámetros de entrada y el resultado es el valor devuelto por la función. Por lo tanto una invocación a
una función es una expresión válida, y puede formar parte de expresiones más complejas. La llamada a
una función, siempre va a formar parte de una expresión, de cualquier expresión en la que en el punto
en la que se llama a la función, pudiera ir colocado cualquier valor del tipo de datos que devuelve la
función, esto se debe a que el valor que devuelve una función está asociado a su nombre.
Qué sucede cuando se invoca a una función?
1. Al hacer la llamada y ceder el control a la función, se asocia (asigna el valor) de cada parámetro
real a cada parámetro formal asociado, siempre por orden de aparición y de izquierda a derecha,
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 37
Algorítmica y Programación I
por lo que siempre que no coincidan los tipos y el número de parámetros formales y reales, se
produce un error.
2. Si todo ha ido bien, se ejecutan las acciones de la función hasta que finalice o lleguemos a una
de tipo retorno <valor> que pondrá fin a la ejecución (si el mecanismo es por sentencia retorno)
3. Se le asocia al nombre de la función el valor retornado y se devuelve el control al subprograma
que hizo la llamada pero sustituyendo el nombre de la función por el valor devuelto.
Ejemplos de definición y uso de función
Problema 1: Se desea diseñar una función para resolver la potencia entera de un número real.
De la lectura del problema encontramos los siguientes datos de entrada y datos de salida:
Datos de entrada
Número real del cual se quiere conocer su potencia: base
Número entero que representa la potencia a la que se
quiere elevar la base: exponente
Datos de salida
Resultado de la operación: potencia
base
exponente
potencia
Cliente
Potencia
La base y el exponente deberán ser comunicados desde el algoritmo que invoca mediante parámetros
de entrada y el resultado de la operación será el valor que deberá tomar la función. Debemos elegir un
nombre significativo para esta operación, y la llamaremos potencia.
La manera de resolver una potencia, es multiplicando la base por sí misma tantas veces como indica el
exponente. Por lo tanto utilizaremos una estructura de repetición y la más adecuada a este caso es el
Desde. Si el exponente es negativo, se realiza el procedimiento como si fuera positivo y luego se invierte
el resultado.
La función queda definida como:
Funcion potencia (E base: real,E exponente: entero): real
exponente
(* calcula la potencia entera base
precondición : base = B y exponente = E
poscondición: potencia = BE
*)
Var
resultado: real
i: entero
Inicio
resultado <- 1
Desde i <- 1 hasta abs(exponente) hacer
resultado <- resultado * base
Fin Desde
Si (exponente < 0 ) entonces
potencia <- 1 / resultado
Sino
potencia <- resultado
Fin Si
Fin Funcion
Valor final que
asume la función
Un algoritmo que la utiliza podría tener una sentencia como alguna de las siguientes:
Mostrar (potencia(3,2))
volCubo <- potencia(lado,3)
En la primera se muestra el cuadrado de tres, base se asocia con el valor 3 y exponente con el valor 2.
En la segunda, si lado es una variable real ya inicializada, base se asocia con el valor de lado y
exponente con el valor 3.
Problema 2: Se desea resolver el subproblema de obtener el factorial de un número.
De la lectura del problema encontramos los siguientes datos de entrada y datos de salida:
nro
factorial
Cliente
Factorial
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 38
Algorítmica y Programación I
Datos de entrada
Número del cual se quiere conocer el factorial: nro
Datos de salida
Resultado de la operación: factorial
Sabemos que el factorial no está definido para números negativos, por lo que impondremos como
precondición que nro sea mayor o igual que cero. El factorial de cero es uno y el factorial de un número n
es el producto de los primeros n números naturales, para ello requerimos de un acumulador de producto.
La generación y el producto de los n número naturales se puede realizar con una estructura de repetición
Desde
La función queda definida como:
Funcion factorial (E nro: entero): entero (* calcula el factorial de nro
precondición : nro = N y N >= 0
poscondición: factorial = N!
*)
Var
resultado: entero
i: entero
Inicio
resultado <- 1
Desde i <- 1 hasta nro hacer
resultado <- resultado * i
FinDesde
factorial <- resultado
FinFuncion
Observemos que si el cliente de esta función no respetara la precondición, la función devolvería como
resultado el valor 1, el cual es incorrecto. La garantía de que funcione correctamente es que se cumpla
con la precondición.
Ejemplo de utilización de esta función en otro algoritmo:
Algoritmo UsaFactorial
Var
nro: entero
Inicio
Repetir
Mostrar (“ingrese nro >=0 para calcular factorial y 0 para finalizar”)
Ingresar (nro)
Si nro > 0 entonces
Mostrar ( nro, “! = ”, factorial(nro))
Sino
Mostrar( “ingrese nuevamente, el nro no debe ser negativo”)
FinSi
Hasta nro = 0
Fin
Problema 3: Se desea resolver el subproblema de obtener el nombre del mes correspondiente a su
orden numérico.
De la lectura del problema encontramos los siguientes datos de entrada y datos de salida:
Datos de entrada
mes
Número de mes: mes
Datos de salida
nombreMes
Resultado de la operación: nombreMes
Cliente
nombreMes
El número de mes es un número comprendido entre 1 y 12. Una solución es imponer como precondición
que se cumpla esa condición y sólo preocuparnos por los meses correctos. Otra solución, es responder
“Mes incorrecto” cuando mes no cumple esa condición, en este último caso, no habría precondición para
el valor del mes.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 39
Algorítmica y Programación I
La función queda definida como:
Funcion nombreMes (E mes: entero): cadena
(* devuelve el nombre del mes
precondición : mes = M y M >= 1 y M <=12
poscondición: nombreMes {enero, febrero,...,diciembre} y mes>=1 y mes<=12)
nombreMes = “”
*)
*)
o
Inicio
nombreMes <- “”
Segun mes hacer
1: nombreMes <- “enero”
2: nombreMes <- “febrero”
3: nombreMes <- “marzo”
4: nombreMes <- “abril”
5: nombreMes <- “mayo”
6: nombreMes <- “junio”
7: nombreMes <- “julio”
8: nombreMes <- “agosto”
9: nombreMes <- “setiembre”
10: nombreMes <- “octubre”
11: nombreMes <- “noviembre”
12: nombreMes <- “diciembre”
FinSegun
FinFuncion
Recordemos que la función debe devolver un valor sea cual sea el camino de ejecución seguido, por eso
inicializamos a nombreMes al principio y lo redefinimos dentro de la estructura de decisión de acuerdo
con el mes en cuestión. Observemos que si el cliente de esta función no respetara la precondición, la
función devolvería como resultado el valor “”, el cual no representa un mes. La garantía de que funcione
correctamente es que se cumpla con la precondición. Una alternativa es la siguiente:
Funcion nombreMes (E mes: entero): cadena
(* devuelve el nombre del mes
precondición : mes = M
poscondición: (nombreMes {enero,...,diciembre} y mes>=1 y mes<=12)
= “mes incorrecto”
*)
o nombreMes
Inicio
Segun mes hacer
1: nombreMes <- “enero”
2: nombreMes <- “febrero”
3: nombreMes <- “marzo”
4: nombreMes <- “abril”
5: nombreMes <- “mayo”
6: nombreMes <- “junio”
7: nombreMes <- “julio”
8: nombreMes <- “agosto”
9: nombreMes <- “setiembre”
10: nombreMes <- “octubre”
11: nombreMes <- “noviembre”
12: nombreMes <- “diciembre”
Sino nombreMes = “mes incorrecto”
FinSegun
FinFuncion
Problema 4: Se desea resolver el subproblema de obtener un valor entero que se solicita que ingrese el
usuario y que cumpla con la condición de pertenecer a un rango determinado.
Datos de entrada
Rango de validación: tope1 y tope2
Msg
tope1
tope2
enteroEnRango
Cliente
enteroEnRango
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 40
Algorítmica y Programación I
Mensaje para el usuario: msg
Datos de salida
Resultado de la operación: enteroEnRango
La función queda definida como:
Funcion enteroEnRango (E msg: cadena, E tope1,tope2: entero): entero
(* devuelve un valor entero dentro de un rango
precondición : msg = M y tope1=T1 y tope2= T2 y T1 <=T2
poscondición: enteroEnRango  [T1,T2]
*)
var
nro: entero
Inicio
Repetir
Mostrar (msg )
Ingresar (nro)
hasta nro >= tope1 y nro <= tope2
enteroEnRango <- nro
FinFuncion
Problema 5: Se desea resolver el subproblema de conocer si el usuario confirma aceptar un mensaje.
Es decir, se muestra un mensaje al usuario y se le solicita una respuesta por sí o no, devolviendo
verdadero o falso, respectivamente.
msg
Datos de entrada: Mensaje que se solicita confirmación: msg
Datos de salida: Resultado de la operación: confirma
confirma
Cliente
confirma
La función queda definida como:
Funcion confirma (E msg: cadena): logico
(* devuelve un valor logico verdadero si confirma
precondición : msg = M
poscondición: confirma  {falso,verdadero}
*)
o falso, si no.
var
resp: caracter
Inicio
Repetir
Mostrar (msg, “S/N” )
Ingresar (resp)
hasta resp = ‘n’ o resp = ‘N’ o resp = ‘S’ o resp = ‘s’
confirma  resp = ‘s’ o resp = ‘S’
Fin Funcion
Procedimientos
Cualquier otra acción con nombre que no se corresponda con la definición de función se declarará como
un procedimiento. La mayoría de los lenguajes de programación permiten poder definir nuevas acciones
y utilizarlas de forma similar a las predefinidas. Un algoritmo de tipo procedimiento es un algoritmo
auxiliar (subalgoritmo) que encapsula la forma en que se resuelve una operación, proceso o acción. Las
operaciones mostrar e ingresar que hemos estado utilizando son procedimientos predefinidos. Al igual
que en las funciones el nombre elegido debiera ser significativo, indicar qué hace, y debe establecerse la
cantidad y tipo de parámetros que recibe. A diferencia de las funciones, no retornan un valor cuando son
invocados por lo tanto no se establece tipo de su resultado. Los procedimientos representan
sentencias mientras que las funciones representan valores.
La definición de un procedimiento es similar a la de cualquier otro algoritmo: Tiene una cabecera, una
declaración del ambiente y un cuerpo. La sintaxis de la cabecera es:
Procedimiento <nombre> ( lista de parámetros formales con sus respectivos tipos ).
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 41
Algorítmica y Programación I
Por ejemplo, la cabecera del procedimiento línea(x1, y1, x2, y2) que dibuja una línea desde el punto de
coordenadas (x1, y1) al punto (x2, y2) sería:
Procedimiento línea(E x1, y1, x2, y2: real)
Los nombres elegidos para los parámetros formales deberán ser significativos para lo que están
representando dentro del procedimiento. Es la manera de darle un nombre y poder referirse a los
argumentos con que será invocado. Por ejemplo, en línea(0.5,0.0,1.5,3.1), el parámetro formal x1 se
refiere al argumento actual 0.5, y1 a 0.0, x2 a 1.5 e y2 a 3.1
En el ambiente se detallan las variables o constantes auxiliares que pudieran necesitarse, y en el cuerpo,
las sentencias necesarias para resolver el problema. No hay sentencia especial ni valor de retorno, se
retorna cuando se llega a su fin.
Los parámetros pueden ser de entrada (sólo se utiliza su valor), entrada-salida (se utiliza su valor y su
dirección para poder modificarlo) o de salida (no interesa su valor inicial, se requiere su dirección para
poder asignarle valor).
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 42
Algorítmica y Programación I
Diferencias entre funciones y procedimientos:
1. Una función devuelve un único valor y un procedimiento puede devolver 0, 1 o N.
2. Ninguno de los resultados devueltos por el procedimiento se asocian a su nombre como ocurre
con la función.
3. Mientras que la llamada a una función forma siempre parte de una expresión, la llamada a un
procedimiento es una instrucción.
Qué sucede cuando se invoca a un procedimiento?
1. Se cede el control al procedimiento al que se llama y lo primero que se hace al cederle el control
es sustituir cada parámetro formal de la definición por el parámetro actual o real de la llamada
asociado a él. Esta asociación entre parámetros formales y reales se hace de izquierda a
derecha por orden de colocación y para que se pueda producir esta asociación tienen que existir
el mismo número de parámetros formales que reales, y además el tipo tiene que coincidir con el
del parámetro formal asociado, sino se cumple alguna de estas condiciones hay un error.
2. Si la asociación ha sido correcta comienzan a ejecutarse las instrucciones del procedimiento
hasta llegar a la última instrucción. Al finalizar se vuelven a asociar los parámetros formales que
devuelven los resultados a los parámetros reales asociados en la llamada, es decir, de esta
manera algunos de los parámetros reales de la llamada ya contendrán los resultados del
procedimiento.
3. Finalmente se cede el control a la siguiente instrucción a la que se hace la llamada, pero
teniendo en cuenta que en esta instrucción y en las siguientes puedo usar los parámetros reales
en los que se devolvieron los resultados del procedimiento para trabajar con ellos.
Ejemplos de definición y uso de procedimiento
Problema 1: Se desea resolver el subproblema de reemplazar en un texto todas las apariciones de un
carácter por otro. Por ejemplo, si el texto es “las casas” y se debe reemplazar la “a” por “o”, el texto que
queda es “los cosos”.
De la lectura del problema encontramos los siguientes datos de entrada y datos de salida:
Datos de entrada
Car1
Texto a reemplazar: texto
Carácter a buscar: car1
Carácter de reemplazo: car2
Datos de salida
Car2
texto
Cliente
Reemplazo
Texto final: texto
Si bien hay un solo dato de salida, se trata de modificar el dato de entrada. Por lo tanto será un
parámetro de entrada-salida y no puede resolverse con una función. No hay precondiciones especiales
para este problema más que los parámetros de entrada o de entrada-salida vengan inicializados, porque
si el texto fuera vacío, no habría nada para cambiar y devolvería el mismo texto vacío. Como texto, car1
y car2 son datos de entrada, deberán estar inicializadas.
Para resolver el problema, se recorre el texto de principio a fin y por cada carácter del texto se lo
compara con car1, si hay coincidencia se lo reemplaza por car2.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 43
Algorítmica y Programación I
La operación queda definida como:
Procedimiento Reemplazo (E/S texto: cadena, E car1,car2: carácter)
(* reemplaza en texto
precondición: texto =
poscondición: texto =
encuentra en T2 o (C1
*)
todas las apariciones de car1 por car2
T1 y car1= C1 y car2 = C2
T2 y (C1 no se encuentra en T2 o C1 = C2) y (C2 se
no se encuentra en T1 y T1 = T2))
Var
i: entero
Inicio
Desde i<-1 hasta long(texto) hacer
Si texto[i] = car1 entonces
Texto[i] <- car2
FinSi
FinDesde
FinProc
Ejemplo de utilización de este procedimiento en otro algoritmo:
Algoritmo UsaReemplazo
(* Recibir un texto y mostrarlo luego de reemplazar todas las vocales por guiones
*)
Const
Guion= ‘-’
Observar que:
Var

texto es el parámetro real asociado al
texto: cadena
parámetro formal de entrada-salida y es
Inicio
una variable inicializada (por efecto de
Mostrar (“ingrese texto”)
la operación ingresar
Ingresar (texto)

la correspondencia con los parámetros
Reemplazo (texto, ‘a’, Guion)
de entrada car1 y car2 se realiza con
Reemplazo (texto, ‘e’, Guion)
parámetros reales constantes (por
Reemplazo (texto, ‘i’, Guion)
ende, inicializados).
Reemplazo (texto, ‘o’, Guion)
Reemplazo (texto, ‘u’, Guion)
Mostrar (texto)
Fin
Problema 2: Se desea resolver el subproblema de conocer las veces que se repite un carácter
determinado en un texto y el lugar o posición de su última aparición. Si el carácter no se encontrara, la
cantidad será nula y la posición cero. De la lectura del problema encontramos los siguientes datos de
entrada y datos de salida:
texto
Datos de entrada
Texto donde buscar: texto
Carácter a buscar: car1
Datos de salida
Cantidad de veces que se encuentra: cant
Posición de la última aparición: pos
car1
cant
pos
Cliente
averiguaCantPos
El algoritmo que lo use deberá suministrar el texto y carácter a buscar y obtendrá como resultados la
cantidad y la posición. Se requieren dos parámetros de entrada y dos parámetros de salida, por lo tanto
no puede resolverse con una función y deberá hacerse con un procedimiento. No hay precondiciones
especiales para este problema, y como texto y car1 son datos de entrada, deberán estar inicializadas. No
importa el valor que puedan tener cant y pos, este subalgoritmo les proporcionará el valor.
Para resolver el problema, primero se debe inicializar a pos y a cant en cero, luego se recorre el texto de
principio a fin y por cada carácter del texto se lo compara con car1, si hay coincidencia se incrementa
cant y se guarda su posición en pos. La operación queda definida como:
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 44
Algorítmica y Programación I
Procedimiento averiguaCantyPos (E texto: cadena, E car1: carácter, S
cant, pos: entero)
(* Cuenta todas las apariciones de car1 en texto y da la posición de su última
aparición
precondición: Texto = T y car1 = C
poscondición: cant=N y pos=P y ((N>0 y P>0 y P <= long(T) y T[P]=C y C se
encuentra en T Nveces) o (N=0 y P=0 y C no se encuentra en T)) *)
Var
i: entero
Inicio
cant <-0
pos <- 0
Desde i<-1 hasta long(texto) hacer
Si texto[i] = car1 entonces
cant <- cant +1
pos <- i
FinSi
FinDesde
FinProc
Observar que los parámetros
formales de salida (cant y pos) son
inicializados en el procedimiento
por cualquiera de los caminos que
siga el algoritmo.
Ejemplo de utilización:
Algoritmo UsaAveriguaCantyPos
(* Recibir un texto y mostrar la cantidad
de espacios en blanco y la posición del
último espacio *)
Observar que
 Los parámetros reales, texto y car, que se
corresponden con parámetros formales de
entrada, están inicializados (texto, por efecto de
la operación ingresar y car por ser una
constante).
 los parámetros reales cantidad y posición
correspondientes con parámetros formales de
salida son variables sin inicializar en el momento
de la invocación y quedan inicializadas por el
procedimiento, luego se utilizan sus valores.
Const
car= ‘ ’
Var
texto: cadena
cantidad, posicion:entero
Inicio
Mostrar (“ingrese texto”)
Ingresar (texto)
averiguaCantyPos (texto, car, cantidad, posicion)
Si (cantidad > 0) entonces
Mostrar (“hay ”, cantidad, “espacios y el último está en ”, posicion)
Sino
Mostrar(“No hay espacios en blanco”)
FinSi
Fin
Recursión
La recursión es una herramienta muy potente en algunas aplicaciones, sobre todo de cálculo. Se basa
en expresar el resultado de un problema como operaciones aplicadas sobre una instancia reducida del
mismo problema, hasta que se llega a un caso donde el problema queda bien definido. Este concepto es
el mismo que se utiliza al demostrar inductivamente un problema.
El uso de recursión es particularmente idóneo para la solución de aquellos problemas que pueden
definirse en forma natural en términos recursivos. Para que un problema pueda tener solución recursiva
es necesario:
1. El problema debe poder descomponerse en subproblemas del mismo tipo.
2. Debe haber uno o más casos en que la solución es directa o trivial.
3. Cada uno de los subproblemas debe ser de grado menor al que pertenece, y debe converger a
la solución trivial.
Es muy importante que el caso recursivo converja hacia la condición del caso trivial pues si no fuera así,
el problema no tiene solución.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 45
Algorítmica y Programación I
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 46
Algorítmica y Programación I
Tomemos el siguiente ejemplo de función recursiva:
1
si n=1
S(n-1) + n
si n > 1
S(n) =
Se puede observar que en la definición de S(n):
1. El problema se descompone en un subproblema del mismo tipo, al calcular S(n-1).
2. Existe un caso en que la solución es directa, cuando n=1.
3. Cada subproblema es de un grado menor y converge a la solución trivial, ya que en cada
invocación recursiva n es menor y en un número finito de veces llegará al valor 1.
Para computar el resultado de S(n) (en caso que sea mayor que 1), es necesario calcular el resultado
de S(n-1) y así sucesivamente hasta llegar al caso base (n = 1). Es necesario que estos casos estén
definidos para asegurar que la recursión termine.
Cómo se calcula S(3)?
S(3)
S(3) = (llamada recursiva)
S(2)+3 = (llamada recursiva)
S(1)+2+3 = (llamada recursiva)
(1+2)+3 = (3+3)= 6 (suma)




Retorno de
valores
S(2)
Llamada
recursiva
S(1)
¿Y si la definición de S(n) hubiese sido la siguiente?
1
si n=1
S(n+1) + n
si n > 1
S(n) =



S(3) = (llamada recursiva)
S(4)+3= (llamada recursiva)
S(5)+4+3 = (llamada recursiva)
y no terminaría nunca !!!
Un algoritmo recursivo incluye, por definición, instrucciones que consisten en ejecutar el mismo
algoritmo, a las que se denominará “llamadas recursivas”, es decir, llamadas a sí mismo. La escritura de
un procedimiento o función recursiva es similar a sus homónimos no-recursivos; sin embargo, para evitar
que la recursión continúe indefinidamente, es preciso incluir una condición de terminación. Como primera
observación, se hace notar que, en todo algoritmo recursivo ha de haber al menos una acción
alternativa. El texto del algoritmo debe incluir invocaciones del propio algoritmo, por ser éste recursivo,
pero no se ejecutan en todos los casos pues de lo contrario la recursividad no terminaría.
Se distingue pues en esa alternativa dos tipos de ramas: los casos directos o triviales, que no provocan
llamadas recursivas, y los casos recursivos o generales.
¿Qué debe tener un método recursivo?



Condicional
Caso base (no recursivo), en el ejemplo se da en n = 1
Caso recursivo, en el ejemplo s(n-1)+n, que converja o se aproxime hacia la condición del
caso base, en el ejemplo n = 1.
El algortimo de s(n) queda:
Funcion s (E n:entero): entero
(* precondicion: n=N y N >= 1
poscondicion: s = S *)
Inicio
Si n > 1 entonces
s <- s(n –1) + n
(* rama del caso recursivo *)
Sino
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 47
Algorítmica y Programación I
s <- 1
Fin Si
Fin Funcion
(* rama del caso base *)
Recursión vs Repetición
La recursión puede ser utilizada como una alternativa a la repetición o estructura repetitiva. Por ejemplo,
se desea realizar la sumatoria de los números de una lista de enteros. La solución repetitiva consiste en
inicializar el resultado en cero y repetir el proceso de sumar un valor al resultado hasta que la lista se
termine. Esquemáticamente el algoritmo tiene este aspecto:
Suma <- 0
Mientras hay números en la lista hacer
Suma <- suma + nro
Fin mientras
Para la solución recursiva el caso trivial es que la lista esté vacía, y en ese caso el resultado es cero. El
caso general o recursivo consiste en sumar el primer elemento de la lista y la sumatoria del resto de
elementos, primero + la sumatoria del resto de la lista. Este caso general propone un subproblema de un
grado menor que converge a la solución trivial, alcanzándose cuando el resto de la lista es vacía.
La definición recursiva queda:
0
si lista es vacía
Primero (lista) + Resto (lista)
si lista no es vacía
Sumatoria (lista) =
Donde Primero(lista) devuelve el primer elemento de la lista y Resto(lista) devuelve la lista sin su primer
elemento.
Ejemplos de problemas recursivos:
Factorial: El factorial de un número n no negativo es 1 si
el n es cero y es el producto entre n y el factorial (n-1) si
n>0.
1
factorial(n)
si n = 0
n * factorial(n-1) si n > 0.
El caso directo se da cuando el valor a calcular el factorial es cero. Cada subproblema consiste en
calcular el factorial de un número menor, y en un número finito de invocaciones el subproblema
convergerá al caso directo.
Planteo del algoritmo:
Datos de entrada
Número del cual se quiere conocer el factorial: nro
Datos de salida
Resultado de la operación: factorial
Restricciones
Nro >= 0
nro
factorial
Cliente
Funcion FactorialRecursiva (E nro:entero): entero
(* precondicion: nro=N y N >= 0
poscondicion: Factorialrecursiva = N! *)
Inicio
Si nro > 0 entonces
FactorialRecursiva <- FactorialRecursiva(nro –1) * nro
Sino
FactorialRecursiva <- 1
Fin Si
Fin Funcion
Factorial
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 48
Algorítmica y Programación I
Como puede observarse una de las ramas de la alternativa atiende el caso directo o trivial que no
provoca llamadas recursivas y la otra, los casos recursivos o generales.
FactorialRecursiva(4) = (llamada recursiva)
4 * FactorialRecursiva(3) = (llamada recursiva)
4 * (3 * FactorialRecursiva(2) ) = (llamada recursiva)
4 * (3 * (2 * FactorialRecursiva(1) ) ) = (llamada recursiva)
4 * (3 * (2 * (1 * FactorialRecursiva(0) ) ) = (caso base) = 4 * (3 * (2 * (1 * 1)))) = 24
24
FactorialRecursiva(4)= 4 *
FactorialRecursiva(3) = 3 *
6
2
Llamada
recursiva
FactorialRecursiva(2) = 2 *
FactorialRecursiva(1) = 1 *
Retorno de
valores
1
FactorialRecursiva(0) = 1
1
Potencia de números enteros:
Potencia(base,exp)
1
si exp = 0
base * potencia(base,exp-1)
si exp > 0
1 / potencia(base,abs(exp))
si exp < 0
Datos de entrada
Número entero del cual se quiere conocer su potencia: base
Número entero que representa la potencia a la que se quiere elevar la base: exponente
Datos de salida
Resultado de la operación: potencia
Funcion PotenciaRecursiva (E base, exp:entero): real
(* precondicion: base=B y exp=E
poscondicion: PotenciaRecursiva = BE *)
base
Inicio
exponente
Si exp = 0 entonces
potencia
PotenciaRecursiva <- 1
Sino
Cliente
Potencia
Si exp > 0 entonces
PotenciaRecursiva <- PotenciaRecursiva(base,exp –1) * base
Sino
PotenciaRecursiva <- 1 / PotenciaRecursiva(base, abs(exp))
Fin Si
Fin Si
Fin Funcion
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 49
Algorítmica y Programación I
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 50
Algorítmica y Programación I
Invocación PotenciaRecursiva(3,–2)
1/9
PotenciaRecursiva(3,–2)= 1/
*
Llamadas
recursivas
PotenciaRecursiva(3,2) = 3 *
9
Retorno de
valores
3
PotenciaRecursiva (3,1) = 3 *
*
PotenciaRecursiva (3,0) = 1
1
Los conejos de Fibonacci
Cierto matemático italiano de nombre Leonardo de Pisa, pero mejor conocido como Fibonacci, propuso
el siguiente problema: Suponga que acabamos de comprar una pareja de conejos adultos. Al cabo de un
mes, esa pareja tiene una pareja de conejitos (un conejo y una coneja). Un mes después, la primer
pareja tiene otra pareja de conejitos (nuevamente, un conejo y una coneja) y, al mismo tiempo, sus
primeros hijos se han vuelto adultos. Así que cada mes que pasa, cada pareja de conejos adultos tiene
una pareja de conejitos, y cada pareja de conejos nacida el mes anterior se vuelve adulta. La pregunta
es, ¿cuántas parejas de conejos adultos habrá al cabo de n meses?
Para resolver este problema, llamemos Fn al número de parejas adultas al cabo de n meses. Si n es al
menos 2, entonces Fn es igual a Fn-1 + Fn-2. ¿Por qué?
Fn queda en términos de Fn-1 y Fn-2, que a su vez quedan en términos de Fn-2, Fn-3 y Fn-4, que a su
vez... ¡Descompusimos el problema en subproblemas del mismo tipo!
Ahora salimos del círculo vicioso recordando que al principio había una pareja de conejos adultos, la
misma que había al final del primer mes, así que F0 = F1 = 1. ¡los casos bases!
Hagamos un ejemplo:
F4 = F3 + F2 = (F2 + F1) + (F1 + F0) = ((F1 + F0) + 1) + (1 + 1) = ((1 + 1) + 1 ) + 2 = (2 + 1) + 2 = 3 + 2 = 5.
La sucesión de números F0 = 1, F1 = 1, F2 = 2, F3 = 3, F4 = 5, etc. recibe el nombre de sucesión de
Fibonacci.
Para calcular Fn se requiere:
Datos de entrada
Número del cual se quiere conocer Fn: nro
Datos de salida
Resultado de la operación: fibonacci
Restricciones
Nro >= 0
Funcion Fibonacci (E nro:entero): entero
(* precondicion: nro=N y N >= 0
poscondicion: Fibonacci = F *)
Inicio
Si nro > 1 entonces
Fibonacci <- Fibonacci (nro –1) + Fibonacci (nro-2)
Sino
Fibonacci <- 1
Fin Si
Fin Funcion
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 51
Algorítmica y Programación I
Las torres de Hanoi
Una antigua leyenda dice que en cierto monasterio de Hanoi había tres postes y que en uno de ellos
había 64 discos de tamaño decreciente, uno encima de otro y con el mayor hasta abajo. Los monjes del
monasterio han estado trabajando sin cesar para llevar los discos desde su poste original hasta algún
otro siguiendo una regla sencilla: solamente pueden mover un disco a la vez de un poste a otro, siempre
y cuando queda arriba de uno mayor.
Llamemos a los postes A, B y C (siendo A el poste original
y C el poste al que queremos mover todos los discos). Si
no hubiera disco no debo hacer nada (caso trivial). Si
solamente hubiera un disco, la tarea hubiera sido muy
sencilla: movamos el único disco del poste A al poste C
(caso trivial).
Si solamente hubieran dos discos, la tarea no hubiera sido
mucho más difícil: basta mover el disco pequeño al poste
B, después el disco grande al poste C y finalmente el disco
pequeño al poste C.
Si continuamos de esta manera, pronto nos daremos
cuenta que la labor monumental de mover los 64 discos se
reduce a la siguiente: movamos los 63 discos más
pequeños del poste A al B, movamos el disco más grande del poste A al C y finalmente movamos los 63
discos más pequeños del poste B al C.
¿Cómo hacemos para mover los 63 discos? Lo haremos de la misma forma, excepto que renombrando
los postes adecuadamente.
De esta manera, hemos dividido la tarea de mover n discos en dos tareas menores. Por lo tanto, si hay
discos, primero se mueven n-1 discos del poste izquierdo al poste medio (del A al B), luego se mueve el
disco n del poste izquierdo al derecho (del A al C) y finalmente se mueve los n-1 disco del poste medio al
derecho (del B al C). Observar que existen dos llamadas recursivas, una que mueve de A a B y otra que
mueve de B a C. La acción de mover el
El siguiente algoritmo recursivo mueve n discos de un poste original a un poste destino utilizando un
tercer poste auxiliar:
Procedimiento Hanoi(n, Original, Auxiliar, Destino)
Inicio
Si n es mayor que 0 entonces
Hanoi(n-1, Original, Destino, Auxiliar)
Mueve un disco de Original a Destino
Hanoi(n-1, Auxiliar, Original, Destino)
Fin si
Fin Proc
Observación
Dentro del condicional
se produce la
recursión.
Si n es 0 no hace nada.
Por ejemplo, si n es igual a 4, nuestro algoritmo haría los siguientes movimientos (denotamos por AB la
operación de mover un disco de A a B, etcétera):
AB, AC, BC, AB, CA, CB, AB, AC, BC, BA, CA, BC, AB, AC, BC.
Ambito: variables locales y globales
¿Qué es el ámbito de un identificador?
El ámbito de un identificador (variables, constantes, funciones, procedimientos) es la parte del programa
en la que se conoce y por tanto se puede usar un identificador. Según el ámbito hay 2 tipos de variables,
locales y globales:
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 52
Algorítmica y Programación I
Local: Aquella que está declarada y definida dentro de un subprograma. Su ámbito coincidirá con el
ámbito del subprograma en la que este definida.
Esto quiere decir que la variable no tiene ningún significado, no se conoce y no se puede acceder a ella
desde fuera del subprograma y que tiene una posición de memoria distinta a la de cualquier otra, incluso
si es de una variable que tiene el mismo nombre pero que está definida fuera del subprograma.
Las variables locales a un subprograma se definen en la parte de la definición de variables del mismo.
Los parámetros formales de un subprograma se comportan dentro de él como si fueran también
variables locales a él.
Globales: Son las que están definidas a nivel del programa, es decir, su ámbito es el programa o
algoritmo principal y todos los subprogramas que van junto con él.
A esta variable podemos acceder desde cualquiera de los subprogramas y el programa principal, salvo
que alguno de esos subprogramas tenga definida una variable local con el mismo nombre que la variable
global, en este caso si utilizo el nombre de esa variable me referiré a la local, nunca a la global.
Lugar en el que se definen las variables globales:
En algunos lenguajes se define en el programa principal, y esa variable será global, en otros lenguajes
se definen fuera del programa principal y fuera de cualquier otro subprograma (antes de empezar el
programa principal).
El problema de usar variables globales es que como todos los subprogramas las pueden modificar,
puede ser que haya usos indebidos cuando un subprograma utiliza una variable global sin saber que otro
la ha modificado, por esa razón el paso de información entre los subprogramas debiera realizarse sólo
por parámetros.
PP
Var A,B
Procedimientos anidados
Algunos
lenguajes
de
programación
permiten
definir
procedimientos o funciones dentro de la definición de otro
procedimiento o función. A esto se lo llama anidamiento o
anidación.
Si la anidación de procedimientos está permitida, aparecen otras
situaciones a considerar en cuanto al ámbito. Se dice que una
variable local se conoce en el procedimiento en el que está
definida y en todos los procedimientos anidados que son los que
componen el ámbito de dicho procedimiento.
PP1
Var A,C
PP2
Var x,y
PP3
Var x, C
Si en alguno de esos procedimientos anidados esté definida otra
variable local con el mismo nombre, esta última tiene preferencia,
ya que se considera el ámbito más restringido.
Variable
Ámbito
Variable
Ámbito
A de PP
PP
x de PP2
PP2
B de PP
PP, PP1, PP2, PP3
y de PP2
PP2
A de PP1
PP1, PP2, PP3
x de PP3
PP3
C de PP1
PP1, PP2
C de PP3
PP3
Efectos laterales
Un efecto lateral es cualquier modificación que un subprograma (sea función o procedimiento), realiza en
elementos situados fuera de él pero sin hacer esas modificaciones a través del paso de parámetros.
Los efectos laterales siempre hay que evitarlos porque no somos conscientes de las modificaciones que
pudieran realizarse y resulta difícil rastrear los errores. Para evitar los efectos laterales, la comunicación
entre subprogramas debiera hacerse sólo a través del paso de parámetro.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 53
Algorítmica y Programación I
Los efectos laterales normalmente se producen por el uso de variables globales o variables locales que
abarcan varios procedimientos (esto solo es posible si hay anidación de subprogramas). Por lo tanto
evitaremos su uso excepto que sea imprescindible.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 54
Algorítmica y Programación I
Ejemplo:
Var A:entero
Algoritmo EJ
Var B:entero
Inicio
B <- 1
A <- 2
PP(B)
Mostrar (A)
Fin
En este ejemplo, A es una variable
global que el algoritmo principal la
inicializa en 2. Luego de llamar al
procedimiento PP con argumento
B, A sufrió un cambio de estado,
sin embargo, en la lectura del
algoritmo principal nada hace
suponer que cambiaría. Sucede
que el procedimiento PP la
modifica accediendo globalmente.
Procedimiento PP(E x:entero)
Inicio
A <- x+2
Fin Proc
Estos efectos
evitarse.
laterales
deben
Métodos de paso de parámetros
Existen distintas formas en que los lenguajes resuelven el paso de parámetros: haciendo copia de valores,
pasando la dirección o camino de acceso al parámetro real, a través del nombre.
Paso de parámetros por copia:
o
Por valor.
o
Por valor-resultado.
o
Por resultado.
La característica fundamental de este método de paso de parámetros es que el parámetro formal
siempre se considera que tiene asociada una dirección de memoria en la que está almacenado y que por
tanto se comporta igual que una variable local del subprograma en que aparece.
En este método lo que importa es el valor del parámetro actual.
Por valor: Nos interesa el valor del parámetro actual a la entrada, para ello este valor se copia en la
dirección de memoria del parámetro formal asociado. En este caso el parámetro real puede ser una
constante, expresión o variable, y nunca se va a usar para devolver resultado a través de él. Si el
parámetro actual fuera una variable y la modificásemos dentro del subprograma (algo que no
deberíamos hacer), fuera del subprograma no tendría ninguna repercusión esta modificación, es decir,
esa variable seguiría valiendo lo mismo en el programa desde el que se hace la llamada después y antes
de hacerla.
Los lenguajes utilizan este método para implementar el pasaje de parámetros de ENTRADA.
Por valor-resultado: En el valor-resultado nos interesa el valor del parámetro actual tanto a la entrada
como a la salida de la ejecución del subprograma.
Esto quiere decir que si se cambia el valor del parámetro formal, cambiará también el valor de su
parámetro real asociado, cosa que no ocurría antes, y esto supone por tanto que ahora el parámetro real
tiene que tener asociada obligatoriamente una dirección de memoria, por lo que siempre tendrá que ser
una variable (no una constante ni una expresión).
Es un de las maneras de implementar el pasaje de parámetros de ENTRADA-SALIDA
Por resultado: Nos interesa el valor del parámetro real solamente a la salida o fin de la ejecución del
subprograma en que aparece. Esto significa que al hacer la llamada no se copia el valor del parámetro
real en el parámetro formal asociado, sin embargo a la salida se copia el valor del parámetro formal en la
dirección del parámetro real asociado, esto significa por tanto, que el parámetro real tiene que tener
asociada una expresión que tiene que ser una variable (no puede ser una constante o una expresión).
Es una manera de implementar el pasaje de parámetros de SALIDA
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 55
Algorítmica y Programación I
Paso de parámetros por referencia
La característica principal de este tipo de paso de parámetros es que el parámetro formal va a tener
también asignada una dirección de memoria en la que se almacena, pero en esa dirección NO SE
GUARDA SU VALOR, sino que se almacena la dirección de su parámetro real asociado, es decir, el
parámetro formal apunta al parámetro real que tiene asociado y cualquier modificación que se efectúe
sobre el parámetro formal tendrá una repercusión directa en el parámetro real asociado ya que lo que
modificará será el valor almacenado en la dirección que indica el parámetro formal que es la de su
parámetro real asociado.
El proceso será por tanto el siguiente:
o
Al hacer la llamada al procedimiento en el parámetro formal que se pasa por referencia, se va a
guardar la dirección del parámetro real asociado para que apunte a él.
o
Durante la ejecución cualquier referencia al parámetro formal se hará accediendo a la dirección
apuntada por dicho parámetro, es decir, accediendo directamente al parámetro real asociado,
por lo que cualquier cambio en el parámetro formal afectará directamente al parámetro real
asociado. De esta manera habremos pasado el resultado.
La mayoría de los lenguajes utiliza este método para implementar el pasaje de parámetros de
ENTRADA-SALIDA y de SALIDA.
Paso de parámetros por nombre
En este caso, el parámetro formal se sustituye literalmente por el parámetro actual asociado. Esta
sustitución literal del parámetro formal por el parámetro actual no se produce hasta que no se usa el
parámetro formal.
El paso de parámetro por nombre es lo que se parece más a la substitución de parámetros en una
función matemática.
Pasaje de parámetros en los distintos lenguajes
En la práctica la mayor parte de los lenguajes utilizan el tipo de paso de parámetro por valor (para
parámetros de entrada) y por referencia (para parámetros de entrada-salida o de salida). Los otros dos
tipos de paso de parámetros por copia (por resultado y por valor-resultado), no se implementan
normalmente porque los efectos son prácticamente iguales que el paso de parámetros por referencia.
En Pascal para denotar que se utilizará pasaje por referencia se precede al parámetro formal con la
palabra reservada Var mientras que el pasaje por valor no lleva indicación especial.
En Visual Basic, las palabras ByVal y ByRef las que se utilizan para distinguir el pasaje. También es
factible declarar a una de ellas por defecto y sólo aclarar cuando es la otra.
En C, sólo existe el pasaje por valor. El pasaje por referencia debe hacerse trabajando directamente con
punteros.
En Ada, se indica con las palabras in, out e in-out para indicar por valor, resultado o valor-resultado.
En Java, no se requiere distinguirlo: si se trata de un tipo simple es por valor, si se trata de un objeto es
por referencia.
Estructuras de datos
Una estructura de datos es un conjunto de variables (probablemente de distintos tipos) relacionadas
entre sí de diversas formas. Existe la posibilidad de definir y utilizar tipos estructurados, entre los que se
encuentran los arreglos, registros, cadenas, archivos, etc. Todos los tipos estructurados son “moldes”
para variables estructuradas, las cuales pueden contener más de un valor. Los tipos de datos
estructurados pueden ser construidos a partir de tipos simples o de otros tipos estructurados, con lo que
la variedad es enorme.
Pueden clasificarse de acuerdo al tipo de datos que las forman, en homogéneas y heterogéneas. Una
estructura de datos es homogénea si todos sus componentes son del mismo tipo. Una estructura de
datos es heterogénea si sus componentes son de distinto tipo.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 56
Algorítmica y Programación I
Otra clasificación es en función de la cantidad de almacenamiento utilizado por la estructura durante la
ejecución del programa, dividiéndose en estáticas y dinámicas. Una estructura de datos se dice estática
si la cantidad de elementos que contiene es fija, es decir, la cantidad de memoria no varía durante la
ejecución de un programa. Una estructura de datos se dice dinámica si la cantidad de elementos que
contiene y por ende, la cantidad de memoria puede variar durante la ejecución de un programa.
Los lenguajes de programación brindan un conjunto de constructores de tipo estructurado, con los cuales
el usuario puede crear o definir tipos para trabajar con estructuras de datos compuestas que representan
los elementos del mundo real. Estos constructores son:
o
o
o
o
o
o
Arreglo o Array para construir estructuras homogéneas estáticas accesibles por índice.
Cadena o String para construir estructuras homogéneas estáticas de caracteres
Registro o Record para construir estructuras heterogéneas estáticas accesibles por nombre de
componente.
Conjunto o Set para construir estructuras homogéneas estáticas de elementos simples con
operaciones de unión, intersección, diferencia y pertenencia.
Archivo o File para construir estructuras homogéneas o heterogéneas que crecen
dinámicamente en medio externo.
Puntero y registros autoreferenciados para construir estructuras dinámicas.
Arreglos
En ocasiones en un programa necesitamos agrupar variables que almacenen datos de un mismo tipo.
Supongamos, por ejemplo, que deseamos registrar la temperatura promedio mensual durante un año en
nuestra ciudad. Si utilizáramos los tipos de datos simples, necesitaríamos declarar 12 variables reales
distintas, una por cada mes, para almacenar dicha información. Sería útil poder contar con alguna
estructura que nos permita agrupar estos datos bajo un mismo nombre, y acceder individualmente a
cada uno de ellos. Esta estructura se puede crear con un constructor de tipo denominado arreglo.
Un arreglo es un conjunto finito y homogéneo de celdas direccionables. Se puede acceder directamente
a cada una mediante su posición, la cual es determinada por el o los índices. En otras palabras, es una
estructura de datos indexada, estática y homogénea.
Los arreglos se clasifican en dos grupos:
o
Unidimensionales (llamados también vectores): poseen una única dimensión, es decir, cada
posición es accesible mediante un único índice. En nuestro ejemplo de las temperaturas
promedio, necesitaríamos indicar sólo el mes para obtener la temperatura promedio del mismo.
o
Multidimensionales (por ejemplo, las matrices): poseen más de una dimensión, cada elemento
dentro de ellos se direcciona mediante dos o más índices. Las matrices son un caso particular de
los arreglos multidimensionales, pues poseen 2 dimensiones. Ejemplo: se requiere registrar las
temperaturas promedio mensuales de las ciudades capitales de provincias en los últimos 10
años. En este caso se requiere utilizar un arreglo de 3 dimensiones una para el mes, otra para el
año y otra para la ciudad.
Para definir un tipo de arreglo, debemos dar un nombre de tipo y asociarlo con la definición adecuada, la
cual consiste en indicar el o los rangos de índice y el tipo de dato, según la siguiente plantilla:
<nombreTipo> = arreglo [<rango>] de <tipo>
El rango puede ser cualquier subrango de un tipo enumerado. El tipo del elemento debe ser de un tipo
básico o de un tipo previamente definido (simple o estructurado). Algunos lenguajes utilizan la cantidad
de elementos en lugar del rango, y en ese caso el índice solamente puede ser entero en un valor que va
de 1 a esa cantidad (Lenguaje Basic), o desde 0 al tope –1 (Lenguaje C)
Ejemplos:
Const
Max = 10
Tipo
// definir tLista como arreglo de 10 enteros
tLista= arreglo [1..Max] de enteros
// definir tMatriz como una matriz cuadrada de 10 x 10
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 57
Algorítmica y Programación I
tMatriz= arreglo [1..Max, 1..Max] de real
// definir un tipo que sirva para contadores de caracteres mayusculas
(* si utilizamos un indice de tipo carácter nos será más facil
identificar el contador de cada carácter *)
tCont= arreglo[ ‘A’.. ‘Z’] de enteros
Var
Lista: tLista
Matriz: tMatriz
ContadorLetra: tCont
Si las variables ya estuvieran inicializadas, para acceder a un elemento de ellas, debemos acompañar al
nombre de la variable con la indicación del índice o de los índices que representan la posición del dato.
Lista
10
20
[1]
25
[2]
45
[3]
58
[4]
Lista[1] = 10 y Lista[9]
Matriz
12
[5]
21
[6]
36
[7]
98
[8]
50
[9]
[10]
> Lista[10]
1.0
2.0
2.5
4.5
5.8
1.2
2.1
3.6
9.8
5.0
[1]
5.0
1.2
2.1
0.4
0.6
0.8
1.0
1.2
1.6
2.0
[2]
7.0
1.5
2.0
1.4
1.5
2.1
3.2
8.5
2.2
1.0
[3]
9.0
1.4
5.0
0.4
0.8
1.2
0.4
1.2
0
1.2
[4]
8.0
1.9
5.4
5.1
2.2
0.2
1.1
1.6
1.4
1.4
[5]
4.5
1.7
2.1
2.2
3.5
2.1
1.6
1.2
1.1
2.1
[6]
5.6
1.0
1.3
1.5
5.1
6.2
2.5
3.2
4.2
2.1
[7]
-1.1
1.0
-2.1
2.0
3.1
3.2
2.3
2.5
2.8
2.9
[8]
2.1
0
0.2
0.5
1.5
1.8
1.6
1.1
2.3
2.0
[9]
0
2.3
2.2
8.1
-9.1
-5.0
2.3
2.4
7.1
2.8
[10]
[1]
[2]
[3]
[4]
[5]
[6]
[7]
[8]
[9]
[10]
Matriz[1,1] = 1.0 y Matriz[3,9] =2.2
ContadorLetra
10
20
25
10
20
2
4
1
5
2
[A]
[B]
[C]
[D]
[E]
[F]
[G]
[H]
[I]
[J]
...
...
5
0
12
5
0
0
0
1
[S]
[T]
[U]
[V]
[W]
[X]
[Y]
[Z]
ContadorLetra[‘A’] = 10 y ContadorLetra[‘Z’] = 1
Recorridos en el arreglo
Cuando se desea recorrer un arreglo en forma completa, la mejor manera de hacerlo es utilizando una
estructura de repetición del tipo Desde por cada índice del arreglo.
Si el arreglo es multidimensional, las estructuras de repetición se anidarán y dependiendo del orden de
anidamiento será el orden en que se visitarán las celdas. Si deseamos que Matriz sea recorrida por filas,
el primer Desde se asociará con el primer índice (que indica la fila). Si deseamos que sea recorrida por
columnas el "Desde" más exterior será el que se asociará con el segundo índice.
Por ejemplo, el siguiente fragmento de código mostrará los valores de la primer fila primero, luego los de
la segunda, y así hasta finalizar.
Desde fila <- 1 hasta Max hacer
Desde columna <-1 hasta Max hacer
Mostrar (Matriz [fila,columna])
Fin Desde
Fin Desde
En cambio, el siguiente fragmento de código mostrará los valores de la primer columna primero, luego
los de la segunda, y así hasta finalizar.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 58
Algorítmica y Programación I
Desde columna <- 1 hasta Max hacer
Desde fila <-1 hasta Max hacer
Mostrar (Matriz [fila,columna])
Fin Desde
Fin Desde
Accesos aleatorios en el arreglo
Cuando se desea acceder en forma aleatoria a posiciones del arreglo según el pedido del usuario,
debemos solicitar la posición y validar que esté dentro del rango del índice antes de acceder al elemento.
Si se trata de Matriz, debemos pedir el valor para cada índice y validar a cada uno.
Definición del rango del índice
En general el índice será numérico y comenzará desde 1, pero dependiendo del problema puede ser
conveniente utilizar otro rango o tipo de dato para el índice.
Cuando no se sabe cuántos elementos se van a requerir, se debe pensar en definir un arreglo lo
suficientemente grande como para contener la cantidad máxima que pueden presentarse. Luego, en
tiempo de ejecución se utilizará sólo una porción del arreglo si no se presentan todos los datos. Por
ejemplo, deseo almacenar las notas del curso de Algorítmica y Programación, a razón de una por
alumno. Como pretendo que el programa sirva a lo largo de los años y la cantidad de alumnos es
variable año tras año, defino un arreglo con el máximo de alumnos que preveo puedo tener, por ejemplo
200. En un año en particular a lo mejor utilizo 100 y en otro año utilizo 120. En este caso, además del
tope real del arreglo utilizado para su definición, debo llevar una variable con la cantidad real utilizada, la
cual será menor o igual al tope, y servirá de extremo superior en el recorrido del arreglo. Cualquier
acceso a una celda del mismo deberá estar entre 1 y esa cantidad.
Llenado de datos de un arreglo
El criterio para el ingreso de datos lo establece el programador, en función del modelo que esté
programando. En general se preferirá un llenado secuencial, con una estructura de repetición Desde que
pida los datos uno tras otro. Pero si por el tipo de problema se establece como más conveniente un
recorrido aleatorio, debemos poder reconocer qué celdas han sido llenadas y cuáles no, para advertirle
al usuario cuando intenta volver a llenar una celda que tiene datos o para recordarle que hay celdas sin
datos. Para poder reconocer las celdas sin datos cargados, previo al proceso de llenado aleatorio, el
arreglo debe ser inicializado con un valor determinado que sirva de indicador o que sea el valor por
defecto que debe quedar en las celdas sin llenar.
Ejemplo:
Tengo que pasar las notas de los exámenes a un arreglo de notas donde cada índice representa un
alumno en el orden que figura en la lista alfabética. Si los exámenes los tengo ordenados como la lista, la
mejor manera de llenar las notas es con llenado secuencial: el algoritmo impone el índice de la celda que
se va a llenar, utilizando como índice la variable contador de la estructura de repetición Desde. Si los
exámenes no los tengo ordenados, sería más fácil para el usuario llenarlo en forma aleatoria, eligiendo el
propio usuario la celda que va a llenar. En este caso puedo inicializar previamente el arreglo con –1, que
no es un valor de nota posible, y si al finalizar la carga aún quedan celdas con –1 significará que falta
pasar la nota de ese alumno, si en cambio, pretendo pasar la nota del alumno 5 y en la celda 5 hay un
valor distinto de –1, significa que ya ingresé su nota, y el programa debiera advertirlo.
Algoritmos básicos con arreglos
Los ejemplos siguientes representan operaciones comunes que se presentan en problemas que
requieren estructuras de datos como arreglos.
Tomaremos como ejemplo al tipo tLista definido anteriormente como un arreglo de [1.. Max] de enteros,
para arreglos unidimensionales y el tipo tMatriz definido anteriormente como un arreglo de [1..
Max,1..Max] de real, para arreglos multidimensionales.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 59
Algorítmica y Programación I
Inicialización de un arreglo unidimensional
Se trata de una operación que pretende llenar un rango de celdas consecutivas del arreglo con un valor
fijo, recibido como parámetro.
Primero, ultimo
Datos de entrada
Rango: primero y último del rango de celdas
Valor de inicialización: val
Datos de salida:
Arreglo de datos: Lista
val
lista
Cliente
Inicializar
Si bien hay un solo parámetro de salida, esta operación debe realizarse con un procedimiento porque las
funciones sólo pueden devolver datos simples o cadenas. Entonces definiremos un procedimiento
Inicializar con estos parámetros.
Procedimiento Inicializar(S Lista:tLista, E primero, ultimo: entero, E
val:entero)
(*Precondición: primero=P y ultimo=F y val=V y P<=F y [P,F] 
rango(tLista) *)
(*Poscondición: Lista= {Li} y Li = V  i  [P,F]*)
Var
i: entero
Inicio
Desde i<-primero hasta ultimo hacer
Lista[i] <- val
Fin Desde
Fin Proc
Llenado secuencial de un arreglo unidimensional
Se trata de una operación que pretende llenar un rango de celdas consecutivas del arreglo con un valor
ingresado por el usuario.
Primero,
Datos de entrada
Rango: primero y fin del rango de celdas
Datos de salida:
Arreglo de datos: Lista
ultimo
Lista
Cliente
LlenarListaSec
Si bien hay un solo parámetro de salida, esta operación debe realizarse con un procedimiento porque las
funciones sólo pueden devolver datos simples o cadenas. Entonces definiremos un procedimiento
LlenarListaSec con estos parámetros.
Procedimiento LlenarListaSec(S Lista:tLista, E primero, ultimo: entero)
(*Precondición: primero=P y ultimo=F y P<=F y [P,F]  rango(tLista) *)
(*Poscondición: Lista = {Li}  i  [P,F] *)
Var
i: entero
Inicio
Desde i<-primero hasta ultimo hacer
Mostrar(“Ingresar valor”, i , “:”)
Ingresar (Lista[i])
Fin Desde
Fin Proc
Consulta aleatoria de un arreglo unidimensional
Se trata de una operación que pretende mostrar celdas a pedido del usuario dentro de un rango de
celdas del arreglo.
Primero, ultimo
Datos de entrada
Rango: primero y fin del rango de celdas
Lista
Cliente
MostrarListaAlea
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 60
Algorítmica y Programación I
Arreglo de datos: Lista
No hay datos de salida, entonces definiremos un procedimiento MostrarListaAlea con estos parámetros.
Procedimiento MostrarListaAlea(E Lista:tLista, E primero, ultimo: entero)
(*Precondición: primero=P y ultimo=F y val=V y P<=F y [P,F] rango(tLista)
y Lista = {Li}  i  [P,F] *)
(*Poscondición: se mantienen las precondiciones *)
Var
pos: entero
Inicio
Repetir
Pos <- enteroEnRango(“Ingrese posición”,primero,ultimo)
Mostrar (Lista[pos])
Hasta confirma(“finaliza?”)
Fin Proc
Verificar si un arreglo unidimensional está ordenado en forma ascendente
Se trata de una operación que pretende verificar que cada elemento que está en una posición i de la lista
es menor o igual al que está en la posición i+1. Para tener la oportunidad de que el algoritmo sirva para
verificar una sublista, se requiere conocer el principio y fin del rango de celdas.
Primero, ultimo
Datos de entrada
Rango: primero y fin del rango de celdas a verificar
Arreglo de datos: Lista
Datos de salida:
Resultado de la operación: estáOrdenado (lógico)
Lista
estáOrdenado
Cliente
estáOrdenado
Como se requiere un único dato de salida de tipo simple que puede ser calculado en función de los datos
de entrada, esta operación conviene realizarse como una función. Supondremos que la lista está
ordenada y si en el proceso de verificación de que cada elemento i sea menor o igual al respectivo i+1
fallara, cortamos la verificación y devolvemos el valor falso.
Funcion estáOrdenado(E lista:tLista, E primero, ultimo: entero): logico
(*Precondición: primero=P y ultimo=F y y P<=F y [P,F] rango(tLista) y
Lista = {Li}  i  [P,F] *)
Poscondición: estáOrdenado = verdadero si Li<=Li+1  i  [P,F-1] y
falso si no*)
Var
i: entero
verifica: logico
Inicio
verifica <- verdadero
i <- primero
Mientras i< ultimo y verifica hacer
Si lista[i] > lista[i+1] entonces
verifica <- falso
Fin Si
i <- i + 1
Fin Mientras
estáOrdenado <- verifica
Fin Funcion
Llenado secuencial de un arreglo bidimensional
Se trata de una operación que pretende llenar un rango de celdas consecutivas del arreglo bidimensional
con un valor ingresado por el usuario. Debemos distinguir si queremos realizarlo por filas o por
columnas, cambiará el orden en que anidamos las estructuras de repetición.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 61
Algorítmica y Programación I
Datos de entrada
Rango: primero y fin del rango de filas y primero y fin del rango de columnas
Datos de salida:
Arreglo de datos: Matriz
Si bien hay un solo parámetro de salida, esta operación debe realizarse con un procedimiento porque las
funciones sólo pueden devolver datos simples o cadenas. Entonces definiremos un procedimiento
LlenarMatrizSecF para llenarla por filas y LlenarMatrizSecC para llenarla por columnas.
Por razones de simplicidad, supondremos que el principio de las filas y de las columnas siempre es 1,
por lo que al ser una constante conocida en tiempo de diseño, no requerimos que sea pasada como
parámetro, de no hacer esta suposición, deberemos tener dos parámetros adicionales que informen el
principio del rango de filas y el principio del rango de columnas.
Procedimiento LlenarMatrizSecF(S matriz:tMatriz, E finF, finC: entero)
(*Precondición: finF=F1 y finC=F2 y [1,F1] rango1(tMatriz) y [1,F2] 
rango2(tMatriz)*)
(*Poscondición: matriz={ Mij }  i  [1,F1] y  j  [1,F2]*)
Var
finF, finC
i,j: entero
Inicio
matriz
Desde i<-1 hasta finF hacer
Cliente
LlenarMatrizSecF
Desde j<-1 hasta finC hacer
Mostrar (“Ingrese elemento ”,i, “,”,j, “:”)
Ingresar (matriz[i,j])
Fin Desde
Fin Desde
Fin Proc
Procedimiento LlenarMatrizSecC(S matriz:tMatriz, E finF, finC: entero)
(*Precondición: finF=F1 y finC=F2 y [1,F1] rango1(tMatriz) y [1,F2] 
rango2(tMatriz)*)
(*Poscondición: matriz={ Mij }  i  [1,F1] y  j  [1,F2]*)
Var
finF, finC
i,j: entero
Inicio
matriz
Desde j<-1 hasta finC hacer
Cliente
LlenarMatrizSecC
Desde i<-1 hasta finF hacer
Mostrar (“Ingrese elemento ”,i, “,”,j, “:”)
Ingresar (matriz[i,j])
Fin Desde
Fin Desde
Fin Proc
Consulta aleatoria de un arreglo bidimensional
Se trata de una operación que pretende mostrar celdas a pedido del usuario dentro de una matriz.
Datos de entrada
Rango: principio y fin del rango de filas y
principio y fin del rango de columnas
Arreglo de datos: Matriz
No hay datos de salida, entonces definiremos un procedimiento MostrarMatrizAlea con estos parámetros.
Por razones de simplicidad, supondremos que el principio de las filas y de las columnas siempre es 1,
por lo que al ser una constante conocida en tiempo de diseño, no requerimos que sea pasada como
parámetro, de no hacer esta suposición, deberemos tener dos parámetros adicionales que informen el
principio del rango de filas y el principio del rango de columnas.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 62
Algorítmica y Programación I
Procedimiento MostrarMatrizAlea(E matriz:tMatriz, E finF, finC: entero)
(*Precondición: finF=F1 y finC=F2 y [1,F1] rango1(tMatriz) y [1,F2] 
rango2(tMatriz)y matriz={ Mij }  i  [1,F1] y  j  [1,F2]*)
(*Poscondición: se mantienen las precondiciones *)
Var
finF, finC
posF, posC: entero
Matriz
Inicio
Cliente
MostrarMatrizAlea
Repetir
posF <- enteroEnRango(“Ingrese fila”,1,finF)
posC <- enteroEnRango(“Ingrese columna”,1,finC)
Mostrar (matriz[posF,posC])
Hasta confirma(“finaliza?”)
Fin Proc
Verificar si una matriz cuadrada es simétrica
Se trata de una operación que pretende verificar que los elementos que están en posiciones i, j debajo
de la diagonal principal, son iguales a los que están en posiciones j,i sobre la diagonal principal. La
diagonal principal de una matriz cuadrada es aquella cuyos índices son iguales. Para tener la
oportunidad de que el algoritmo sirva para verificar una submatriz, se requiere conocer el principio y fin
del rango de celdas. Si asumiéramos que la submatriz siempre comienza desde la primer fila y columna,
el principio sería una constante y no se requeriría como parámetro. Dada que es una matriz cuadrada, el
rango de filas coincide con el rango de columnas.
Datos de entrada
Rango: principio y fin del rango de filas/columnas a verificar
Arreglo de datos: Matriz
Datos de salida:
Resultado de la operación: esSimétrica (lógico)
Primero, ultimo
Matriz
esSimetrica
Cliente
EsSimetrica
Como se requiere un único dato de salida de tipo simple que puede ser calculado en función de los datos
de entrada, esta operación conviene realizarse como una función. Supondremos que la matriz es
simétrica y si en el proceso de verificación de que cada elemento i,j sea igual al respectivo j,i fallara,
cortamos la verificación y devolvemos el valor falso.
Funcion EsSimetrica(E matriz:tMatriz, E princ, ultimo: entero): logico
Var
Precondición: princ=P y ultimo=F y P<=F y [P,F] 
i, j: entero
rango1(tMatriz) y rango1(tMatriz) = rango2(tMatriz)
verifica: logico
y matriz={Mij} i  [P,F] y j  [P,F])
Poscondición: (EsSimetrica = verdadero y (Mij=Mji
Inicio
verifica <- verdadero i  [P,F] y j  [P,F]) ) o EsSimetrica=falso
i <- princ +1
Mientras i<= ultimo y verifica hacer
j <- princ
Mientras j < i y verifica hacer
Si matriz[i,j] <> matriz[j,i] entonces
verifica <- falso
Fin Si
j <- j + 1
Fin Mientras
i <- i + 1
Fin Mientras
EsSimetrica <- verifica
Fin Func
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 63
Algorítmica y Programación I
Ejemplos de problemas que requiere la utilización de arreglos.
Problema 1: Recibir una lista de 100 valores enteros y mostrarla en orden inverso al ingresado.
Para poder mostrar los valores en orden inverso, primero debo tenerlos almacenados y la forma de
hacerlo es utilizando una estructura de datos de tipo arreglo unidimensional. Este algoritmo se puede
resolver con dos procedimientos: uno para el ingreso secuencial y otro para mostrarlo en orden inverso.
Para este último caso se puede utilizar la estructura de repetición desde el fin al principio con paso –1.
Problema 2: Recibir una lista de 100 valores enteros y mostrarla ordenada en orden numérico
ascendente.
Otra vez, para poder mostrar los valores en orden ascendente, primero debo tenerlos almacenados
utilizando una estructura de datos de tipo arreglo unidimensional. Este algoritmo se puede resolver con
tres procedimientos: uno para el ingreso secuencial, otro para producir el ordenamiento y otro para
mostrarlo.
Problema 3: Se desea un programa que reciba un texto y muestre la cantidad de veces que se repite
cada carácter en el texto.
El texto será almacenado en una variable de tipo cadena pero para almacenar la cantidad de apariciones
de cada carácter, necesitaremos un contador para cada uno. Por ello, la solución es tener un arreglo de
contadores, uno por cada carácter posible, inicializado en cero. Recorriendo una vez la cadena,
incrementaremos el contador correspondiente y al final sabremos cuánto se repitió cada carácter.
Aquellos contadores que aún están en cero, será porque el carácter que representan no se encontraba
en el texto.
Problema 4: Se desea un programa que permita realizar producto de matrices.
En este problema se requiere contar con un tipo de arreglo bidimensional lo suficientemente grande para
contener cualquier matriz y tres variables de este tipo para almacenar las matrices que se multiplicarán y
a la matriz resultado. Las dimensiones de las matrices serán conocidas en tiempo de ejecución, por lo
tanto se requieren variables adicionales para las mismas. Dado que el producto de matrices requiere que
las dimensiones de las matrices a multiplicar cumplan la condición que la matriz primera tenga tantas
columnas como filas tiene la segunda, deberá verificarse esta situación antes de intentar ingresarlas para
multiplicarlas. En este algoritmo se requerirán al menos tres procedimientos: uno para el ingreso
secuencial el cual será invocado dos veces, una para cada matriz a multiplicar, otro para producir el
producto y otro para mostrar el resultado, además de haber ingresado previamente las dimensiones de
las matrices y verificar que cumplan la condición requerida.
Cadenas de caracteres
Una cadena es un arreglo de caracteres que se caracteriza por tener una longitud. La longitud de una
cadena es el número de caracteres que posee. Cuando se define una cadena se establece un máximo
de capacidad, pero al asignarle un valor puede ser que no se ocupe toda esa capacidad, la función
longitud devuelve la cantidad realmente ocupada.
Puede ser utilizado como un tipo básico, definiendo variables directamente de tipo cadena, en este caso,
utiliza la máxima capacidad establecida en el lenguaje, la cual asumiremos que es de 255 caracteres. Si
queremos crear tipos de cadenas con menor capacidad, las definiremos en la sección tipo, dando un
nombre y limitando la cantidad, según la siguiente plantilla:
<nombreTipo> = cadena [<tamaño>]
Ejemplo: para definir un tipo de cadena para almacenar direcciones de hasta 50 caracteres:
tDirección = cadena[50]
Operaciones
Existen varias operaciones predefinidas para su uso con cadenas.
Concatenación: Dos cadenas se concatenan cuando se obtiene una tercera formada por los caracteres
de la primera y los de la segunda. El operador de concatenación es el símbolo +. También se puede
realizar con la función concatena(cad1, cad2).
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 64
Algorítmica y Programación I
Ejemplo:
Texto
<- "Hoy " + "consulto" + " dudas"
Texto2 <- concatena(texto, “ y luego estudio”)
Texto será una variable de tipo cadena que queda inicializada con “Hoy consulto dudas” y Texto2
quedará con el valor “Hoy consulto dudas y luego estudio”
Largo de una cadena: Long(cadena). Devuelve la longitud de la cadena.
Ejemplos:
Long("Juan") ->
4
Long("Día del estudiante") -> 18
Conversión de un número a una cadena: str(número). Devuelve una cadena que contiene el número
que le fue pasado como parámetro a la función, pero en formato alfanumérico.
Ejemplos:
str(108)
-> "108"
str(27.05) -> "27.05"
Conversión de una cadena a número: val(cadena). Devuelve el valor numérico de la cadena. Si la
cadena contiene caracteres no numéricos, la función devuelve 0.
Ejemplo:
val("8.73") -> 8.73
val("23")
->
23
val("2x3")
->
0
Extraer una subcadena: Subcadena(cadena,posición,long) Devuelve una cadena formada por “long”
cantidad de caracteres tomados de “cadena” a partir de “posición”. Si la longitud es mayor de la cantidad
de caracteres que restan, devuelve los que puede.
Ejemplo:
Subcadena("Programación",1,8) -> "Programa"
Subcadena("Estado del arte",7,3) -> "del"
Buscar una cadena dentro de otra: Buscar(cadenaFuente,cadena). Devuelve la posición a partir de
la cual se encuentra “cadena” dentro de “cadenaFuente”. Si “cadena” no es una subcadena de
“cadenaFuente”, la función devuelve el valor 0.
Ejemplo:
Buscar("Algoritmica y programación","programa") -> 15
Buscar("Algoritmica y programación","o") -> 4
Buscar("Algoritmica y programación","lenguaje") -> 0
Reemplazar una cadena dentro de otra: Reemplazar(cadenaFuente, cadBusca, cadReemplazo).
Devuelve la cadena que resulta de sustituir todas las apariciones de cadBusca en cadenaFuente por
cadReemplazo.
Ejemplo:
Reemplazar(“Estudio poco”,”poco”, “mucho”) -> “Estudio mucho”
Reemplazar (“Algoritmica”, “lenguaje”, “programa") -> “Algoritmica”
Insertar una cadena dentro de otra a partir de una cierta posición: insertar( cadenaFuente,
posición, cadena). Devuelve la cadena que resulta de insertar “cadena” a partir de “posición” en
“cadenaFuente”.
Ejemplo:
Insertar( “Luis Rodriguez”,6,"Maria ") -> “Luis Maria Rodriguez”
Comparación de Cadenas: Las cadenas pueden compararse haciendo uso de los operadores de
comparación. Internamente se comparan posición a posición. Dos cadenas serán iguales si todos los
caracteres coinciden y tienen igual longitud.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 65
Algorítmica y Programación I
Registros
Un registro es una estructura de datos que permite agrupar valores de tipo no homogéneo, los cuales
pueden identificarse a través del nombre del registro y el nombre del elemento en cuestión. Por ejemplo
si quisiéramos agrupar la información de un empleado, como el nro. de legajo, categoría y sueldo, no
podríamos hacerlo mediante un arreglo pues los datos no son homogéneos. En su lugar definimos un
registro con la siguiente plantilla:
<nombreTipo> = Registro
campo1: <tipo>
campo2: <tipo>
...
campoN: <tipo>
Fin registro
Para el ejemplo del empleado:
tipo
tEmpleado = Registro
legajo: entero
categoría: caracter
Legajo
Categoria sueldo
sueldo: real
Empleado
Fin registro
var
Empleado : tEmpleado
Para acceder a un elemento en particular, lo hacemos refiriéndonos con la variable de tipo registro,
seguida de un punto y el nombre del campo:
Empleado.Legajo <- 125
Empleado.categoria <- ‘A’
Empleado.sueldo <- 1807.5
125
A
1807.50
Legajo
Categorí
a
sueldo
Empleado
El tipo de datos de los elementos de un registro puede ser cualquier tipo conocido o previamente
definido, a excepción de uno de tipo archivo.
Un campo puede ser de tipo arreglo previamente definido o de otro tipo registro previamente definido,
por ejemplo si nos interesara agrupar los datos de un alumno en una estructura registro con la
información de su nombre, matricula, fecha de inscripción y lista de cinco notas:
Const
Max=5
Tipo
tNotas = arreglo [1..Max] de entero
tFecha = Registro
dia, mes, año: entero
Fin registro
tNombre = cadena[50]
tAlumno = Registro
nombre: tNombre
matricula: entero
fecha: tFecha
notas: tNotas
Fin registro
var
alumno : tAlumno
Para ingresar los datos del alumno haríamos:
Mostrar (“ingrese Nombre”)
Ingresar(alumno.nombre)
Mostrar (“ingrese matricula”)
nombre
matricula
fecha
notas
Alumno
Juan Perez 1234
nombre
12 | 6 | 04
5 7 9 8 6
dia mes año [1] [2] [3] [4 ] [5]
matricula fecha
notas
Alumno
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 66
Algorítmica y Programación I
Ingresar (alumno.matricula)
ObtenerFechaValida(“Fecha inscripcion”,alumno.fecha)
Desde i <- 1 hasta Max hacer
Mostrar (“Ingrese nota ”, i)
Ingresar (alumno.nota[i])
Fin Desde
Si queremos tener los datos de todos los alumnos de un curso, debiéramos definir un arreglo cuyos
elementos sean de tipo tAlumno:
Const
MaxAl=200
MaxNota=5
Tipo
tNotas = arreglo [1..MaxNota] de entero
tFecha = Registro
dia, mes, año: entero
[1]
Fin registro
[2]
tNombre = cadena[50]
[3]
...
tAlumno = Registro
[cantAl]
nombre: tNombre
....
matricula: entero
[199]
fecha: tFecha
[200]
notas: tNotas
Fin registro
Juan Perez
Ana Garcia
Jorge Adad
Luis Garcia
Pedro Gol
1234
1256
2145
2550
3210
12 3 04
12 5 03
21 5 04
25 2 03
32 1 04
5
6
5
8
9
7
8
6
5
3
nombre
matricula
fecha
notas
9
8
5
4
9
8
9
6
8
7
6
9
8
7
9
ListaAl
tListaAl = arreglo [1..MaxAl] de tAlumno
var
listaAl : tListaAl
al, i, cantAl: entero
Para mostrar los datos de los alumnos de un curso haríamos:
cantAl <- enteroEnRango(“Cantidad de alumnos”,1,MaxAL)
...
Desde al <-1 hasta cantAl hacer
Mostrar (“Nombre: ”,listaAl[al].nombre)
Mostrar (“matricula: ”,listaAl[al].matricula)
Mostrar (“Año Inscripción: ”, listaAl[al].fecha.año)
Desde i <- 1 hasta MaxNota hacer
Mostrar (“nota ”, i, “=”,listaAl[al].notas[i])
Fin Desde
Fin Desde
Observar: listaAl es un arreglo por lo que se requiere del índice para acceder a un elemento; listaAl[al]
es un registro por lo que se requiere del punto y el nombre del campo para acceder a un miembro;
lista[al].fecha es un registro por lo que requerimos del punto y el nombre del campo para acceder al año;
lista[al].fecha.año es un entero y se puede mostrar directamente; lista[al].notas es un arreglo y
lista[al].notas[i] es un entero que representa la nota i del alumno al.
Registros con variante
Supongamos que deseamos almacenar la información de clientes de un banco: nro. cliente, nombre, tipo
de cliente, crédito y que de acuerdo al tipo de cliente, hay datos adicionales o especiales necesarios para
un tipo pero no para otro. Por ejemplo, los clientes de tipo corporativo requieren la información de
cantidad de miembros y tipo de sociedad que los clientes de tipo individual el número de seguro social.
Hay campos comunes y campos especiales. Podríamos definir un registro con la totalidad de los
campos, algunos de los cuales estarían vacíos para los clientes individuales y otros lo estarían para los
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 67
Algorítmica y Programación I
corporativos. Es posible definir una variante de registro que minimice la cantidad de huecos, y dando el
aspecto que el registro varía su formato según el contenido de alguno de sus campos. Para ello, en la
definición del registro, hacemos:
<nombreTipo> = Registro
campo1: <tipo>
campo2: <tipo>
...
campoN: <tipo>
Según campoD hacer
variante1: <definiciones de campos d1>
variante2: <definiciones de campos d1>
Fin Segun
Fin registro
donde campoD es el campo por el cual se discrimina la conformación del registro y variante1,
variante2 son los valores que puede tomar campoD. Las definiciones de campos d1 o d2, serán las
necesarias para el problema, en la forma <nombreCampo>: <tipo>
Los campos de la variante se acceden de igual forma que los restantes, a través del nombre del registro
y del campo.
El sistema guarda almacenamiento para la parte común más el tamaño de la variante más grande, de
esta forma cada variante tendrá lugar suficiente para sus campos. En tiempo de ejecución, y según el
valor que contenga el campo discriminante, sabrá qué campos son los que realmente están
almacenados.
Ejemplo:
Tipo
tTipoCliente
tSociedad
tNombre
tCliente
=
=
=
=
(corporativo, individual)
(anonima,respLim, ...)
Cadena[50]
Registro
nroCli: entero
nombre: tNombre
tipoCli: tTipoCliente
credito: puntoFijo(2)
Según tipoCli hacer
corporativo: cantMiembros: entero
tipoSoc: tSociedad
individual: NroSS: entero
Fin Segun
Fin registro
Procedimiento mostrarReg(E cliente: tCliente)
Inicio
Mostrar( “Cliente nro.:”, cliente.nroCli)
Mostrar( “Nombre:”, cliente.nombre)
Si cliente.tipoCli = corporativo entonces
Mostrar( “tipo corporativo”)
Mostrar( “cantidad Miembros:”, cliente.cantMiembros)
Sino
Mostrar( “tipo individual”)
Mostrar( “Nro SS:”, cliente.nroSS)
Fin Si
Mostrar( “Crédito:”, cliente.credito)
Fin Proc
Formato del Registro
NroCli
Nombre
TipoCli
Credito
¿?
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 68
Algorítmica y Programación I
Con Variante 1
321
NroCli
Juan Perez
Nombre
Corporativo 2500,56
TipoCli
Credito
5
cantMiembros
Con variante 2
256
NroCli
Luis Gracia
Nombre
Individual
TipoCli
2563
nroSS
1200.50
Credito
anonima
tipoSoc
Conjuntos
Es una colección homogénea de elementos, sin repetición y sin relación de orden entre ellos. Desde el
punto de vista informático, los elementos son todos de un mismo tipo simple y enumerativo: carácter,
entero o enumerado creado por el usuario.
Para definirlo es necesario indicar cuál es el tipo base. La cantidad de elementos, es matemáticamente
infinita, y cada lenguaje la acota a un número máximo de elementos, que en Pascal es 255.
La definición de tipos conjunto se realizan con la siguiente plantilla:
<nombreTipo> = conjunto de <tipo_base>
Ejemplo:
Tipo
TCjtoLetras = conjunto de carácter
Var
MisLetras: tCjtoLetras
Construcción de un conjunto
El conjunto se construye escribiendo los elementos individuales consecutivamente, encerrados entre
corchetes y separados por comas:
[elem1, elem2, ..., elemN]
También puede hacerse, indicando el subrango de elementos, en lugar de hacerlo por extensión. Así
por ejemplo, para asignar a misLetras el conjunto de las vocales haría:
MisLetras <- [‘a’, ‘e’, ‘i’, ‘o’, ‘u’]
Y para asignar a misLetras el conjunto de caracteres minúscula haría:
MisLetras <- [‘a’..‘z’]
El conjunto vacío
Se representa por corchete de apertura seguido del corchete de cierre: [] es el conjunto vacío o nulo.
Operaciones sobre datos de tipo conjunto
Operación
Operador Tipo de Resultado
Ejemplo
Unión
+
Conjunto
[1,2] + [2,0,8] -> [1,2,0,8]
Intersección
*
Conjunto
[1,2] * [2,0,8] -> [2]
Diferencia
-
Conjunto
[1,2]-[2,0,8] -> [1]
Pertenencia
en
Lógico
1 en [1,2] -> verdadero
Igualdad
=
Lógico
[1,2] = [2,0,8] -> falso
Distinto
<>
Lógico
[1,2] <> [2,0,8] -> verdadero
Incluye a
>=
Lógico
[1,3] >= [2] -> falso
Incluido en
<=
Lógico
[1,2] <= [1,2,0 -> verdadero
Cantidad de Elementos
#
entero
#[1,2] -> 2
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 69
Algorítmica y Programación I
Archivos
La noción de Archivo está relacionada con los conceptos de:
o
o
o
almacenamiento permanente de datos
Partición de grandes volúmenes de información en unidades más pequeñas que pueden ser
almacenadas en memoria principal, y procesadas por un programa.
Almacenamiento de datos independientemente de la ejecución de un programa.
Un archivo es un conjunto de datos relacionados almacenados en un medio externo (a la memoria
central). Estos datos están estructurados en una colección de entidades elementales llamadas
genéricamente registros. Cada registro se puede identificar por un campo o una combinación de campos
que lo haga único. Esta identificación se reconoce con el nombre de clave. Por ejemplo, en un archivo
de datos personales el campo del documento puede ser un campo clave.
Estructuras de datos en memoria principal
Ventajas
o
o
Pequeño tiempo de acceso
El tiempo necesario para acceder a los datos es independiente de la posición en que se
encuentra.
Desventajas
o
o
o
Falta de persistencia: Los datos existen mientras se encuentra en ejecución el módulo en que
fueron definidos. Al terminar, o al interrumpirse la energía, los datos desaparecen.
Capacidad bastante limitada. Grandes volúmenes de información pueden no caber en memoria
principal.
Para solucionar estas dificultades se necesitan los dispositivos secundarios como discos, cintas,
etc., donde se almacenará la información que podrá ser recuperada para su tratamiento
posterior.
Clasificación de archivos
Existen muchas maneras de clasificar los archivos, en particular nos interesa distinguir la manera en que
un programa accede al archivo:
Archivo de entrada: El programa sólo puede recuperar la información pero no puede modificarla ni
agregar datos. Un programa fuente es un archivo de entrada para un compilador. Un documento es un
archivo de entrada para un listado.
Archivo de salida: El programa sólo puede escribir. Un archivo de código objeto es un archivo de salida
de un programa compilador. Un documento es un archivo de salida de un programa editor de texto.
Archivo de entrada/salida: El programa puede leer y escribir. Puede crearse en una fase del programa
y modificarse en otra.
Organización de archivos.
La organización de archivos se refiere a la forma en que están estructurados los datos en un archivo:
Organización secuencial: Los registros son almacenados consecutivamente, de tal modo que cada registro excepto el primero tiene otro
que le precede, y, cada registro, excepto el último tiene otro que le
sigue. El orden físico con que fueron grabados es el orden de lectura o
de acceso a los mismos.
principio
registro 1
registro 2
registro n –1
fin de archivo
registro n
Para acceder a un registro n, se tiene que acceder primero a los n-1 registros anteriores. Todos los
dispositivos de almacenamiento masivo permiten esta organización, algunos, como las lectoras y
grabadoras de cinta magnéticas sólo permiten la organización secuencial. Los archivos de texto son de
organización secuencial.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 70
Algorítmica y Programación I
Organización secuencial con índice: Los registros están en posiciones consecutivas pero se puede
acceder mediante un camino muy corto casi directo, a través de un índice, semejante al índice alfabético
de un libro. Esta organización requiere de dos archivos, el de datos, igual que en la organización
secuencial, y el de índices que es una tabla con dos campos: uno que contiene el campo clave que
identifica al registro, y el otro que tiene la dirección en que el registro se encuentra realmente. El archivo
de índices se mantiene ordenado respecto de la clave, lo cual permite recuperar los datos en orden al
campo clave y facilita la tarea de recuperar un dato en particular.
Área de Índices
clave
posición
15
23
56
80
100
2
0
4
3
1
Área de Datos
clave
resto de los datos
23
100
15
80
56
Organización relativa: El orden físico no tiene por qué corresponderse con el orden lógico. Los datos se
sitúan en el archivo y se accede a ellos directamente mediante su posición, o lugar relativo que ocupan.
La relación entre el registro y la posición se determina por alguna fórmula. Se puede acceder a un
registro sin necesidad de acceder a todos los anteriores. El archivo puede llegar a contener “huecos”.
Modos de acceso a los registros de un archivo
Acceso secuencial: para acceder a un registro debo acceder primero todos los registros anteriores. Los
archivos de texto tienen organización y acceso secuencial. Todos los archivos almacenados en
dispositivos secuenciales como unidades de cinta, sólo pueden accederse secuencialmente.
Acceso directo: puedo acceder a un registro directamente sin necesidad de pasar por todos los
anteriores. El archivo debe estar almacenado en un dispositivo direccionable. En una organización
secuencial con registros de igual tamaño, puede llegar a darse un acceso directo. En la organización
relativa, el acceso es directo. La organización secuencial con índice permite un acceso directo al archivo
de datos, previa ubicación de la clave a recuperar, en general, se accede secuencialmente al de claves y
directamente al de datos.
Operaciones sobre archivos
o
Creación / acceso
o
Actualización
o
o
o
Inserción de registros
modificación de registros
supresión de registros
Como consecuencia de la actualización el archivo cambia su contenido. Si el archivo sólo permite
acceso secuencial o es de sólo entrada, la actualización se logra creando un nuevo archivo a partir
del existente.
Para suprimir un registro se necesita crear un nuevo archivo con todos los registros excepto los que
se desean eliminar. Para evitar esto, se suele trabajar con el criterio de bajas lógicas, donde un
campo del registro indica si está activo o no. De esta manera, si el registro que se recupera tiene la
marca de baja (o de no estar activo), puede ser ignorado.
o
o
Recuperación: el archivo mantiene su contenido
o Consulta de uno o más registros.
o Listado de un grupo o de todos los registros.
Mantenimiento
o estructuración
o reorganización
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 71
Algorítmica y Programación I
Entorno de programación mínimo para trabajar con archivos secuenciales.
Un entorno de programación deberá proveer alguna forma de estructura de datos para archivos y un
conjunto de operaciones elementales que permitan como mínimo:
o
o
o
crear un archivo vacío
acceder a un archivo existente
recuperar o leer un registro del archivo
o
o
o
grabar o escribir un registro en el
archivo
saber si se llegó al fin de archivo.
cerrar la conexión con el archivo.
Con seguridad, un lenguaje que me permita trabajar con archivos proveerá esas operaciones y
posiblemente algunas más. Los parámetros, nombre y forma de las operaciones dependerán de cada
lenguaje.
Archivos de texto
Son archivos secuenciales cuyos elementos son caracteres organizados en líneas de texto. Para facilitar
su manejo, los lenguajes cuentan con operaciones y tipos de datos especiales.
Para ello vamos a definir un nuevo tipo que llamaremos tTexto y que permitirá identificar variables para
referirse a archivos de texto. Los elementos de un archivo de texto son caracteres, y por lo tanto puedo
utilizar variables de tipo carácter o de tipo cadena.
Var
miArchivo: tTexto
(* variable de memoria con que voy a referenciar a mi archivo *)
s: cadena/ caracter (* variable de memoria con que voy a referenciar un elemento*)
No debemos confundir la variable archivo con el archivo de datos. La primera es una estructura de datos
que tiene la información necesaria para vincularse y acceder al archivo físico que contiene los datos.
Operaciones que permiten conectarse con el dispositivo externo, creando un nuevo
archivo o utilizando un archivo existente
Crear(varArch, nombArch): Crea un archivo vacío con el nombre indicado en nombArch (de tipo cadena)
e inicializa la variable varArch (de tipo archivo tTexto) con la información necesaria para poder vincularse
con el archivo físico externo. Si existe un archivo con el mismo nombre lo destruye, lo pone a cero.
varArch es un parámetro de salida y nombArch es un parámetro de entrada. La operación crear inicializa
a varArch con la información necesaria.
Ej: Crear (miArchivo, "Memorias.txt”)
Abrir (varArch, nombArch) Abre el archivo con el nombre indicado en nombArch, se posiciona al
principio del archivo e inicializa la variable varArch con la información necesaria para poder vincularse
con el archivo físico externo. Si no existe un archivo con ese nombre da un error.
Ej: Abrir (miArchivo, "Memorias.txt”)
Anexar (varArch, nombArch) Si existe un archivo con el nombre indicado en nombArch, lo abre y se
posiciona al final del archivo, si no existe el archivo, lo crea. En cualquiera de los dos casos inicializa la
variable varArch con la información necesaria para poder vincularse con el archivo físico externo y queda
posicionado al final del archivo, de modo tal que puedan agregarse datos.
Ej: Anexar (miArchivo, "Memorias.dat”)
En los tres casos, varArch es un parámetro de salida que queda con la información necesaria para poder
vincularse con el archivo físico externo (descriptor del archivo, información de estado, si utiliza o no
buffer, posición dentro del archivo, etc.) y nomArch es un parámetro de entrada, es el nombre del archivo
existente o a crear.
Operaciones que permiten recuperar un elemento
Leer (varArch, s) Recupera un caracter si el parámetro s es de tipo caracter o los caracteres de una línea
que quepan en s si s es una cadena. Avanza el puntero del archivo al comienzo del próximo caracter no
leído. Querer leer después del último caracter da error. En general se utiliza con un s de tipo caracter.
Ej: Leer(miArchivo, s)
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 72
Algorítmica y Programación I
LeerLN (varArch, s) Recupera la línea de texto apuntada en ese momento del archivo referenciado a
través de varArch, lo copia sobre el segundo parámetro (que tiene que ser de tipo cadena o de tipo
caracter) y avanza el puntero del archivo al comienzo de la siguiente línea. Los caracteres que no se
pueden almacenar en el parámetro s se ignoran. Querer leer después del último caracter da error. En
general se utiliza con un s de tipo cadena suficientemente grande como para almacenar la línea de texto.
Ej: LeerLN (miArchivo, s)
En los dos casos, varArch es un parámetro de entrada-salida: tiene la información necesaria para poder
vincularse con el archivo físico externo (descriptor del archivo, información de estado, si utiliza o no
buffer, posición dentro del archivo, etc.) y actualiza su estado luego de acceder y recuperar un elemento
del archivo físico, y s es un parámetro de salida que quedará inicializado con el valor del elemento
recuperado.
Operaciones que permiten agregar un elemento al archivo
Escribir (varArch, s) Graba o escribe el contenido de s en la posición actual del archivo referenciado a
través de varArch, avanza el puntero al final de lo escrito (quedando siempre en el final).
Ej: Escribir (miArchivo, s)
EscribirLN (varArch, s) Graba o escribe el contenido de s más un caracter de nueva línea. Avanza el
puntero al final de lo escrito.
Ej: EscribirLN (miArchivo, s)
En los dos casos, varArch es un parámetro de entrada-salida: tiene la información necesaria para poder
vincularse con el archivo físico externo (descriptor del archivo, información de estado, si utiliza o no
buffer, posición dentro del archivo, etc.) y actualiza su estado luego de acceder y agregar un elemento al
archivo físico, y s es un parámetro de entrada con el valor del elemento a agregar.
Operaciones de consulta de estado
esFinLinea (varArch) Retoma verdadero Si se llegó a un final de línea, falso sino.
Ej:
Si esFinLinea(miArchivo) entonces
...
Fin Si
esFinArchivo (varArch) o eof(varArch) Retoma verdadero Si se llegó al final del archivo, falso sino. Se lo
consulta normalmente antes de leer.
Ej:
Si no esFinArchivo(miArchivo) entonces
Leer (miArchivo, s)
Fin Si
Operaciones de cierre
Cerrar (varArch) Completa las operaciones que pudieron quedar pendientes de actualizar en el archivo
físico y desestablece la conexión entre la variable de memoria y el dispositivo. Cualquier operación sobre
ese archivo que se hiciera después de cerrar, produce error (a excepción de abrir o anexar).
Ej: Cerrar(miArchivo)
Ejemplos de algoritmos con archivo de texto
Agregar datos a un archivo existente
Datos de entrada: Nombre del archivo existente al que se
le agregará datos.
Datos de salida: Los cambios se producen sobre el
archivo. No hay datos de salida mediante los parámetros.
nombre
Cliente
AgregarDatos
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 73
Algorítmica y Programación I
Nota: El algoritmo abre el archivo para anexar y al finalizar lo cierra.
Procedimiento AgregarDatos(E nombre:cadena)
(* agrega datos al archivo nombre, si el archivo no existe lo crea*)
(* precondición: nombre = N
poscondición: existe un archivo de nombre N y N tiene 1 o más lineas
de texto
*)
var
arch:tTexto
s: cadena
Inicio
Anexar(arch,nombre)
Mostrar (“Ingrese lineas de texto”)
Repetir
Ingresar(s)
EscribirLN( arch,s)
hasta confirma(“Finaliza?”)
Cerrar(arch)
Fin Proc
nomb1, nomb2
Anexar los datos de un archivo a otro
Cliente
copiarDatos
Datos de entrada: Nombre del archivo existente del
que se toman los datos: nomb1. Nombre del archivo sobre el que se anexan los datos: nomb2.
Datos de salida: Los cambios se producen sobre el archivo nomb2. No hay datos de salida
mediante los parámetros.
Nota: El algoritmo abre los archivos y al finalizar los cierra. El archivo nomb1 debe existir.
Procedimiento CopiaDatos(E nomb1, nomb2:cadena)
(* copia los datos del archivo1 al final del archivo2
Precondición: nomb1 = N1 y nomb2= N2 y el archivo N1 debe existir
Poscondición: el archivo N2 contiene sus datos originales más los datos
del archivo N1 *)
var
arch1,arch2:tTexto
c: caracter
Inicio
Abrir(arch1,nomb1)
Anexar(arch2,nomb2)
Mientras ( no eof(arch1) hacer
Leer (arch1, c)
Escribir (arch2,c)
Fin Mientras
Cerrar(arch1)
Cerrar(arch2)
Fin Proc
Archivos de un determinado tipo de dato
Contamos con el constructor archivo para crear nuestros propios tipos de archivos, y de manera similar
al constructor arreglo le indicamos de qué tipo son sus elementos.
Ejemplo:
tipo
tReg= registro
(*descripción del tipo del elemento *)
x,y: real
h: real
Fin registro
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 74
Algorítmica y Programación I
tArch= archivo de tReg
Var
miArchivo: tArch
(*descripción del tipo de archivo *)
Operaciones que permiten conectarse con el dispositivo externo, creando un nuevo
archivo o utilizando un archivo existente
Crear(varArch, nombArch): Crea un archivo vacío con el nombre indicado en nombArch (de tipo cadena)
e inicializa la variable varArch (de tipo archivo) con la información necesaria para poder vincularse con el
archivo físico externo. Si existe un archivo con el mismo nombre lo destruye, lo pone a cero. Es la misma
que para archivos de texto.
Ej: Crear (miArchivo, "Mediciones. dat”)
Abrir (varArch, nombArch) Abre el archivo con el nombre indicado en nombArch, se posiciona al
principio del archivo e inicializa la variable varArch con la información necesaria para poder vincularse
con el archivo físico externo. Si no existe un archivo con ese nombre da un error. Es la misma que para
archivos de texto.
Ej: Abrir (miArchivo, "Mediciones. dat”)
En los dos casos, varArch es un parámetro de salida que queda con la información necesaria para poder
vincularse con el archivo físico externo (descriptor del archivo, información de estado, si utiliza buffer,
posición dentro del archivo, etc.) y nomArch es un parámetro de entrada, es el nombre del archivo.
Operación que permite recuperar un elemento
Leer (varArch, reg) Recupera el registro apuntando en ese momento del archivo referenciado a través de
varArch, lo copia sobre el segundo parámetro (que tiene que ser del mismo tipo que los elementos del
archivo) y avanza el puntero del archivo al siguiente elemento. Querer leer después del último registro
produce error.
Ej: Leer (miArchivo, reg)
VarArch es un parámetro de entrada-salida: tiene la información necesaria para poder vincularse con el
archivo físico externo (descriptor del archivo, información de estado, si utiliza o no buffer, posición dentro
del archivo, etc.) y actualiza su estado luego de acceder y recuperar un elemento del archivo físico, y reg
es un parámetro de salida que quedará inicializado con el valor del elemento recuperado.
Operación que permiten agregar un elemento al archivo
Escribir (varArch, reg) Graba o escribe el contenido de reg en la posición actual del archivo referenciado
a través de varArch y avanza el puntero del archivo al siguiente elemento o posición.
Ej: Escribir (miArchivo, reg)
Operaciones de consulta de estado
esFinArchivo (varArch) o eof(varArch) Retoma verdadero Si se llegó al final del archivo, falso sino. Se
lo consulta normalmente antes de leer.
Ej: Si no esFinArchivo(miArchivo) entonces
Leer (miArchivo, reg)
Fin Si
Operaciones de cierre
Cerrar (varArch) Completa las operaciones que pudieron quedar pendientes de actualizar en el archivo
físico y desestablece la conexión entre la variable de memoria y el dispositivo. Cualquier operación sobre
ese archivo que se hiciera después de cerrar, produce error (a excepción de abrir).
Ej: Cerrar(miArchivo)
Operaciones que permiten acceso directo
Aunque estén organizados en forma secuencial, dado que sus elementos son todos de un mismo tipo, y
por lo tanto del mismo tamaño, es posible calcular donde se encuentra el i-ésimo elemento. Para permitir
acceso directo a una posición del archivo, sin tener que recorrer las anteriores, en general los lenguajes
proveen por lo menos las siguientes primitivas, que se suman a las descriptas anteriormente, y no están
disponibles para archivos de texto:
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 75
Algorítmica y Programación I
Posicionarse (varArch, pos) Ubica el puntero del archivo en la posición pos relativa al principio.
Ej: Posicionarse (miArchivo, 2)
(*se posiciona al final del segundo registro o comienzo del tercero, ya que las posiciones se cuentan
desde 0.*)
DóndeEstoy(varArch): entero Devuelve la posición relativa en que se encuentra ubicado el puntero del
archivo.
Tamaño(varArch): entero Devuelve la cantidad de elementos que tiene el archivo referenciado por
varArch.
Otras operaciones útiles para cualquier tipo de archivo
ExisteArchivo(nombre):lógico. Retoma verdadero si existe un archivo con ese nombre. Nombre puede
tener el path completo. Ej:
Si ExisteArchivo(s) entonces
abrir (miArchivo, s)
Fin Si
BorrarArchivo(nombre): Elimina el archivo con ese nombre. El archivo debe estar cerrado.
RenombrarArchivo(nombre1, nombre2): Cambia el nombre del archivo nombre1 con nombre2
Ejemplos de algoritmos con archivos de datos de un tipo determinado
tipos
tReg= registro
(*descripción del tipo del elemento *)
clave: entero
...
Fin registro
tArch= archivo de tReg (*descripción del tipo de archivo *)
Agregar datos a un archivo existente
Datos de entrada: Nombre del archivo existente al que se le agregará datos.
Datos de salida: Los cambios se producen sobre el archivo. No hay datos de salida mediante los
parámetros.
Nota: El archivo debe existir. El algoritmo abre y cierra el archivo.
Procedimiento AgregarDatos(E nombre:cadena)
(* agrega datos al final del archivo nombre
Precondición: nombre= N y Debe existir un archivo N
Poscondicion El archivo N tiene uno o más registros*)
var
nombre
arch: tArch
reg: tReg
Inicio
Abrir(arch, nombre)
Cliente
Posicionarse(arch, tamaño(arch))
Mostrar (“Ingreso de datos”)
Repetir
LlenarReg(reg) //llena reg con datos ingresados por el usuario
Escribir (arch, reg)
hasta confirma(“¿desea terminar?”)
Cerrar(arch)
Fin Proc
agregarDatos
Copiar datos de un archivo existente sobre el final de otro.
Datos de entrada: Nombre de los archivos: nomb1 y
nomb2 (los datos de nomb1 se copian al final de los
datos de nomb2).
nomb1, nomb2
Cliente
copiarDatos
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 76
Algorítmica y Programación I
Datos de salida: Los cambios se producen sobre el archivo nomb2. No hay datos de salida
mediante los parámetros.
Nota: Los archivos deben existir. El algoritmo abre y cierra cada archivo.
Procedimiento copiaDatos(E nomb1,nomb2:cadena)
(* copia los datos del archivo1 al final del archivo2
Precond: nomb1=N1 y nomb2=N2 y deben existir los archivos N1 y N2
Poscond: tamaño de N2 >= tamaño de N1 y los datos de N1 están en N2 *)
var
arch1,arch2: tArch
reg: tReg
Inicio
Abrir(arch1,nomb1) //es necesario que existan ambos arch
Abrir(arch2,nomb2)
Posicionarse(arch2, tamaño(arch2))
Mientras ( no eof(arch1) ) hacer
Leer (arch1, reg)
Escribir (arch2, reg)
Fin Mientras
Cerrar(arch1)
Cerrar(arch2)
Fin Proc
Crear un archivo con los datos de un archivo existente que cumplen una cierta condición
Crear un archivo nomb2 con los datos del archivo nomb1 que cumplen la condición, nroEmp >
1000
Datos de entrada: Nombre del archivo origen: nomb1 y
nomb1,
nombre del archivo a crear: nomb2. Valor de la condición
para seleccionar qué registros se grabarán en nomb2.
nomb2, tope
Datos de salida: Los cambios se producen sobre el
Cliente
seleccionaDatos
archivo nomb2. No hay datos de salida mediante los
parámetros.
Nota: El archivo nomb1 debe existir. El algoritmo abre nomb1 y crea nomb2. Al finalizar cierra
cada archivo.
Procedimiento seleccionaDatos(E nomb1,nomb2:cadena, E tope:entero)
(* Crea un archivo nomb2 con los datos del archivo nomb1 que cumplen la
condición, nroEmp > tope .
Precondición: nomb1=N1 y nomb2=N2 y el archivo N1 debe existir y tope =
T >0
Poscondicion: El archivo N2 tiene 0 o más registros y el nroEmp de cada
registro es > T*)
var
arch1,arch2: tArch
reg: tReg
Inicio
Abrir (arch1,nomb1)
Crear (arch2,nomb2)
Mientras ( no eof(arch1) ) hacer
Leer (arch1, reg)
Si reg.nroEmp > tope entonces
Escribir (arch2, reg)
Fin Si
Fin Mientras
Cerrar(arch1)
Cerrar(arch2)
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 77
Algorítmica y Programación I
Fin Proc
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 78
Algorítmica y Programación I
Modificar datos de un archivo
Modificar el precio de cada registro del archivo nomb1, multiplicándolo por cierto factor.
Datos de entrada: Nombre del archivo origen: nomb1.
nomb1
Factor por el que se incrementa el precio.
factor
Datos de salida: Los cambios se producen sobre el
archivo nomb1. No hay datos de salida mediante los
Cliente
modificaDatos
parámetros.
Nota: El archivo nomb1 debe existir, y al finalizar queda modificado. El algoritmo abre y cierra al
archivo nomb1.
Procedimiento ModificaDatos(E nomb1:cadena, E factor: real)
(* Modifica el precio de cada registro del archivo nomb1, multiplicandolo
por el factor
Precondición: nomb1=N1 el archivo N1 debe existir
Poscondicion: El archivo N1 es modificado: el precio de cada registro es
llevado al valor precio * factor *)
var
arch: tArch
reg: tReg
pos: entero
Inicio
Abrir (arch,nomb1)
Mientras ( no eof(arch) ) hacer
posdondeEstoy(arch)
Leer (arch, reg)
reg.precioreg.precio * factor (* modifico reg *)
Posicionarse(arch, pos)
Escribir (arch, reg)
Fin Mientras
Cerrar(arch)
Fin Proc
Insertar un registro en orden de su clave
Se dispone de un archivo ya abierto y ordenado por el campo clave y de un registro con datos que se
pretende insertar en orden.
Datos de entrada: archivo abierto: arch. Registro de
datos a insertar: regNuevo
Datos de salida: archivo abierto: arch.
Nota: arch debe estar abierto y ordenado por su campo
clave. La clave del registro regNuevo no debe existir
previamente en arch.
regNuevo
arch
Cliente
insertaReg
Se deberá hacer lugar corriendo hacia el final los registros cuya clave sea superior. Puede darse que el
registro deba insertarse al final, en medio o al principio. Nos ubicaremos al final del archivo, leeremos el
último registro y lo comparamos con el nuevo, si nuevo es mayor, grabamos y finalizamos, si no,
grabamos el recién leído en la posición siguiente y leemos el anterior y comparamos, repitiendo el
proceso hasta que o encontramos que nuevo es mayor o que no queda uno anterior. En tal caso,
grabamos en la posición siguiente de la que acabamos de recuperar. Usaremos una bandera
encontrePos para indicar si encontré o no la posición donde insertarlo.
Procedimiento InsertaReg(E/S arch:tArch, E regNuevo: tReg)
(* Inserta regNuevo en arch, respetando el orden de la clave
Precondición: arch=F y F está abierto y ordenado y regNuevo=N y N.clave
no pertenece a F
Poscondicion: El archivo F está ordenado y N.clave pertenece a F *)
var
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 79
Algorítmica y Programación I
reg: tReg
pos: entero
encontrePos: logico
Inicio
Pos <- tamañoArch(arch) –1 // posición del ultimo registro
EncontrePos <- falso
Mientras ( pos >= 0 y no encontrePos ) hacer
Posicionarse(arch, pos)
Leer (arch, reg)
Si reg.clave > regNuevo.clave entonces
Escribir (arch, reg)
pos <- pos - 1
Sino
encontrePos <- verdadero
Fin Si
Fin Mientras
Posicionarse(arch, pos + 1)
Escribir (arch, regNuevo)
Fin Proc
Actualización de un archivo
Es el proceso por el cual se realizan altas, bajas o modificaciones al archivo. Cada registro del archivo es
identificado de una manera única por una clave. La clave es un campo o conjuntos de campos cuyo
valor o combinación de valores es única. La clave puede ser impuesta por la aplicación, por ejemplo el
nro. de documento de una persona, o creada con algún mecanismo de generación automática (auto
numerado y no repetible)
Un alta implica la inserción de un nuevo registro, por ello la clave del nuevo registro no puede
encontrarse entre las claves de los registros existentes. Una baja es la eliminación de un registro para lo
cual se lo debe ubicar a través de su clave. Una modificación implica un cambio en algún campo del
registro, para ello se ubica el registro a modificar a través de su clave. Para dar de baja o modificar un
registro es necesario que la clave que lo identifica se encuentre entre las claves de los registros
existentes.
Existen dos maneras de actualizar un archivo:
Creando un nuevo archivo: parto del archivo maestro con la información original y otro archivo ABM
con indicaciones de las operaciones a realizar: A=alta, B=baja, M=modificación y los datos asociados a
la operación. Ambos archivos deben estar ordenados por su clave.
El nuevo archivo contendrá:
o
o
o
todos los datos del maestro cuyas claves no figuran en el ABM,
todos los datos del ABM que son altas y cuyas claves no figuran en el maestro, y
todos los registros del ABM que están indicados como modificación y cuyas claves figuraban en
el maestro.
Maestro
ABM
M.clave
Actualización
Nuevo
Maestro
? ABM.clave
<
Precondiciones:
Maestro y ABM están ordenados por
campo clave.
Dentro de cada archivo, no hay registros
repetidos con igual clave.
grabo el registro M y leo del maestro
= operación ?
>
‘A’: error, grabo de M y leo de ambos archivos
‘B’: leo de ambos archivos (no grabo ninguno)
‘M’: grabo el registro de ABM. Leo de ambos.
operación ?
‘A’: grabo el registro de ABM. Leo de ABM.
‘B’,’M’: error y leo de ABM
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 80
Algorítmica y Programación I
Son casos de error las siguientes situaciones:
o Un alta con clave existente en el maestro
o Una baja con clave inexistente en el maestro
o Una modificación con clave inexistente en el maestro
El proceso se realiza en forma semejante al correspondiente a una fusión: mientras hay elementos a
procesar en ambos archivos, comparo sus claves y tomo decisiones de cual incorporar en el archivo
resultante, teniendo en cuenta lo descripto anteriormente en cuanto a lo que debe contener y a los casos
de error. Cuando quedan elementos de un solo archivo, si son del maestro, los agrego a todos, pero si
son del ABM, sólo agrego aquellos que están marcados como altas, y registro como error a los marcados
como baja o modificación.
Modificando el mismo archivo: En este caso es necesario trabajar con bajas lógicas: el registro de
datos, además de los campos necesarios para la información asociada al problema, deberá contener un
campo adicional de tipo lógico que sirva para indicar si está o no activo el registro.
V: el registro está activo
clave
Restantes datos
activo: lógico
F: está dado de baja
Para dar un alta deberá comprobarse que la clave del nuevo registro no figura en el archivo y luego
inserta el registro en orden (corriendo hacia el final todos los registros mayores que su clave si es que
estaban ordenados).
Para dar una baja, se ubica el registro cuya clave se quiere dar de baja, se lo recupera, se modifica el
campo de indicación de activo a no activo y se lo vuelve a grabar en el mismo lugar.
Para hacer una modificación, se ubica (por su clave) y se recupera el registro que se desea modificar,
se realiza la modificación y se lo vuelve a grabar en el mismo lugar.
Son casos de error las siguientes situaciones:
o Un alta con clave existente en el maestro: existe y está activa. Si existe y está inactiva, no es un
error, pues se trata de una baja lógica y por lo tanto el registro y su clave puede reutilizarse para
el alta. En este caso, no se inserta corriendo los elementos, sino que se sobrescribe y se lo
activa.
o Una baja con clave inexistente en el maestro: No existe o existe y está inactiva.
o Una modificación con clave inexistente en el maestro: No existe o existe y está inactiva.
Cuando la clave es auto generada, y no hay ninguna otra restricción para agregar un elemento al
archivo, el proceso de alta no debiera producir error por clave repetida, a menos que falle el mecanismo
de autogeneración. Un mecanismo podría ser generar el número siguiente a la clave más alta. En este
caso, el nuevo registro quedaría ubicado siempre al final del archivo.
Ejemplo de actualización en línea
Dispondremos de operaciones para dar altas, bajas, hacer consultas o modificaciones que reciben el
nombre del archivo y se encargan de abrirlo, procesarlo y cerrarlo. Supondremos que el registro tiene un
campo lógico llamado activo, que indica si está en actividad o dado de baja.
Asumiremos que contamos con una operación BuscarClave (arch, clave, pos, reg) que
busca la clave en el archivo arch ya abierto y si la encuentra devuelve su posición en pos y el registro
que la contiene en reg, si no devuelve el valor –1 en pos para indicar que no la encontró.
Procedimiento Alta (E nomb: Cadena)
Var
Precondición: nomb=N y el archivo N
reg: tReg
existe y está ordenado por su clave
arch: tArch
PosCondición: el archivo N tiene 1 o
pos: entero
más datos ordenados por su clave
Inicio
Abrir(arch, nomb)
repetir
PedirClave( reg.clave) // pide datos del campo clave
BuscarClave(arch, reg.clave, pos, reg)
Si pos >= 0 entonces
//fue encontrado un registro con esa clave
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 81
Algorítmica y Programación I
Si reg.activo entonces // ya existe, es un error dar de alta
Mostrar(“ERROR alta....”)
Sino
// reutilizo el registro para volver a incorporalo
PedirRestoDatos(reg)
Posicionarse(arch, pos)
Escribir(arch, reg)
Fin Si
Sino // no se encontraba esa clave, lo inserto
PedirRestoDatos(reg)
InsertaReg(arch, reg)
Fin Si
Hasta confirma(“Finaliza?”)
Cerrar(arch)
Fin Proc
Procedimiento Baja (E nomb: Cadena)
Var
Precondición: nomb=N y el archivo N
reg: tReg
existe y está ordenado por su clave
arch: tArch
PosCondición: el archivo N tiene 1 o
pos: entero
menos datos ordenados por su clave
Inicio
Abrir(arch, nomb)
repetir
PedirClave( reg.clave) // pide datos del campo clave
BuscarClave(arch, reg.clave, pos, reg)
Si pos >= 0 entonces
//fue encontrado un registro con esa clave
Si no reg.activo entonces //no activo, es un error dar de baja
Mostrar(“ERROR baja....”)
Sino
// marco como baja si confirma eliminarlo
MostrarReg(reg)
Si confirma(“Lo elimina?”) entonces
reg.activo <- falso
Posicionarse(arch, pos)
Escribir(arch, reg)
Fin Si
Fin Si
Sino // no se encontraba esa clave, error dar de baja
Mostrar(“ERROR baja....”)
Fin Si
Hasta confirma(“Finaliza?”)
Cerrar(arch)
Fin Proc
Procedimiento Consulta (E nomb: Cadena)
Var
Precondición: nomb=N y el archivo N
reg: tReg
existe y está ordenado por su clave
arch: tArch
PosCondición: el archivo N tiene los
pos: entero
mismos datos ordenados por su clave
Inicio
Abrir(arch, nomb)
repetir
PedirClave( reg.clave) // pide datos del campo clave
BuscarClave(arch, reg.clave, pos, reg)
Si pos >= 0 entonces
//fue encontrado un registro con esa clave
Si no reg.activo entonces //no activo, es un error consultarlo
Mostrar(“ERROR consulta....”)
Sino
// lo muestro
MostrarReg(reg)
Fin Si
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 82
Algorítmica y Programación I
Sino // no se encontraba esa clave, error consulta
Mostrar(“ERROR consulta....”)
Fin Si
Hasta confirma(“Finaliza?”)
Cerrar(arch)
Fin Proc
Procedimiento Modificacion (E nomb: Cadena)
(* Precond: nomb=N y el archivo N existe y está ordenado por su clave
Poscond: el archivo N mantiene ordenados los datos por su clave*)
Var
reg: tReg
arch: tArch
pos: entero
Inicio
Abrir(arch, nomb)
repetir
PedirClave( reg.clave) // pide datos del campo clave
BuscarClave(arch, reg.clave, pos, reg)
Si pos >= 0 entonces
//fue encontrado un registro con esa clave
Si no reg.activo entonces //no activo, es un error modificar
Mostrar(“ERROR modif....”)
Sino
// lo muestro
MostrarReg(reg)
PedirDatosModificacion(Reg)
Posicionarse(arch, pos)
Escribir(arch, reg)
Fin Si
Sino // no se encontraba esa clave, error consulta
Mostrar(“ERROR modif....”)
Fin Si
Hasta confirma(“Finaliza?”)
Cerrar(arch)
Fin Proc
Cada cierto tiempo habrá que reorganizar el archivo, eliminando físicamente los registros dados de baja.
Para ello es necesario recorrer el archivo y copiar en uno nuevo sólo los registros que están activos.
Procedimiento Reorganizar (E nomb: Cadena)
(* Precond: nomb=N y el archivo N existe
Poscond: cantidadBajas(archivo Nomb) = 0 *)
Var
Reg: tReg
Arch1, arch2: tArch
Inicio
Abrir(arch1, nomb)
Crear(arch2, “Auxiliar”)
Mientras no eof(arch1) hacer
Leer(arch1, reg)
Si reg.activo entonces
Escribir(arch2, reg)
Fin Si
Fin Mientras
Cerrar(arch1)
Cerrar(arch2)
Eliminar(arch1)
Renombrar(arch2,nomb)
Fin Proc
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 83
Algorítmica y Programación I
Ordenamiento, búsqueda e intercalación
Son operaciones básicas en el campo de la documentación y en la que según señalan las estadísticas,
las computadoras emplean la mayor cantidad de tiempo.
La ordenación es la operación de organizar un conjunto de datos en un orden dado, creciente o
decreciente en datos numéricos o alfabéticos. Hay varios métodos para ordenar un conjunto de datos.
La búsqueda de información es una tarea muy frecuente. Si los datos están ordenados, buscar un
elemento en ellos resulta más sencillo y rápido. Si no lo están, habrá que recorrer todos para darse
cuenta que no se encuentra el elemento.
La intercalación o mezcla es la operación de unir dos conjuntos de datos ordenados en uno sólo.
Métodos de ordenamiento
o
Ordenación interna: se realiza en memoria interna de la computadora de gran velocidad y
acceso aleatorio. Se tienen los datos en un arreglo.
o
Ordenación externa: se realiza en medios de almacenamiento externos como cintas o disco.
Estos dispositivos son más lentos en la operaciones de entrada salida pero pueden contener
mayor cantidad de información. Los datos están en un archivo.
Datos de entrada: lista de datos a ordenar: lista. Rango de
los elementos a ordenar: principio y final
Principio, final
Lista
Datos de salida: lista de datos ordenada: lista.
Cliente
ordenar
Método de selección
https://es.wikipedia.org/wiki/Ordenamiento_por_selecci%C3%B3n#/media/File:Selection-SortAnimation.gif
Se basa en buscar el menor elemento e intercambiarlo con el de la primera posición. Luego repetir el
proceso desde el segundo elemento, luego desde el tercero, y así hasta que sólo quede uno.
Ejemplo:
[1]
Lista 20
[2]
32
[3]
12
[4]
15
[5]
25
[6]
16
[7]
30
[8]
10
[9]
18
[10]
28
Iteración 1
10
32
12
15
25
16
30
20
18
28
10
Iteración 2
10
12
32
15
25
16
30
20
18
28
12
Iteración 3
10
12
15
32
25
16
30
20
18
28
15
Iteración 4
10
12
15
16
25
32
30
20
18
28
16
Iteración 5
10
12
15
16
18
32
30
20
25
28
18
Iteración 6
10
12
15
16
18
20
30
32
25
28
20
Iteración 7
10
12
15
16
18
20
25
32
30
28
25
Iteración 8
10
12
15
16
18
20
25
28
30
32
28
Iteración 9
10
12
15
16
18
20
25
28
30
32
30
mínimo
En la primera iteración encontramos que el elemento de la posición 8 es el menor y lo intercambiamos
con el que se encuentra en la primer a posición. En la segunda iteración, consideramos la lista a partir de
la posición 2 y encontramos que el elemento de la posición 3 es el menor y lo intercambiamos con el que
se encuentra en la segunda a posición. Así seguimos hasta que en la novena iteración nos quedaron
acomodados los dos últimos valores.
Necesitamos de una estructura de repetición que recorra del principio al final de la lista para ubicar el
menor en cada pasada. Y de otra estructura de repetición que repita el proceso N-1 veces. Puesto que
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 84
Algorítmica y Programación I
en cada pasada sabemos cuántos elementos consultar, podemos realizarlo utilizando dos estructuras de
repetición anidadas basadas en contador: “estructura Desde”
Procedimiento OrdSeleccion(E/S Lista:tLista, E principio, final: entero)
Var
Precondición: principio=P
i, p, menor: entero
y final=F y P<=F y [P,F]
Inicio
Desde p<- principio hasta final -1 hacer
 Rango(tLista) y Lista=
Menor <- p
{Li} i  [P,F]
Desde i<- p +1 hasta final hacer
Poscondición: Lista={Vi}
Si lista[i] < lista[menor] entonces
y {Vi}=Permutación({Li})
Menor <- i
y Vi<=Vi+1  i  [P,F]*)
Fin Si
Fin Desde
Intercambio(lista[p], lista[menor])
Fin Desde
Fin Proc
Método de intercambio o burbuja
https://es.wikipedia.org/wiki/Ordenamiento_de_burbuja#/media/File:Bubble-sort-example-300px.gif
Se comparan dos posiciones consecutivas, primera y segunda, si están en orden se mantienen como
están, en caso contrario se intercambian. Luego se comparan segunda con tercera y así hasta finalizar.
Como resultado de toda esta comparación, el elemento mayor se habrá corrido hacia el final quedando
en su posición correcta. Se repite el proceso hasta que no haya más intercambios o que el último
intercambio haya sido con el primer elemento.
[1]
Lista 20
[2]
32
[3]
12
[4]
15
[5]
25
[6]
16
[7]
30
[8]
10
[9]
18
Iteración 1
20
12
15
25
16
30
10
18
28
[10]
28
Último intercambio
32
9
Iteración 2
12
15
20
16
25
10
18
28
30
32
8
Iteración 3
12
15
16
20
10
18
25
28
30
32
6
Iteración 4
12
15
16
10
18
20
25
28
30
32
5
Iteración 5
12
15
10
16
18
20
25
28
30
32
3
Iteración 6
12
10
15
16
18
20
25
28
30
32
2
Iteración 7
10
12
15
16
18
20
25
28
30
32
1
En la primer iteración el valor 32 se fue corriendo hacia el final, resultando que el último intercambio se
realizó estando en la posición 9, con lo que sólo se aseguró de ubicar correctamente ese único
elemento. En la segunda iteración se fueron corriendo los valores 20, 25 y 30, quedando con la certeza
de haber acomodado un elemento. En la tercer iteración quedaron acomodados los valores 25, 28 ya
que el último intercambio se hizo cuando el 25 estaba en la posición 6.
Procedimiento Burbuja(E/S Lista:tLista, E principio, final: entero)
Var
Precondición: principio=P
i, ultI, tope: entero
y final=F y P<=F y [P,F] 
ordenado: logico
Rango(tLista) y Lista=
Inicio
{Li} i  [P,F]
ultI <- final
Poscondición: Lista={Vi} y
Repetir
{Vi}=Permutación({Li}) y
ordenado <- verdadero
Vi<=Vi+1  i  [P,F]*)
tope <- UltI - 1
Desde i<- principio hasta tope hacer
Si lista[i] > lista[i+1] entonces
Intercambio(lista[i], lista[i+1])
ultI <- i
ordenado <- falso
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 85
Algorítmica y Programación I
Fin Si
Fin Desde
Hasta ordenado
Fin Proc
Método de inserción o de la baraja
https://es.wikipedia.org/wiki/Ordenamiento_por_inserci%C3%B3n#/media/File:Insertion-sort-example300px.gif
Consiste en insertar un elemento en el arreglo en la parte ya ordenada y comenzar de nuevo con los
elementos restantes. Se basa en comparaciones y desplazamientos sucesivos. Cada elemento del
vector es insertado en el lugar correspondiente respecto a los otros elementos ya ordenados.
Se parte suponiendo que se tiene una lista ordenada con un solo elemento (el primero) y se trata de
colocar en orden al segundo elemento: se compara el segundo elemento con el primero, si están en
orden se mantienen como están, en caso contrario se desplaza el primero un lugar hacia la derecha para
poder colocar el segundo en su lugar. Luego, se compara el tercer elemento con los ya ordenados para
buscar la posición que le corresponda respecto a los mismos. Si fuese necesario, se desplazan los
elementos posteriores hacia la derecha para liberar la posición del vector donde se insertará el elemento
considerado. Se repite esta operación con todos los elementos restantes.
A continuación aplicaremos el método a nuestro vector:
[1]
Lista 20
[2]
32
[3]
12
[4]
15
[5]
25
[6]
16
[7]
30
[8]
10
[9]
18
[10]
28
Elemento a
ubicar
Sector
ordenado
[1]
Lista 20
[2]
32
[3]
12
[4]
15
[5]
25
[6]
16
[7]
30
[8]
10
[9]
18
[10]
28
Iteración 1 20
32
12
15
25
16
30
10
18
28
32
1..2
Iteración 2 12
20
32
15
25
16
30
10
18
28
12
1..3
Iteración 3 12
15
20
32
25
16
30
10
18
28
15
1..4
Iteración 4 12
15
20
25
32
16
30
10
18
28
25
1..5
Iteración 5 12
15
16
20
25
32
30
10
18
28
16
1..6
Iteración 6 12
15
16
20
25
30
32
10
18
28
30
1..7
Iteración 7 10
12
15
16
20
25
30
32
18
28
10
1..8
Iteración 8 10
12
15
16
18
20
25
30
32
28
18
1..9
Iteración 9 10
12
15
16
18
20
25
28
30
32
28
1..10
1..1
En la primera iteración se comparan el primero y segundo elemento, como están en orden, no hay
intercambio. En la segunda iteración se considera al tercer elemento y al compararlo con los dos
elementos ya ordenados, se encuentra que debe ubicarse en la primer posición del vector. Se desplazan
un lugar a la derecha los elementos que se encuentran en las dos primeras posiciones para liberar la
posición 1, donde se ubica el 12. Y así en cada iteración tomamos un nuevo elemento comparándolo con
los anteriores que están ordenados entre sí.
Procedimiento OrdInsercion(E/S Lista:tLista, E principio, final: entero)
(* Precond: principio=P y final=F y P<=F y [P,F]  rango(tLista)
y Lista= {Li} i  [P,F]
Poscond: Lista={Vi} y {Vi}=Permutación({Li}) y Vi<=Vi+1  i  [P,F]*)
Var
i, j, aux: entero
Inicio
Desde i<- principio +1 hasta final hacer
aux <- lista[i]
j <- i-1
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 86
Algorítmica y Programación I
Mientras j>=principio y aux < lista[j] hacer
lista[j+1] <- lista[j]
j <- j-1
Fin Mientras
lista[j+1] <- aux
Fin Desde
Fin Proc
Método de ordenamiento rápido o quicksort
https://es.wikipedia.org/wiki/Algoritmo_de_ordenamiento#/media/File:Sorting_quicksort_anim.gif
Se basa en el hecho que es más rápido y fácil de ordenar dos listas pequeñas que una lista grande.
Consiste en dividir el arreglo en dos partes, una con los elementos menores a un valor de separación,
llamado pivote, y otra con los elementos mayores. Así el arreglo queda divido en tres partes:
1. El elemento de separación o pivote.
2. Subvector VI con los elementos menores o iguales.
3. subvector VD que contiene los elementos superiores.
Para cada subvector se vuelve aplicar el mismo método hasta que las sublistas tengan dos elementos
los cuales se comparan directamente.
La solución recursiva de este método consiste en dividir el vector en dos partes, de modo tal que el
pivote quede en la posición definitiva y todos los elementos menores de un lado y los mayores del otro
lado. Luego aplicar el método para el subvector de la izquierda (desde principio hasta posición del pivote
–1) y para el subvector de la derecha (desde la posición del pivote +1 y el final). La recursión finaliza
cuando el principio es mayor que el final del subvector.
Procedimiento quicksort(E/S Lista:tLista, E principio, final: entero)
(* Precond: principio=P y final=F y P<=F y [P,F]  rango(tLista)
y Lista= {Li} i  [P,F]
Poscond: Lista={Vi} y {Vi}=Permutación({Li}) y Vi<=Vi+1  i  [P,F]*)
Var
posPivote: entero
Divide la lista en dos partes,
Inicio
desde principio a posPivote – 1,
Si principio < final
entonces
son todos menores a los que
Particion(lista, principio, final, posPivote)
están de posPivote +1 al final.
Quicksort(lista,principio, posPivote-1)
El elemento en posPivote está
en el lugar correcto.
Quicksort(lista, posPivote+1, final)
Fin Si
Fin Proc
Ordenamiento en archivos
Mantener los datos ordenados simplifica la tarea de búsqueda. También se requiere para confeccionar
listados en los cuales los datos deben figurar en un determinado orden.
Soluciones:
o
o
Llevar el conjunto de datos a memoria (en un arreglo) y ordenarlo con cualquiera de los métodos
de ordenamiento de listas en memoria. Esto no siempre es posible porque:
 la capacidad de la memoria es limitada
 los arreglos deben tener un tamaño fijado antes de compilar. Los archivos pueden crecer
dinámicamente.
Llevar a memoria sólo el campo que se quiere ordenar y su posición relativa en el archivo (armar
el arreglo con la posición (pos) y el campo clave). Ordenar el arreglo. Recorrer el arreglo
ordenadamente, accediendo directamente al registro en el archivo correspondiente al campo pos
del elemento del arreglo.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 87
Algorítmica y Programación I
o
Aunque esta solución ocupa menos memoria que la anterior, tiene las mismas limitaciones que
ella.
Llevar a memoria sólo un subconjunto de registros por vez. Ordenarlo y guardarlo en un archivo
intermedio. Luego por cada dos grupos ordenados, fusionarlos en uno sólo. Repetir el proceso
de fusión hasta que no queden más subgrupos. Este método consta de una fase de
ordenamiento y otra de intercalación.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 88
Algorítmica y Programación I
Análisis de los algoritmos de ordenamiento, eficiencia.
Por algoritmo "más eficiente" usualmente nos referimos al más rápido. Debido a que los requerimientos
de tiempo son usualmente un factor importante cuando se trata de decidir si un algoritmo es lo
suficientemente eficiente para ser útil en la práctica..
La eficiencia de un algoritmo de ordenamiento entonces, se mide en función del tiempo que insume la
cantidad de comparaciones que deben realizarse para ordenar la lista.
Algoritmo
Nombre original
Complejidad
Ordenamiento por selección
Selection sort
O(n²)
Ordenamiento de intercambio o burbuja
Bubblesort
O(n²)
Ordenamiento por inserción o de la baraja Insertion sort
O(n²)
Ordenamiento rápido
Promedio: O(n log n),
peor caso: O(n²)
Quicksort
Métodos de Búsqueda
En muchas aplicaciones es necesario buscar un elemento dentro de una determinada estructura de
datos (vector, matriz, archivo, etc.). Puede resultar que en la búsqueda encontremos dicho elemento,
pudiendo indicar dónde, o que no lo encontremos.
La operación de búsqueda puede realizarse con una función que devuelva la posición donde se
encuentra el elemento o el valor –1 si no se halla.
Datos de entrada: lista y rango donde buscar y el valor a encontrar.
Datos de salida: posición donde se encuentra
Primero, ultimo
Lista
Valor
posición
De acuerdo a las características de la búsqueda, podemos
diferenciar dos métodos: búsqueda lineal y búsqueda binaria.
Cliente
Búsqueda
Búsqueda lineal
Consiste en recorrer secuencialmente la estructura de datos a partir del primer elemento y comparar
cada elemento con el buscado hasta encontrarlo o hasta que no queden elementos para comparar. Si
ocurre este último caso, la búsqueda ha terminado sin éxito.
Búsqueda lineal sobre un vector no ordenado
Funcion BuscSec (E Lista: tlista, E principio,
entero) : entero
Var
i, pos:entero
Inicio
pos  -1
i  principio
Mientras (pos = -1 y (i  final) hacer
Si lista[i] = elemento entonces
pos <- i
Sino
i <- i+1
Fin Si
Fin Mientras
BuscSec <- pos
Fin Func
Búsqueda lineal sobre un vector ordenado
final: entero, E elemento:
Precondición:
principio=P y final=F y
elemento=E y P<=F y
[P,F]  rango(tLista) y
Lista= {Li} i  [P,F]
PosCondición:
(BuscSec = -1 y
E no pertenece a {Li})
Precondición:
o
principio=P y final=F y
(BuscSec
= N
N y [P,F]
elemento=E
y y
P<=F
y LN = E)
[P,F]
 rango(tLista) y
Lista= {Li} y
Li<=Li+1 i  [P,F]
PosCondición:
(BuscSecOrd = -1 y
E no pertenece a {Li})
o
(BuscSecOrd = N y N 
[P,F]
y LN = E)
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 89
Algorítmica y Programación I
La eficiencia de la búsqueda lineal puede mejorar si se realiza sobre un vector ordenado, porque sólo
será necesario recorrer el vector hasta la posición donde se encuentre el elemento buscado o uno mayor
a éste.
Funcion BuscSecOrd (E Lista: tlista, E principio, final: entero, E
elemento: entero) : entero
Var
i, pos:entero
Inicio
pos  -1
i  principio
Mientras (pos = -1 y (i  final) y lista[i] < = elemento hacer
Si lista[i] = elemento entonces
pos <- i
Sino
i <- i+1
Fin Si
Fin Mientras
BuscSecOrd <- pos
Fin Func
Búsqueda binaria
Este método requiere que la estructura de datos sobre la que se realizará la búsqueda se encuentre
ordenada. Compara el elemento buscado con el elemento central de la estructura. Si el elemento
buscado coincide con el elemento central, se termina la búsqueda. Si el elemento buscado es menor que
el central, se considera la primera mitad de la estructura, y se ubica el elemento central de dicha mitad, y
se lo compara con el buscado. Se repite este proceso hasta que se encuentra el elemento buscado o
hasta que no queden más elementos por comparar. Generalmente es mucho más rápido que la
búsqueda lineal.
Búsqueda binaria sobre un vector ordenado
Funcion BuscBinaria (E Lista: tlista, E principio, final: entero, E
elemento: entero): entero
(* Precondición: principio=P y final=F y elemento=E y P<=F y
[P,F]  rango(tLista) y Lista= {Li} y Li<=Li+1 i  [P,F]
PosCondición: (BuscBinaria = -1 y E no pertenece a {Li}) o
(BuscBinaria = N y N  [P,F] y LN = E)
*)
Var
P,f,m,pos:entero
Inicio
Inicialmente, el punto medio M es la posición
pos  -1
central del vector. Si el elemento buscado es
mayor que el correspondiente a la posición
p  principio
M, se fija P como la posición siguiente a M
f  final
(se descarta la primer mitad del vector). Si
Mientras (pos = -1 y p <= f) hacer
por el contrario, el elemento buscado es
m <- (p + f) div 2
menor que el de la posición M, se toma como
Si lista[m] = elemento entonces
F la posición anterior a M, desechándose la
pos <- m
segunda mitad del vector. El nuevo punto
Sino
medio M es la posición central del subvector
Si lista[m] > elemento entonces
comprendido entre las posiciones P y F .
f <- m-1
Si al comparar el elemento de la posición M
Sino
con el buscado encontramos que ambos
p<- m+1
coinciden, ha finalizado la búsqueda de
Fin Si
manera exitosa. Cuando la posición inicial P
Fin Si
es mayor que la final F significa que el
Fin Mientras
elemento buscado no se encuentra dentro
BuscBinaria <- pos
del vector.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 90
Algorítmica y Programación I
Fin Func
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 91
Algorítmica y Programación I
Búsqueda en archivos
Los algoritmos de búsqueda en archivo son los mismos que para arreglos. La diferencia consiste en la
forma de obtener un elemento. En el archivo se requiere recuperar (leer) un elemento antes de poder
comparar. Cuando se busca un elemento se lo hace por el valor de su campo clave. La operación puede
ser más útil si además de devolver la posición en que se encuentra el registro en el archivo, se devuelve
también el registro recuperado.
Datos de entrada: archivo donde buscar: arch y el valor de clave a encontrar: dato.
Datos de salida: posición donde se encuentra y el registro
encontrado con todos los datos
En este caso, debemos resolverlo con un procedimiento con dos
parámetros de salida: la posición y el registro.
Cliente
arch
dato
Pos
Reg
BúsquedaArch
Procedimiento BuscarNoOrd(E/S arch: tArch, E dato: entero, S reg:treg, S pos:
entero)
(* Precond: arch=A y dato=D y A está abierto
PosCond: (pos=-1 y dato no pertenece A) o (pos>=0 y pos <= tamaño(A)-1
y reg=R y R.clave=D y posicion(R)=pos ) *)
Const
NoEsta = -1 (* valor para no encontrado *)
Inicio
Posicionarse(arch,0) (*posiciono al principio*)
pos  NoEsta
Mientras ( no eof(arch) y (pos= NoEsta) ) hacer
Leer (arch, reg)
Si reg.clave = dato entonces
pos  DondeEstoy(arch) -1
Fin Si
Fin Mientras
Fin Proc
Procedimiento BuscarBin(E/S arch: tArch, E dato: entero, S reg:treg, S
pos: entero)
(* Precond: arch=A y dato=D y A está abierto y ordenado por su clave
Poscond: (pos=-1 y dato no pertenece A) o (pos>=0 y pos<=tamaño(A)-1 y
reg=R y R.clave=D y posicion(R)=pos *)
Const
NoEsta = -1 (* valor para no encontrado *)
var
p,f,m: entero
Inicio
p  0
f  Tamaño (arch) – 1
pos  noEsta
Mientras (p <= f ) y (pos = noEsta) hacer
m  (p + f) /2
Posicionarse(arch, m)
Leer (arch, reg)
Si reg.clave = dato entonces
pos  m
Sino
Si reg.clave < dato entonces
p  m + 1
Sino
f  m - 1
Fin Si
Fin Si
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 92
Algorítmica y Programación I
Fin Mientras
Fin Proc
Análisis de los algoritmos de búsqueda
La eficiencia de un algoritmo de búsqueda se mide en función de la cantidad de comparaciones que
deben realizarse cuando un elemento a buscar no se encuentra en la lista. Es una medida del tiempo
que se tarda, a mayor tiempo, menos eficiente.
Para la búsqueda secuencial con datos no ordenados la cantidad de comparaciones es igual a la
cantidad de elementos de la lista. Si la lista tiene N elementos, el tiempo de búsqueda es proporcional a
N.
Para la búsqueda secuencial con datos ordenados la cantidad de comparaciones varía entre un mínimo
de uno (si el elemento a buscar es menor que el primero) y un máximo de N (si el elemento a buscar es
mayor que el último). En promedio podemos decir que el tiempo de búsqueda es proporcional a N/2.
Para la búsqueda binaria el tiempo no es lineal, sino que cuanto más datos tiene la lista, el tiempo crece
en menor proporción ya que constantemente la lista es dividida en dos y por cada partición, sólo se hace
una comparación. El tiempo de búsqueda es proporcional log2 (N)
Promedio de comparaciones para distintos N
N
10
100
1000
1.000.000
Sec. No ordenado
10
100
1000
1.000.000
Sec. ordenado
5
50
500
500.000
Binario
4
7
10
20
Método
Se puede ver que la búsqueda binaria es un método muy eficiente pero requiere que los datos estén
ordenados.
Intercalación
La intercalación, mezcla o fusión es la operación de unir dos conjuntos de datos ordenados en uno
sólo y que se mantenga el orden.
Una solución podría ser poner todos los elementos del primer conjunto, a continuación, todos los del
segundo y ordenarlos, pero este algoritmo es muy ineficiente ya que no aprovecha el conocimiento de
que cada conjunto de datos está ordenado.
Un proceso de intercalación eficiente consiste en comparar un elemento de cada lista (comenzando con
el primero en cada una), colocar el más pequeño en la nueva lista y tomar el siguiente elemento al que
ya fue ubicado. Este proceso se continúa mientras haya elementos en ambas listas para ser ubicados.
Cuando una de las listas se termina, se colocan todos los restantes de la otra lista.
Podemos tomar consideraciones especiales cuando ambas listas poseen elementos de igual clave.
Según el modelo del problema, podemos copiar ambos, no copiar ninguno, elegir uno de ellos por alguna
condición o cálculo.
Seudocódigo genérico
obtener el primer elemento de cada conjunto, lista o archivo (C1, C2)
Posicionarse al comienzo del tercer conjunto (C3)
Mientras tengo elementos a procesar en los dos (en C1 y en C2) hacer
Comparo los elementos.
Si son iguales entonces
Proceso de acuerdo con el modelo del problema
obtengo el siguiente registro de ambos
Sino
guardo el menor de ellos
y obtengo el siguiente registro del que fuera menor
FinSi
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 93
Algorítmica y Programación I
me posiciono en la próxima posición de C3
Fin Mientras
Guardo en C3 los elementos que faltan procesar del conjunto que quedó.
Ejemplo de intercalación de listas numéricas
Intercalar dos listas de enteros ordenadas en forma ascendente. En caso de elementos iguales entre las
listas, poner ambos.
Lista1, Lista2
N1, N2
Datos de entrada: las dos listas (lista1 y lista2) y sus
respectivos topes, N1 y N2 (asumimos que el rango es de 1 al
Lista3
tope).
Datos de salida: la nueva lista ordenada con los elementos
Cliente
Intercalación
de ambas: lista3. Si no se pusieran todos los elementos, se requiere también la cantidad de datos
colocados en la lista3 como dato de salida.
Se requiere que la lista de salida sea lo suficientemente grande como para contener ambas listas. Si las
tres son del mismo tipo, no podrán venir ambas listas llenas, sino que la suma de elementos de ambas
deberá ser a lo sumo igual a la capacidad física de la lista (Max), de lo contrario la lista de salida tendrá
que tener una capacidad doble.
Procedimiento Intercalación(E L1,L2:tlista; E n1,n2: entero;S L3:tlista)
(* precond: L1[i] < L1[i+1]  i  [1,n1] y L2[i] < L2[i+1]  i  [1,n2]
y [1,n1+n2]  rango(tLista)
poscond: L3[i] <= L3[i+1]  i  [1,n1+n2] *)
var
Para acceder a un elemento requiero de un índice por
i, j, k, t: entero
cada lista los cuales deberemos inicializarlos en 1 para
inicio
posicionarnos en el principio.
i <- 1
j <- 1
k <- 1
Mientras (i <= n1 ) y (j <= n2) hacer
Si L1[i] < L2[j] entonces
Que haya elementos a procesar en una
L3[k] <- L1[i]
lista significa que su índice será menor o
i <- i + 1
igual que su tope.
Sino
L3[k]
<- L[j]
j <- j + 1
FinSi
k <- k +1
FinMientras
Desde t <- i hasta n1 hacer
L3[k] <- L1[t]
k <- k +1
FinDesde
Desde t <- j hasta n2 hacer
L3[k] <- L2 [t]
k <- k +1
FinDesde
Fin Proc
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 94
Algorítmica y Programación I
Fusión de archivos
Muchas veces nos encontramos con el problema de fusionar dos conjuntos de datos en uno sólo. Los
archivos están ordenados por un campo clave, y se desea reunir la información en un nuevo archivo,
donde los datos de igual clave figuren una sola vez, quizá con la información de uno de ellos o
realizando alguna operación con la información de ambos (por ejemplo, si se reúnen dos archivos de
stock, la cantidad de artículos de igual clave será la suma de ambos). El algoritmo es similar al de fusión
o intercalación en arreglos.
Precondiciones:
Arch1 y arch2 están ordenados por campo
Arch1
Arch2
clave.
Dentro de cada archivo, no hay registros
repetidos con igual clave.
A1.clave ? A2.clave
Fusión
<
grabo el registro A1 y leo de arch1
=
De acuerdo con el problema, decido cual grabar y
leo de ambos archivos
Nuevo
Arch
>
grabo el registro A2 y leo de arch2
Comparación del algoritmo de fusión en arreglos y en archivos.
Ejemplo: Fusionar los stocks de dos sucursales.
Los datos de los artículos constan de dos campos: el nro. de artículo que identifica al registro (clave) y la
cantidad que hay de ese artículo en el stock. En la fusión, se obtiene un único stock con todos los
artículos de ambas sucursales. Los artículos comunes a ambas, aparecen una única vez con una
cantidad que es la suma de ambas.
El algoritmo de fusión para este problema consiste en:
obtener el primer elemento de cada conjunto, lista o archivo (C1, C2)
Posicionarse al comienzo del tercer conjunto (C3)
Mientras tengo elementos a procesar en los dos (en C1 y en C2) hacer
Comparo los elementos.
Si son iguales entonces
guardo uno de ellos con una cantidad que sea la suma de ambos
obtengo el siguiente registro de ambos
Sino
guardo el menor de ellos
y obtengo el siguiente registro del que fuera menor
FinSi
me posiciono en la próxima posición de C3
Fin Mientras
Guardo en C3 los elementos que faltan procesar del conjunto que quedó.
En el arreglo se accede a un elemento a través del índice y debemos verificar que este sea menor o
igual que el tope. Para obtener otro elemento debemos incrementar el índice. Se sabe que aún quedan
elementos sin procesar si el índice al que estamos accediendo es menor o igual que el tope.
En el archivo, se accede a un elemento mediante la operación de leer y antes de leer tenemos que
asegurarnos que no se haya llegado al final del archivo. Dos leer consecutivos recuperan diferentes
registros ya que el indicador interno después de una lectura o escritura se mueve para que el próximo
acceso se haga en el siguiente registro. La función finArchivo(arch) devuelve Verdadero cuando no
quedan más registros sin leer.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 95
Algorítmica y Programación I
La condición del mientras: “tengo elementos a procesar en los dos”, se resuelve en arreglos con:
“(i <= tope1) y (j <= tope2)”. ¿Será equivalente a decir en archivos “no finArchivo(arch1) y no
finArchivo(arch2) ”?
Para algunos problemas esta condición es equivalente pero para otros no. En el algoritmo de búsqueda
secuencial reemplazamos el i <= f en la solución para arreglos por no finArchivo (arch) en la solución de
archivos. Veamos si podemos hacerlo en el problema de fusión:
Imaginemos que los archivos a intercalar tiene los artículos: {1, 5, 9} y {3, 4}. En el ciclo mientras
tendrán que haber sido grabados los valores {1, 3, 4} quedando { 5, 9} para grabarse al final.
arch1
1
5
arch2
3
4
9
arch3
Antes de entrar al mientras se
requiere recuperar los primeros
elementos: 1 y 3. Los punteros
internos apuntan al 5 y 4
respectivamente. No es eof en
ninguno de los dos por lo que se
entra al mientras.
1
5
3
4
9
1
De la comparación, se
graba el 1 y se lee el 5. Los
punteros internos apuntan al
9 y 4 respectivamente. No
es eof en ninguno de los
dos y se entra al mientras.
1
5
3
4
1
3
9
De la comparación, se graba el
3 y se lee el 4. Los punteros
internos apuntan al 9 y eof
respectivamente. Como es
eof(arch2) no entraría al
mientras y sin embargo el
registro con valor 4 aún no ha
sido procesado.
En el caso de la fusión, los registros son leídos en un ciclo y procesados en el siguiente, por eso la
condición de fin de archivo es cierta pero el registro aún no ha sido procesado. Para solucionar el
problema se puede pensar en una variable lógica (bandera) que detecte si hay o no registro a procesar.
Esta bandera es actualizada en cada lectura de registro: es verdadera si pudo leer y falsa si no. Una
operación leerSiPuedo puede encargarse de obtener el registro y actualizar la bandera.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 96
Algorítmica y Programación I
De esta manera el algoritmo para archivos queda:
tipo
tReg = registro
nroArt: entero
cant: entero
Fin registro
tArch = archivo de tReg
Procedimiento LeerSiPuedo(E/S arch:
tArch, S reg:tReg; S hay: logico)
Inicio
si eof(arch) entonces
hayfalso
sino
Leer(arch, reg)
hayverdadero
finsi
fin proc
Procedimiento Fusión( E s1,s2,s3: cadena)
(* precondicion: Los archivos s1 y s2 existen y sus datos estan ordenados
en forma ascendente por su clave
poscondición: el archivo s3 está ordenado por su clave y todas las claves
de s1 y de s2 se encuentran en s3 una sola vez *)
var
hay1, hay2: logico
arch1, arch2,arch3: tArch
reg1,reg2: tReg
inicio
Abrir(arch1, s1)
Abrir(arch2, s2)
Crear(arch3, s3)
LeerSiPuedo(arch1,reg1,hay1)
LeerSiPuedo(arch2,reg2,hay2)
Mientras (hay1 ) y (hay2) hacer
Si reg1.nroArt < reg2.nroArt entonces
Escribir(arch3, reg1)
LeerSiPuedo(arch1,reg1,hay1)
Sino
Si reg1.nroArt = reg2.nroArt entonces
reg1.cant <- reg1.cant + reg2.cant
Escribir(arch3, reg1)
LeerSiPuedo(arch1,reg1,hay1)
LeerSiPuedo(arch2,reg2,hay2)
Sino
Escribir(arch3, reg2)
LeerSiPuedo(arch2,reg2,hay2)
Fin Si
Fin Si
Fin Mientras
Mientras (hay1 ) hacer
Escribir(arch3, reg1)
LeerSiPuedo(arch1,reg1,hay1)
Fin Mientras
Mientras (hay2) hacer
Escribir(arch3, reg2)
LeerSiPuedo(arch2,reg2,hay2)
Fin Mientras
Fin Proc
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 97
Algorítmica y Programación I
Cortes de control
Desde los primeros algoritmos que hemos escrito, podemos observar que en alguna de sus partes
aparece una selección dicotómica que bifurca el proceso. Esa selección produce un corte en la
secuencia, a partir de una condición que controla por cuál de las opciones debe seguir. Este corte de
control puede hacerse mediante una marca (bandera) o comparando variables de control.
El corte de control es un concepto práctico y se produce al alterar la secuencia mediante la evaluación
de una condición.
Si bien en todos los algoritmos que utilizan estructuras de decisión hay cortes de control, generalmente
esta denominación es utilizada para referirse a listados con subtotales y a fusión de archivos con más
de un nivel de corte de control. En estos casos, el corte de control se produce al alterar la secuencia del
cuerpo de un proceso repetitivo para hacer una tarea especial (habiéndose dado una condición
específica). Si se da la condición para el corte, entonces se realiza la tarea especial, por ejemplo imprimir
el subtotal; y luego, se haya dado o no, se realiza la tarea cotidiana, como imprimir el registro. Aparece
una estructura de decisión dentro de una estructura de repetición.
Puede haber varios niveles de corte. Por ejemplo, se tiene un archivo con la información de los alumnos
de la universidad, ordenados por facultad y dentro de ella, por carrera y se desea imprimir la información
del archivo, incluyendo subtotales de cantidad de alumnos por carrera y por facultad. Al finalizar una
carrera se imprime el subtotal de carrera. Al finalizar una facultad se imprime los subtotales por Facultad.
En cualquier caso, el hecho que cambie la facultad, hace que se deba imprimir subtotales de carrera y de
facultad.
El corte de control toma la siguiente forma:
... { secuencia previa al corte de control}
Si cambia facultad entonces
imprimir subtotales de carrera y de facultad
Sino
Si cambia sólo carrera entonces
imprimir subtotales de carrera
Fin Si
Fin Si
... { continua la secuencia}
Listados - Consideraciones Generales
o
Los listados deben dividirse en hojas que tengan en cuenta el largo del papel (o líneas visibles en la
pantalla).
o
En cada hoja del listado se distinguen tres zonas: encabezado, cuerpo y pie de página.
o
Cada hoja debiera estar numerada. El número puede aparecer en el encabezado o en el pie de
página.
o
En el encabezado se indica el título general y si los datos son listados en columnas, también deben
aparecer los rótulos de ellas. Puede llevar más información como la fecha, el número de hoja, y
cualquier otra necesaria para el problema.
Hoja 2
El cuerpo es la parte
alumnos
regulares
Hoja 1
más
repetitiva:
allí
mbre
Carrera
Listado de alumnos regulares
aparece la información Encabezado
Quevedo
Ing. Civil
Nro.
Nombre
Carrera
de cada elemento a
Perez
A.P.U.
1
Juan Perez
A.P.U.
listar, ocupando uno o
Alca
A.P.U.
2
Vilma Gómez
A.P.U.
más renglones por
...
.......................
...............
Cuerpo
3
Silvia Garcia
Ing. Civil
elemento. En algunos
Suarez
A.P.U.
...
.......................
...............
casos,
cada
cierto
21
Ana Silva
A.P.U.
grupo de datos, se
Pie
de
Pág
muestran subtotales.
o
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 98
Algorítmica y Programación I
o
El pie de página puede tener información o estar vacío: son un grupo de líneas en blanco al final de
cada hoja. Si el listado es por pantalla, en la zona reservada para pie de página se suele poner
directivas para el usuario, como "presione una tecla para continuar", de modo de mantener la
pantalla tanto tiempo como el usuario lo requiera, antes de pasar a la siguiente hoja o pantalla de
información.
o
El listado debe resultar claro para quien lo mire, su aspecto debe ser prolijo.
Estructura de un algoritmo de listado
El algoritmo consiste en tres partes:
o
Tareas iniciales (Por Ej. inicialización de variables, abrir archivo o posicionarse en el primer
elemento a listar, encabezado de la primer hoja)
o
Bucle: Mientras hay elementos para listar, si corresponde, lo listo.
o
Tareas de finalización (por ejemplo, cerrar archivo, imprimir subtotales pendientes y totales
finales, pie de página de la última página, etc.)
Preguntas frecuentes:
Se deben listar todos los elementos?
Depende del problema. Si por ejemplo, se está trabajando con un archivo con bajas lógicas (un campo
especial del registro indica si está vigente o no), una vez que tengo el registro y antes de imprimirlo
verificaré la condición. Esta verificación debe hacerse antes que cualquier otra cosa, pues si no
corresponde imprimirse, no verifico si se terminó la hoja ni si debo hacer subtotales.
Dónde y cómo se analiza si se completó una hoja?
Se necesitará llevar la cuenta de cuantas líneas se van imprimiendo y antes de imprimir preguntarse si
hay lugar en la hoja, si no lo hay, realizar el pie de página, saltar a una nueva hoja e imprimir el
encabezado.
El pie de página para el caso de listados por pantalla, puede consistir en ubicarse en la penúltima línea
de la pantalla, mostrar un mensaje como "Oprima Enter para continuar", y quedar en espera del ingreso
del Enter
Linea
El encabezado puede recibir como parámetros las líneas y las
hoja
hojas (suponiendo que la numeración de páginas se haga en el
encabezado). En este caso, las tareas que haría son:
Listado
Encabezado
incrementar las hojas, imprimir las líneas de encabezado, y
poner en el valor inicial a líneas. Es apropiado que sea el encabezado el encargado de inicializar las
líneas, ya que sabe cuántas líneas utilizó para el mismo.
Dónde y cómo se analiza si tengo que imprimir subtotales?
La impresión de un subtotal depende de lo que sucede entre un campo del registro actual respecto del
anterior. Si por ejemplo, tengo los datos de los alumnos ordenados por carrera y deseo incluir subtotales
de cantidad de alumnos por carrera, sabré que tendré que imprimir el subtotal si la carrera del registro
actual es diferente a la carrera del registro anterior. Nuevamente, antes de imprimir un registro, verificaré
si antes debo imprimir algún subtotal.
Para esto debo conservar los datos necesarios del registro anterior. Si los registros estaban en un
arreglo L, el registro actual será L[i] y el anterior L[i-1]. Si están en un archivo, debiera utilizar una
variable auxiliar para conservar los datos del anterior.
Comparar el registro actual con el anterior es posible para todos, menos para el primero, ya que el
primero no tiene anterior.
Cómo soluciono el caso del primero?
Podemos plantear dos soluciones:
Solución 1:
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 99
Algorítmica y Programación I
Utilizo una bandera: por ejemplo, si está encendida es el primero, si está apagada, no. Antes de
empezar la enciendo y una vez que la utilizo (dentro del bucle), la apago.
Si esPrimero entonces
anterior<- actual
esPrimero<-falso
Sino
me fijo si debo realizar subtotales.
Fin Si
Solución 2:
Antes de empezar, inicializo la variable anterior con los valores del primer registro. De esa
manera al procesar el primer registro encuentra que es igual al anterior y no realiza subtotales.
Esta última solución es mejor que la anterior, ya que la solución 1 pregunta a cada registro si es
el primero, y esto va a ser cierto sólo una vez (la primera). Para evitar la pregunta constante, la
solución dos propone que se considere como anterior del primero al mismo registro, y esto lo
realiza antes de entrar al bucle.
Cuando esta solución se aplica a un conjunto de datos en archivo, debe tenerse en cuenta que para
obtener un registro debe leerse del archivo y que la operación leer, recupera un registro y deja al archivo
preparado para que la próxima operación se haga sobre el registro siguiente. Esto implica que para
inicializar el anterior con el valor del primer registro a listar, se deberá leer del archivo hasta encontrar el
primer registro a listar y luego reposicionarse en la posición que se lo encontró, para que ese primer
registro sea procesado como cualquiera de los restantes.
Cómo se analiza el caso de diferentes niveles de subtotales?
Suponiendo que el ordenamiento fuera universidad, y a igualdad de universidad, por facultad y dentro de
ella por carrera; un cambio en la universidad del elemento anterior y el actual, debe arrastrar a los
demás, es decir, debo imprimir subtotal por carrera, por facultad y por universidad, independientemente
que la carrera no haya cambiado (dos universidades podrían tener una misma identificación de carrera o
facultad, aunque sus identificaciones fueran iguales, son carreras distintas ya que la identificación
completa es universidad-facultad-carrera).
En base a este análisis, la estructura de decisión deberá comenzar preguntando si cambió la
universidad, si fuera así, imprimir subtotales de las tres (carrera, facultad, universidad), sino si cambió la
facultad, imprimir subtotales de carrera y de facultad, sino si cambió la carrera imprimir subtotales de
carrera.
Qué debe hacerse cuando se detecta que corresponde imprimir subtotales?
Las impresión de un subtotal implica:
Subtotal1
Subtototal2
linea
1. Imprimir el subtotal (subtotal1)
2. acumular para el subtotal de siguiente nivel (subtotal2)
Listado
Subtotal
3. poner el subtotal que se acaba de imprimir en cero (subtotal1)
4. incrementar líneas.
Observación: el paso 1 debe hacerse siempre antes que los demás y el paso 2 siempre antes que el
paso 3. Incrementar líneas podría hacerse en cualquier orden.
Es necesario fijarse si tengo lugar en la hoja antes de imprimir un total o subtotal?
En principio, sí. Pero esto daría lugar a que puedan quedar hojas que comienzan con subtotales y donde
la información del grupo al que se refiere, está en una hoja anterior.
Si pretendemos que esto no suceda, y que el subtotal quede siempre en una hoja donde al menos hay
un elemento que corresponde al grupo para el que se realiza el subtotal, se debe tomar alguna
precaución en la estimación del tope (cantidad de líneas máximas): tomar un tope algo menor que la
máxima cantidad real de líneas que puede tener el cuerpo del listado, de manera que, si toca imprimir
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 100
Algorítmica y Programación I
subtotales, haya lugar. Habiendo hecho esto, los subtotales se imprimen antes del corte de control por
hoja llena, sin necesidad de preguntar si hay lugar.
Formato genérico de listados con subtotales
//Tareas iniciales
Poner en cero los acumuladores y contadores (hojas, subotales, totales,
etc.)
Ubicarse en el primer registro a listar (abrir el archivo si es
necesario)
Inicializar adecuadamente los valores de registro anterior.
Encabezado (lineas, hoja)
//Bucle
Mientras hay elementos a listar hacer
Tomar un elemento (actual)
Si corresponde listarlo entonces
Si corresponden subtotales (anterior <> actual) entonces
Realizar los subtotales correspondientes
anterior <- actual
Fin Si
Si lineas > tope entonces
PieDePagina
Encabezado(lineas, hojas)
Fin Si
Imprimir datos del elemento actual
Incrementar lineas
Acumular para subtotales
Fin Si
Fin Mientras
// Tareas finales
Realizar los subtotales pendientes
imprimir total general
pie de página
cerrar archivo //si corresponde
Ejemplos
Listado 1: Listar por pantalla, todos los registros del archivo Alumnos, imprimiendo al final la cantidad
total de alumnos.
El programa que llama al listado, le pasa el nombre del archivo: listado1("C:Alumnos.dat").
Se definen los tipos de datos del problema:
Tipos
tNombre = cadena[50]
tReg= registro
Nro: entero
Nombre: tNombre
Carrera: entero
...
fin registro
tArch = archivo de tReg
Para realizar el pie de página y el encabezado, se definen procedimientos que se encargan de las tareas
correspondientes.
El procedimiento listado1 recibe el nombre del archivo a listar, se encarga de abrirlo al principio y cerrarlo
al final. Como precondición, el archivo debe existir.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 101
Algorítmica y Programación I
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 102
Algorítmica y Programación I
procedimiento Listado1(E nombArch: cadena)
(*Precondición: nombArch=N y el archivo N existe
*)
Const
Tope=20
Var
Arch: tArch
Reg: tReg
Lineas, hojas: entero
Cant: entero
Inicio
Cant<-0
hojas<-0
Abrir(arch,nombArch)
Encabezado(lineas, hojas)
Mientras no eof(arch) hacer
Leer(arch, reg)
Si lineas > tope entonces
PieDePagina
Encabezado(lineas, hojas)
Finsi
Mostrar (reg.nro, reg.nombre, reg.carrera)
Lineas <- lineas +1
Cant <- cant +1
Fin mientras
Mostrar ("Cantidad de alumnos", cant)
Cerrar (arch)
Fin proc
proc Encabezado(S lin: entero,E/S hojas: entero)
Inicio
LimpiarPantalla
Hoja<- hoja +1
Mostrar (" Listado de alumnos Hoja", hoja)
Mostrar ("Nro. Alumno
Carrera")
Mostrar ("_______________________")
Lineas <- 3
Fin proc
Procedimiento PieDePagina
Var s:cadena
Inicio
Mostrar ("Oprima ENTER para continuar")
ingresar s
Fin proc
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 103
Algorítmica y Programación I
Listado 2: Listar por pantalla, todos los alumnos regulares del archivo Alumnos, con subtotales de
cantidad de alumnos por carrera y facultad. El archivo está ordenado por facultad, carrera y legajo.
Tipo
tNombre = cadena[50]
tReg2= registro
Facultad: entero
Carrera: entero
Legajo: entero
Nombre: tNombre
Regular: logico
FinRegistro
tArch2 = archivo de tReg2
El programa que llama al listado, le pasa el nombre del archivo: listado2("C:Alumnos.dat")
El archivo debe existir. Para que los subtotales reflejen adecuadamente las cantidades, el archivo debe
estar ordenado por facultad, carrera y legajo.
Procedimiento Listado2(E nombArch: cadena)
(*Precondición: nombArch=N y el archivo N existe y está ordenado por
facultad, carrera y legajo *)
Const
Tope=20
Var
arch: tArch2
ant, reg: tReg2
lineas, hojas: entero
cant, cCarr, cFac: entero
Inicio
Cant <- 0
hojas <- 0
cCarr <- 0
cFac <- 0
Abrir(arch,nombArch)
InicAnterior(arch, ant)
Encabezado(lineas, hojas)
Mientras no eof(arch) hacer
Leer(arch, reg)
Si reg.regular entonces
Si reg.facultad <> ant.facultad entonces
Subtotal("total carrera", cCarr, cFac, lineas)
Subtotal("total Facultad", cFac, cant, lineas)
Sino
Si reg.carrera <> ant.carrera entonces
Subtotal("total carrera", cCarr, cFac, lineas)
Fin Si
Fin Si
ant <- reg
Si lineas > tope entonces
PieDePagina
Encabezado(lineas, hojas)
Fin Si
Mostrar (reg.nro, reg.nombre, reg.carrera)
lineas <- lineas +1
cCarr <- cCarr +1
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 104
Algorítmica y Programación I
Fin Si
Fin Mientras
Subtotal("total carrera", cCarr, cFac, lineas)
Subtotal("total Facultad", cFac, cant, lineas)
Mostrar ("Cantidad de alumnos", cant)
Cerrar (arch)
Fin Proc
Procedimiento InicAnterior(E/S arch: tArch2, S ant: tReg2)
(*recupera en Ant, a partir de la posición en que se encuentra, el primer
registro que corresponda a un alumno regular. Deja el archivo posicionado
en el registro recuperado
Precondición: arch= A y A está abierto y posicionado en el lugar a partir
del cual quiero obtener el registro.
Poscondición: reg= R y R.regular=verdadero y y arch= A’ y A’ está abierto
y posicionado en el lugar donde encontré el registro R
*)
ant
var
regular:logico
arch
pos:entero
Inicio
Listado
InicAnterior
pos <- dondeEstoy(arch) //inicializo pos por si no entro al mientras
regular <- falso
Mientras no eof(arch) y no regular hacer
Pos <- dondeEstoy(arch)
Leer(arch, ant)
regular <- ant.regular
Fin Mientras
Posicionarse(arch, pos)
Fin Proc
Procedimiento Subtotal(E msg:cadena, E/S subTot1, subTot2, lin: entero)
(* Muestro el msg seguido de subTot1, acumulo suTot1 sobre subTot2,
pongo a cero subTot1 e incremento lineas
Precondición: msg=M y subTot1=T1 y subTot2=T2 y lin=L
Poscondición: subTot1=0 y subTot2=T2+T1 y lin=L +1
Msg
*)
Inicio
Mostrar (msg, subTot1)
subTot2 <- subTot2 + subTot1
subTot1 <- 0
lin <- lin + 1
Fin Proc
Subtotal1
Subtototal2
linea
Listado
Procedimiento Encabezado(S lin: entero,E/S hoja: entero)
Inicio
Linea
LimpiarPantalla
hoja
Hoja<- hoja +1
Mostrar (" Listado de alumnos
Listado
regulares
Hoja", hoja)
Mostrar ("Nro. Alumno
Carrera
Facultad")
Mostrar ("______________________________________")
Lin <- 3
Fin Proc
Procedimiento PieDePagina
Subtotal
Encabezado
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 105
Algorítmica y Programación I
Var
s:cadena
Inicio
Mostrar ("Oprima ENTER para continuar")
Ingresar (s)
Fin Proc
Estructuras de datos dinámicas
La ventaja de las estructuras dinámicas de datos es que no hay que definir el tamaño antes de usarla,
sino que la voy utilizando según la necesito.
Los elementos que forman esta estructura se relacionan entre sí mediante campos enlace o puntero y en
general no están situados en forma contigua en memoria. A cada uno de esos elementos lo llamamos
nodo de la estructura.
Un puntero es un dato cuyo contenido es una dirección de memoria y esa dirección de memoria
corresponde a la dirección del dato apuntado.
Según el tipo de datos de la variable apuntada variará el tipo de puntero. A través de una variable de tipo
puntero podemos establecer la conexión o enlace entre los elementos que van a formar la estructura, y
según se realizan estos enlaces vamos a tener diferentes tipos de estructuras (listas enlazadas, árboles,
grafos).
Las estructuras dinámicas pueden ser lineales o no lineales. Son lineales si desde un elemento se
puede acceder solamente a otro y no lineales, si puedo acceder a varios.
Estructura lineal
Ejemplos: Listas, colas, pilas
Estructura no lineal
Ejemplos: árbol, grafo
Declaración de los punteros en distintos lenguajes
En C: <tipo> *<var_p>
En Pascal: <var_tPuntero>: ^<tipo>
En pseudocódigo: <var_tPuntero>: puntero a <tipo>
Ejemplo:
Ejemplo:
Ejemplo:
int *p
P: ^integer
P: puntero a entero
Acceder a un campo de un registro a través de un puntero
En C: P  nombre
En Pascal: p^.nombre
En pseudocódigo: p^.nombre
Gestión de la memoria dinámica
Siempre que se hace una inserción en una estructura dinámica, tenemos que pedir tantos bytes de
memoria dinámica como ocupa el nodo de la estructura dinámica, y cuando borramos un elemento,
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 106
Algorítmica y Programación I
tendremos que liberar la memoria ocupada por ese elemento. Para conseguir este manejo, todos los
lenguajes de programación tienen dos instrucciones que permiten reservar y liberar memoria dinámica.
En Pascal : new , dispose
En C : malloc , free
En C++ : new , delete
La instrucción de reserva de memoria llevará un único argumento, que será el tipo de datos para el que
se hace la reserva de memoria, y según el tamaño de ese tipo, esa instrucción sabrá el número de bytes
que tiene que reservar, devuelve es un puntero al comienzo de la zona que se ha reservado. Este valor
devuelto será el que tenemos que asignar a una variable de tipo puntero al tipo de datos para el que se
hace la reserva.
La función liberar memoria lleva un único argumento que es un puntero. Libera la memoria que ocupa el
dato apuntado por ese puntero.
Var p: puntero a <tipo>
P <- reservo_mem (<tipo>)
Lib_mem (<var_puntero>)
El valor nil
Sirve para indicar que una variable puntero no tiene asignada dirección, es decir apunta a nada. Guando
se solicita memoria y no hay disponible, la función de gestión devuelve el valor Nil. La operación liberar
memoria, pone en nil el puntero que se pasa por parámetro.
Ejemplo de lista dinámica
Tipo
TPuntNodo = puntero a tNodo
TNodo = Registro
Info: entero
Sig: TPuntNodo
Fin registro
lista
Var
Lista: tPuntNodo
Lista es una variable estática que servirá para tener acceso al comienzo de una lista dinámica, que irá
cambiando de tamaño en la medida que se agreguen o quiten elementos.
Vamos a definir algunas operaciones para trabajar con la lista:
CrearLista( lista) crea la lista vacía
Procedimiento CrearLista( S Lista:tPuntNodo)
(* Poscondicion: Lista = Nil *)
Inicio
Lista <- nil
Fin
Largo(lista): devuelve la cantidad de elementos de la lista.
Funcion Largo( E Lista:tPuntNodo): entero
(* Precondicion Lista= L
poscondicion: Largo = N y cantidad de elementos de L es N*)
var
p:tPuntNodo
cant: entero
Inicio
p <-Lista
cant <-0
Mientras p <> nil hacer
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 107
Algorítmica y Programación I
cant <- cant +1
p <- p^.sig
Fin Mientras
Largo <- cant
Fin
Insertar(lista, elem): inserta un elemento con la información elem en el orden al campo info.
Si la lista está vacía o es menor que el primero, inserta al principio, con lo cual lista pasa a apuntar el
nuevo elemento y éste al que apuntaba lista. Si no es así, debo encontrar el elemento anterior y hacer
que ese apunte al nuevo y el nuevo al que apuntaba aquel.
Procedimiento Insertar( E/S Lista:tPuntNodo, E: elem)
(* Precondicion Lista=L y largo(Lista) = N y elem=E
Poscondicion: (largo(Lista) = N +1 y E pertenece a L ) o no hubo memoria
*)
Var
ant, p, nuevo:tPuntNodo
encontrado: logico
Inicio
nuevo <- reservo_mem(tNodo)
Si nuevo <>nil entonces
nuevo^.info <- elem // completo el campo info con el valor recibido
Si lista = nil entonces
Enganchar(lista,nuevo)
Sino
Si lista^.info > elem entonces
Enganchar(lista,nuevo)
Si no
p<- Lista
// Apunto al primer elemento
Repetir
ant <- p
// guardo en ant el actual
p <- P^.sig // avanzo el puntero del actual
Hasta p=nil o p^.info > elem hacer
Enganchar(ant,nuevo)
Fin Si
Fin Si
Fin Si
Fin Proc
Procedimiento Enganchar( E/S ant, nuevo:tPuntNodo)
Inicio
nuevo^.sig <- ant
// hago que el siguiente al nuevo elemento sea el que estaba primero
ant <- nuevo
// hago que al nuevo elemento
Fin Proc
QuitarPrimero(lista) quita el primer nodo de la lista si puede.
Procedimiento QuitarPrimero( E/S Lista:tPuntNodo)
(* Precondicion Lista=L y largo(Lista) = N
Poscondicion: (largo(Lista) = N -1 o Lista=L *)
Var
p:tPuntNodo
Inicio
Si lista <>nil entonces
p <- lista
// resguardo la direccion a liberar
Lista <- lista^.sig
// hago quela lista apunte al siguiente elemento
Lib_mem(p)
// libero la memoria ocupada
Fin Si
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 108
Algorítmica y Programación I
Fin Proc
Ejemplo de secuencia de código:
CrearLista(Lista)
Lista
Insertar(Lista, 90)
Lista
Insertar(Lista, 60)
90
Lista
QuitarPrimero(Lista)
90
60
Lista
90
60
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 109
Algorítmica y Programación I
Lenguaje Pascal
Pascal fue diseñado alrededor de 1970 por Niclaus Wirth, quien lo diseñó para ser un lenguaje cómodo
para la enseñanza de los fundamentos de la programación. Como un lenguaje de programación
educativo, Pascal ha mostrado gran capacidad y ha sido ampliamente adaptado para programación de
sistemas y aplicaciones.
Es un lenguaje simple, compacto, fácil de aprender en poco tiempo, provee soporte efectivo para
programación estructurada y con una gran variedad de tipos de datos.
Estructura de un Programa en Pascal
Los programas Pascal tienen definidas dos partes: declarativa y ejecutiva. En la primera debe aparecer
todo lo que se usará en la segunda, de lo contrario se detecta como desconocido y no podrá ejecutarse.
En la parte declarativa se enuncian procedimientos, funciones, variables, constantes y nuevos tipos de
datos estructurados. Algunas implementaciones de pascal utilizan unidades, las cuales deben declararse
también antes de poder ser usadas.
Una unidad (Unit) es un conjunto de constantes, tipos de datos variables, procedimientos y funciones.
Cada unidad es como un programa independiente Pascal o bien una librería de declaraciones que se
pueden poner en un programa y que permiten que éste se pueda dividir y compilar independientemente.
Contiene uno o más procedimientos, funciones constantes definidas y a veces otros elementos. Se
puede compilar, probar y depurar una unidad independientemente de un programa principal.
Una vez que una unidad ha sido compilada y depurada, está lista para ser usada sin necesidad de volver
a compilar y los procedimientos, funciones y constantes definidos en esa unidad pueden ser utilizados
por cualquier futuro programa que se escriba sin tener que ser declarados individualmente en el mismo,
basta con declarar que se utiliza dicha unidad.
PROGRAM <identificador>
USES
<declaración de unidades>
Graph, Crt; // indica que se utilizará las unidades Graph y Crt
CONST
<definiciones de constantes>
MAX = 100;
TYPE
<declaración de tipos de datos definidos por el usuario>
tLista = ARRAY [1..MAX] OF INTEGER;
VAR
<definiciones de variables>
lista: tLista;
<definiciones de procedimientos y o funciones>
PROCEDURE <nombreProc>(<lista_parámetros_formales>)
<declaraciones>
BEGIN
<lista de acciones>
END;
FUNCTION <nombreFun>(<lista_parámetros_formales>): <tipo_Resultado>
<declaraciones>
BEGIN
<lista de acciones> // incluyendo una del tipo <nombreFun>:= resultado>
END;
BEGIN
<lista de acciones>
END.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 110
Algorítmica y Programación I
Los identificadores se eligen siguiendo las mismas reglas establecidas para nuestro seudocódigo:
Comienzan con una letra, pueden tener letras, dígitos o el carácter subrayador y no se distinguen
mayúscula de minúscula.
Todas las sentencias finalizan con punto y coma. Los bloques de sentencias o sentencia compuesta
comienzan con BEGIN y finalizan con END. El cuerpo del programa principal termina con END seguido
de un punto, mientras que el cuerpo de una función o de un procedimiento con END seguido de un punto
y coma.
Si una función o un procedimiento no requieren parámetros, no se colocan los paréntesis. El pasaje de
parámetros es por valor (no requiere indicación especial) y por referencia (se precede al parámetro
formal con la palabra VAR). La lista se conforma de la misma manera que la hacemos en el
pseudocódigo, utilizando parámetros por referencia para los de salida, entrada-salida y arreglos.
La devolución del resultado de la función se hace como en el pseudocódigo, asignando al nombre de la
función el valor a retornar.
Los comentarios en Pascal se encierran entre llaves o secuencias paréntesis-asterisco:
{ esto es un comentario Pascal}
(* esto también es un comentario Pascal *)
El mínimo programa Pascal consistiría en:
PROGRAM vacio
BEGIN
END.
Pascal permite anidamiento de definiciones de funciones y procedimientos: dentro del ambiente de
declaraciones de una función o procedimiento, pueden definirse funciones o procedimientos, además de
las constantes, tipos o variables, los cuales serán sólo conocidos por el módulo que contenga la
definición.
Paralelo entre el seudocódigo y Pascal
Aspecto
Pseudocódigo
Pascal
Asignación
<-
:=
Relacionales
=, <>, >, <, >=, <=
=, <>, >, <, >=, <=
Operadores lógicos
No, y, o
NOT
Operadores aritméticos
+
Tipos de datos:
Entero
Real
PuntoFijo
Lógico
Carácter
Cadena
Arreglo [1..Max] de real
Registro
<campo>: <tipo>
Fin registro
Conjunto de <tipo>
Archivo de <tipo>
Puntero de <tipo>
-
*
/
div
mod
+
-
AND
*
OR
/
DIV
MOD
INTEGER;
REAL;
--(NO EXISTE)
BOOLEAN;
CHARACTER;
STRING;
ARRAY [1..Max] OF REAL;
RECORD
<campo>: <tipo>;
END;
SET OF <TIPO>
FILE OF <tipo>;
^<tipo>
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 111
Algorítmica y Programación I
Aspecto
Pseudocódigo
Pascal
Operaciones de entrada
salida
Ingresar (edad)
READ (edad) o
READLN (edad)
Mostrar (“Edad=”,edad)
WRITE (“Edad=”,edad) o
WRITELN(“Edad=”,edad)
Si <condición> entonces
<sentencia>
Fin si
IF <condición> THEN
<sentencia>;
Decisión simple
Pascal espera
sentencia simple,
si hay más de una deben
formarse una sentencia
compuesta,
encerrándolas entre
marcas BEGIN - END
!
Decisión doble
Si <condición> entonces
<sentencia1>
...
<sentenciaN>
Fin si
IF <condición> THEN
BEGIN
<sentencia1>;
...
<sentenciaN>;
END;
Si <condición> entonces
<sentenciaV>
Sino
<sentenciaF>
Fin si
IF <condición> THEN
<sentenciaV>
ELSE
<sentenciaF>;
Según <Expresión> hacer
<cte1>: <accion1>
...
<cteN>: <accionN>
Fin Según
CASE <Expresión> OF
<cte1>: <accion1>;
...
<cteN>: <accionN>;
END;
Según <Expresión> hacer
<cte1>: <accion1>
...
<cteN>: <accionN>
Sino <accionD>
Fin Según
CASE <Expresión> OF
<cte1>: <accion1>;
...
<cteN>: <accionN>
ELSE <accionD>;
END;
Mientras <condición> hacer
<sentencia>
Fin Mientras
WHILE <condición> DO
<sentencia>;
Pascal espera
sentencia simple, si Mientras <condición> hacer
<sentencia1>
hay más de una deben
...
formarse una sentencia
<sentenciaN>
compuesta,
Fin Mientras
encerrándolas entre
marcas BEGIN - END
WHILE <condición> DO
BEGIN
<sentencia1>;
...
<sentenciaN>;
END;
Iteración Condicional con
postesteo
REPEAT
Observar que la
sentencia previa al
ELSE NO termina en
punto y coma.
!
Decisión Múltiple
! Observar que la
sentencia previa al
ELSE NO termina en
punto y coma.
Iteración Condicional con
pretesteo
!
Repetir
<sentencia1>
...
<sentenciaN>
hasta <condición>
<sentencia1>;
...
<sentenciaN>;
UNTIL <condición>;
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 112
Algorítmica y Programación I
Aspecto
Pseudocódigo
Pascal
Iteración con contador
Para i<- 1 hasta 10 hacer
<sentencia>
Fin para
FOR i:= 1 TO 10 DO
<sentencia>;
Pascal espera
sentencia simple, si
Para i<- 10 hasta 1 con
hay más de una deben
paso=-1 hacer
formarse una sentencia
<sentencia>
compuesta,
Fin para
encerrándolas entre
marcas BEGIN - END
Para i<- 2 hasta 10 con paso=
2 hacer
<sentencia>
Fin para
!
FOR i:= 10 DOWNTO 1 DO
<sentencia>;
No se puede realizar en
Pascal pasos que no sean 1 o
–1. Debiera realizarse con un
WHILE
Precedencia de operadores en Pascal
La precedencia de los operadores varía de un lenguaje a otro y es muy importante conocerla para evitar
errores cuando se utiliza una expresión compuesta en la que intervienen distintas operaciones.
El orden de precedencia en Pascal es:
Orden
Operación
1
Paréntesis
()
2
Operadores unarios
not,
3
Operadores multiplicativos
*, /, div, mod, and
4
Operadores aditivos
+, -, or
5
Operadores relacionales
=, <>, <, >, <=, >=, in
Esto significa que si quisiéramos expresar si 3 + 2 es mayor que 5 – 2 y 6 es menor que 7, debemos
necesariamente utilizar algunos paréntesis pues sino daría error en la compilación:
3 + 2 >= 5 – 2 AND 6 < 7 -> La primer operación que intenta resolver es 2 AND 6 y se produce un error
de tipo pues el AND requiere operandos lógicos y tiene operandos enteros. Para este caso,
necesariamente debemos utilizar paréntesis para que la operación AND sea la última en resolverse.
(3 + 2 >= 5 – 2) AND (6 < 7) -> La primer operación que intenta resolver es el primer paréntesis, y dentro
de él la suma y resta, por lo restaría evaluar 5 >= 3 que da verdadero. Luego realiza el segundo
paréntesis dando también verdadero. Y finalmente el verdadero AND verdadero que produce como
resultado final verdadero.
Otros ejemplos:
Ejemplo
Resultado
Comentario
3+2*5
13
Se realiza primero el producto
(3 + 2) * 5
25
Se realiza primero el paréntesis
3<2+5
Verdadero
Se realiza primero la suma
3 < 2 + 5 AND 5 > 7
Error
Se realiza primero el AND pero los operandos
son numéricos y no puede resolverlo
(3 < 2 + 5) AND (5 > 7)
Falso
Se realiza primero cada paréntesis y luego el
AND. En el primer paréntesis se realiza
primero la suma y luego el relacional, dando
verdadero, el segundo paréntesis da falso y
finalmente el AND da falso.
Falso OR NOT Verdadero
Falso
Realiza primero el NOT Verdadero y luego el
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 113
Algorítmica y Programación I
OR. En este caso no se necesita el paréntesis.
NOT Falso AND Verdadero
NOT (Falso AND Verdadero)
!
Verdadero
Realiza primero el NOT Falso y luego el AND.
En este caso si se utilizara paréntesis,
cambiaría la expresión pero casualmente daría
el mismo resultado. Primero hace el AND que
le da Falso y luego el NOT que lo convierte a
Verdadero
¡No olvidarse de encerrar en paréntesis los operandos lógicos!!!. En Pascal generalmente resulta
necesario poner en paréntesis las expresiones de los operandos lógicos.
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 114
Algorítmica y Programación I
Compiladores de Pascal
Varios compiladores de Pascal están disponibles para el uso del público en general, entre ellos podemos
mencionar a:
o Compilador GNU Pascal (GPC), escrito en C, basado en GNU Compiler Collection (GCC). Se
distribuye bajo licencia GPL.
o Free Pascal está escrito en Pascal (el compilador está creado usando Free Pascal), es un
compilador estable y potente. También distribuido libremente bajo la licencia GPL. Este sistema
permite mezclar código Turbo Pascal con código Delphi, y soporta muchas plataformas y
sistemas operativos.
o Turbo Pascal fue el compilador Pascal dominante para PCs durante los años 1980 y hasta
principios de los años 1990, muy popular debido a sus magníficas extensiones y tiempos de
compilación sumamente cortos. Actualmente, versiones viejas de Turbo Pascal (hasta la 7.0)
están disponibles para descargarlo gratuito desde el sitio de Borland (es necesario registrarse).
o Dev Pascal es un entorno integrado de desarrollo para crear programas en Pascal basados en
Windows o en consola MS-DOS, utilizando compiladores de Pascal gratis o de código abierto.
También puede manejar un depurador interno, que podrás descargar por separado en su misma
página web.
o Delphi es un producto tipo RAD (Rapid Application Development) de Borland. Utiliza el lenguaje
de programación Delphi, descendiente de Pascal, para crear aplicaciones para la plataforma
Windows. Las últimas versiones soportan compilación en la plataforma .NET.
o Turbo Pascal proporciona siete unidades estándar para el uso del programador: System, Graph,
DOS, Crt, Printer, Turbo3 y Graph3. Las cinco primeras sirven para escribir sus programas y las
dos últimas para mantener compatibilidad con programas y archivos de datos creados con la
versión 3.0 de Turbo Pascal. Las siete unidades están almacenadas en el archivo TURBO/.TPL
(librería de programas residente propia del programa Turbo Pascal). La versión 7.0 introdujo dos
nuevas unidades WinDos y Strings.
Archivos en Turbo Pascal y en Dev Pascal
El tipo de dato archivo suele variar la forma de implementación respecto de la definición estándar del
lenguaje Pascal, ofreciendo mejores características.
Tanto en dev Pascal como en Turbo Pascal existen tres tipos de archivos: de texto (cada elemento es
un caracter), tipados (typed), los elementos son todos del mismo tipo, y sin tipo (untyped), los
elementos pueden tener cualquier formato. El primero sólo admite acceso secuencial, mientras que los
otros dos admiten tanto acceso secuencial como directo.
El constructor File permite crear tipos de datos y variables para trabajar con archivos. La palabra
reservada text representa al tipo de archivo de texto.
Ejemplos:
Type
tRegAlum= Record
Nro: integer;
Nombre: tNombre;
Carrera: Integer;
...
end;
tArchAlum = File of tRegAlum;
{ defino un tipo de archivo tipado)
Var
Alumnos: tArchAlum;
{defino una variable para trabajar con un
archivo tipado}
MisNotas: text;
{defino una variable para trabajar con
archivo de texto}
MiArchivoSinTipo: File; {defino una variable para trabajar con un
archivo sin tipo}
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 115
Algorítmica y Programación I
Operaciones para archivos en Turbo Pascal / Dev Pascal
Las operaciones de abrir, crear y anexar vistas en pseudocódigo se resuelven en dos instrucciones
pascal: una, común a los tres modos, establece la relación entre el nombre físico y la variable archivo:
Assign(varArch, nombArch)
Esta instrucción no da error, ya que no controla si el archivo existe o no.
El equivalente a la operación específica de crear, se da con la operación rewrite(varArch), previamente
hubo que haber hecho el assign. Rewrite requiere como parámetro la variable de tipo archivo ya
vinculada con el nombre físico, crea el archivo vacío: si existía lo destruye.
El equivalente a la operación específica de abrir, se da con la operación reset(varArch), previamente
hubo que haber hecho el assign. Reset requiere como parámetro la variable de tipo archivo ya vinculada
con el nombre físico, el archivo debe existir y se posiciona al comienzo. Si no existe el archivo da un
error.
El equivalente a la operación específica de anexar, sólo válida para archivos de texto, se da con la
operación append(varArch), previamente hubo que haber hecho el assgn. Append requiere como
parámetro la variable de tipo archivo ya vinculada con el nombre físico, si el archivo no existe, da error y
si existía lo abre y se posiciona al final.
Las restantes operaciones vistas en pseudocódigo tienen un equivalente directo en TurboPascal y en
dev Pascal, con idéntico comportamiento.
Operaciones para archivos de texto en TurboPascal y Dev Pascal
En pseudocódigo
En TurboPascal / Dev pascal
Var
arch: tTexto
s: caracter
Var
arch: text
s: char
Crear(arch, nombArch)
Assign(arch, nombArch)
Rewrite(arch)
Abrir(arch, nombArch)
Assign(arch, nombArch)
Reset(arch)
Anexar(arch, nombArch)
Assign(arch, nombArch)
Append(arch)
Leer (arch, s)
Read (arch, s)
LeerLN(arch, s)
Readln(arch, s)
Escribir (arch, s)
Write (arch, s)
EscribirLN(arch, s)
Writeln(arch, s)
esFinLinea (arch)
Eoln(arch)
esFinArchivo (arch)
Eof(arch)
Operaciones para archivos tipados en TurboPascal y Dev Pascal
En pseudocódigo
Tipos
TReg = registro .... FinRegistro
TArch = archivo de tReg
Var
Arch: tArch
reg: tReg
En TurboPascal / Dev pascal
Type
TReg = record .... end
TArch = File of tReg
Var
Arch: tArch
reg: tReg
Crear(arch, nombArch)
Assign(arch, nombArch)
rewrite(arch)
Abrir(arch, nombArch)
Assign(arch, nombArch)
reset(arch)
Leer (arch, reg)
read (arch, reg)
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 116
Algorítmica y Programación I
En pseudocódigo
En TurboPascal / Dev pascal
Escribir (arch, reg)
Write (arch, reg)
esFinArchivo (arch)
Eof(arch)
tamañoArchivo (arch)
FileSize(arch)
dondeEstoy (arch)
FilePos(arch)
Posicionarse (arch, pos)
seek(arch, pos)
Manejo de errores de entrada salida en TurboPascal y Dev Pascal
Tanto Turbo Pascal como Dev Pascal admiten directivas al compilador para activar o no determinados
controles de errores. Normalmente el compilador tiene activado el control de errores de entrada salida,
por lo que al abrir un archivo que no existe, da un error y finaliza la ejecución. Lo mismo sucede si se
ingresa un punto o cualquier carácter no numérico cuando se espera por un valor entero.
Esta directiva se activa con el comentario al comienzo de una línea: {$I+} y se desactiva con {$I-}
Al producirse un error, turbo pascal guarda en una variable global, de nombre IOResult el código de
error. Si desactivamos el control de errores antes de realizar la instrucción que puede provocar error e
inmediatamente después consultamos por el valor IOResult, si este es distinto de cero indica que hubo
error. Así podremos tomar una acción correctiva, si fuera necesario. No debemos olvidarnos de activar el
control de errores nuevamente.
Ejemplos:
Para consultar si un archivo existe, y así evitar errores queriéndolo usar: Crearemos en
TurboPascal una función que retorne verdadero si el archivo existe y falso si no.
Function existeArch(nombre:string):boolean;
Var
F:File;
Begin
Assign(f, nombre); (* vincula f con el nombre de archivo a abrir*)
{$I-}
(* desactivo el control de errores de E/S*)
reset(f);
(* intento abrirlo *)
close(f);
(* intento cerrarlo*)
existeArch:= IOResult = 0; (* Si no hubo error => existe el archivo*)
{$I+}
(* activo el control de errores *)
End;
Ejemplo de uso:
If existeArch("Alumnos.dat") then
{ abrirlo}
else {crearlo}
Para obtener un valor entero de la entrada estándar: Crearemos en TurboPascal una función que
retorne el valor entero (y rechace valores no enteros).
Function obtenerEntero(msg:string):integer;
Var
n: integer;
Begin
{$I-}
Repeat
writeln (msg);
read(n);
until IOResult = 0;
{$I+}
obtenerEntero:= n;
End;
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 117
Algorítmica y Programación I
Otra forma, haciendo uso de la operación val:
El procedimiento Val (E Cad: cadena, S Num: entero o real, S CódigoError: entero) intenta convertir
la cadena Cad en un valor entero o real según sea el tipo del parámetro Num. Si la conversión se pudo
hacer devuelve su valor en Num y cero como de CódigoError, de lo contrario devuelve un valor distinto
de cero como código de error y Num carece de significado.
Haciendo uso de val, podemos reescribir la función de la siguiente manera:
Function obtenerEntero(msg:string):integer;
Var
Cod, n: integer;
s: string;
Begin
Repeat
writeln (msg);
read(s);
val(s, n, Cod)
until Cod = 0;
obtenerEntero:= n;
End;
Cátedra: Algorítmica y Programación I
Profesor: Dr. Diego Andrés Firmenich
Hoja 118
Algorítmica y Programación I
Bibliografía
Algoritmos y Estructuras de datos, Nicklaus Wirth, Prentice Hall, 1986
Algoritmos, Datos y Programas, con Aplicaciones en Pascal, Delphi y Visual Da Vinci, De Giusti et Al,
Prentice Hall, 2001
An introduction and problem solving with Pascal, Schneider, M.; Perlman,D.; Weingart,S. , Wiley, 1978.
Cómo plantear y resolver problemas, Polya,G. , Ed. Trillas, México,1970.
Estructuras de Datos y Algoritmos, Alfred Aho, John Hopcroft y Jeffrey Ullman. Addison Wesley
Publishing Company. EUA. 1988.
Fundamentos de informática, lógica, resolución de problemas, programas y computadoras. Autores Allen
B. Tucker, W. James Bradley, Robert D. Cupper, David K. Garnick Editorial McGraw-Hill Año 1994
Fundamentos de Programación, Algoritmos y Estructuras de Datos. Luis Joyanes Aguilar, Mc Graw Hill,
2008
Introducción a la Programación y a las Estructuras de datos, A. Gioia-S. Braunstein, Eudeba, 1986
Introduction a la programmation. T.L Algorithmique et Languages, Biondi,J.; Clavel,G. , 2da. Edition.
Masson, 1984.
Introduction to Pascal, Welsh,J.; Elder,J. , Prentice Hall, 1982.
Metodología de la programación, Luis Joyanes Aguilar, Mc. Graw Hill,1992
Pascal, Dale,N,; Orshalick , Mc Graw Hill, 1986.
Pascal. User manual and report, Jensen,K.; Wirth,N. , Springer - Verlag.
Problem solving and computer programming, Grogono,P.; Nelson,Sh , Addison-Wesley, 1982.
Programación en Pascal, 4ª ED., Luís Joyanes Aguilar, Editorial McGraw-Hill, 2006
Programación en Pascal, Grogono, P. , 1986 Addison Wesley.
Una Introducción a la Programación. Un Enfoque Algorítmico, J. García Molina y otros, Thomson, 2005
Descargar