Você está na página 1de 129

See

discussions, stats, and author profiles for this publication at: https://www.researchgate.net/publication/27286780

Notas sobre Lenguaje de Programación y


Conceptos para la Calidad del Software

Article
Source: OAI

CITATIONS READS

0 75

1 author:

Luis Eduardo Mathison Bonaguro


Universidad Centro Occidental Lisandro Alvarado, UCLA
11 PUBLICATIONS 19 CITATIONS

SEE PROFILE

Some of the authors of this publication are also working on these related projects:

FACTORES CRÍTICOS DE ÉXITO EN LA GESTIÓN DE LOS EQUIPOS DE INVESTIGACIÓN CIENTÍFICA


UNIVERSITARIOS View project

Interacción entre las tecnologías de información y la organización View project

All content following this page was uploaded by Luis Eduardo Mathison Bonaguro on 05 October 2015.

The user has requested enhancement of the downloaded file.


LENGUAJES DE PROGRAMACION
Y
CALIDAD DE SOFTWARE

LUIS EDUARDO MATHISON BONAGURO

UNIVERSIDAD CENTROCCIDENTAL "LISANDRO ALVARADO"

BARQUISIMETO, 1997
LENGUAJES DE PROGRAMACION
Y
CALIDAD DE SOFTWARE

Por

Luis Eduardo Mathison Bonaguro

UNIVERSIDAD CENTROCCIDENTAL "LISANDRO ALVARADO"


Decanato de Ciencias

Barquisimeto, 1997
Dedico este trabajo a mi esposa Yaya
y en especial a mis hijos Luis y
Stephanie.
AGRADECIMIENTO

A quienes fueron mis profesores cuando estudié Ingeniería,


donde me enseñaron algunos de los conceptos que hoy pretendo
transmitir con el presente trabajo.

A todos aquellos que han sido mis alumnos en las


asignaturas de Programación I, Lenguajes de Programación y
Estructuras de Datos por permitirme impartirles mis conocimientos.

A mis compañeros profesores que de una u otra forma han


compartido conmigo sus conocimientos sobre tan importante tema,
en especial a los profesores del área de Lenguajes de
Programación (Carlos, Pedro y Darwin).

A las personas que laboraron conmigo en MARAVEN y en


la Dirección de Informática de la UCLA, de donde aprendí la gran
importancia de hacer las cosas bien y de la mejor manera, siempre
pensando en el usuario final.

iii
LENGUAJES DE PROGRAMACION
Y
CALIDAD DE SOFTWARE

Resumen

Luis Eduardo Mathison Bonaguro

El producir aplicaciones y sistemas que funcionen


óptimamente no solo depende de haber realizado un buen análisis
y un buen diseño sino también de seleccionar un lenguaje acorde
con la orientación de los programas, utilizar un método de
traducción apropiado a las exigencias tanto de tiempo de repuesta
en la ejecución, como la utilización adecuada de la arquitectura del
computador en función de un buen rendimiento.

La selección de un lenguaje de programación adecuado no


solo implica conocerlo en su sintaxis, orientación, nivel, proceso de
traducción sino en cada uno de sus componentes: caracteres,
símbolos básicos, elementos básicos y proposiciones.

Por otra parte el conocer el funcionamiento interno del


traductor (compilador) produce beneficios intangibles en cuanto
poder determinar la potencialidad del lenguaje y principalmente la
producción de programas ejecutables optimizados y con alto grado
de portabilidad, hoy día con un grado de importancia significativo en
vista del alto costo de los equipos con configuraciones grandes
(mainframe y minicomputadores); permitiéndose el desarrollo en

iv
configuraciones personales y transportando los fuentes para su
posterior traducción en el equipo grande.

El poseer técnicas de programación estandarizadas permiten


la fácil integración de programas implementados individualmente
(módulos) y su posterior mantenimiento. El analizar los algoritmos
bien sea matemáticamente o empíricamente permiten aproximarse
a programas que realicen sus funciones de una manera óptima.

v
INDICE

AGRADECIMIENTO ........................................................................................... III

RESUMEN ......................................................................................................... IV

INDICE ............................................................................................................... VI

INDICE DE ILUSTRACIONES........................................................................... IX

I. INTRODUCCION ......................................................................................... 10

II. LENGUAJES DE PROGRAMACION ....................................................... 13


A. Propósitos de los Lenguajes .................................................................. 13
1. Naturales ............................................................................................ 13
2. Lenguajes de Computación ................................................................ 14
B. Niveles de los Lenguajes de Programación ........................................... 14
1. Bajo Nivel ........................................................................................... 15
2. Alto Nivel ............................................................................................ 16
a. Lenguajes Declarativos ................................................................... 16
b. Lenguajes Alto Nivel 3era Generación.............................................. 17
c. Lenguajes de Mediano Nivel ........................................................... 18
C. Clasificación de los Lenguajes según su Aplicación .............................. 19
a. Científico ......................................................................................... 21
b. Procesamiento de Datos ................................................................. 21
c. Procesamiento de Texto ................................................................. 21
d. Inteligencia Artificial ........................................................................ 22
e. Programación de Sistemas ............................................................. 22

III. ESTRUCTURA DE LOS LENGUAJES DE PROGRAMACION ............... 24


A. Caracteres ............................................................................................. 25
B. Símbolos Básicos de Caracteres Múltiples .......................................... 25
C. Elementos básicos de un lenguaje de programación ............................ 26
1. Valores ............................................................................................... 27
a. Valores Numéricos ........................................................................ 27
b. Valores Lógicos .............................................................................. 30
2. Nombres Simbólicos o Identificadores ............................................... 30
3. Etiquetas ............................................................................................ 31
4. Cadenas ............................................................................................. 32
D. Expresiones ........................................................................................... 33

vi
1. Expresiones Aritméticas Simples ....................................................... 33
2. Expresiones Booleanas ...................................................................... 34
E. Proposiciones ........................................................................................ 34
1. Proposiciones de Asignación ............................................................. 35
2. Proposición de Bifurcación ................................................................. 36
3. Proposición de Prueba y Decisión o Condicional ............................... 36

IV. TRADUCTORES ...................................................................................... 38


A. Compiladores......................................................................................... 39
1. Funciones de un Compilador .............................................................. 40
B. Interpretadores ...................................................................................... 42
1. Pasos de un Interpretador .................................................................. 44
2. Interpretadores VS Compiladores ...................................................... 45
C. Ensambladores ...................................................................................... 46
1. Pasos de un Ensamblador ................................................................. 46

V. FUNCIONAMIENTO DEL COMPILADOR ................................................ 50


A. Análisis Léxico ....................................................................................... 50
1. Función............................................................................................... 50
2. Base de Datos .................................................................................... 51
3. Algoritmo ............................................................................................ 52
B. Análisis Sintáctico .................................................................................. 53
1. Función............................................................................................... 53
2. Bases de Datos .................................................................................. 54
3. Reconocedores .................................................................................. 54
a. Reconocedor Descendente (TOP-DOWN)...................................... 55
b. Reconocedor Ascendente (BOTTOM-UP) ...................................... 56
c. Arboles Sintácticos .......................................................................... 58
C. Generación de Código Intermedio o Fase de Interpretación ................. 59
1. Función............................................................................................... 59
2. Bases de Datos .................................................................................. 60
3. Tipos de Código Intermedio ............................................................... 60
a. Notación Posfija o Polaca. .............................................................. 60
b. Arbol Analítico ................................................................................. 61
c. Código de Tres Direcciones ............................................................ 63
d. Matriz de Formas Intermedias ......................................................... 64
D. Optimización .......................................................................................... 65
1. Función............................................................................................... 65
2. Tipos de Optimización ........................................................................ 66
3. Criterios de Optimización Independiente ............................................ 67
a. Eliminación de Subexpresiones Comunes ...................................... 67
b. Eliminación de Operaciones entre Constantes o Cálculos en
Tiempo de Compilación ................................................................................ 68
c. Optimización de Expresiones Booleanas ........................................ 68
d. Eliminación de Redundancia ........................................................... 69

vii
e. Reducción de Potencia ................................................................... 70
f. Reducción de Frecuencias ................................................................. 70
4. Optimización de la Matriz de Formas Intermedias.............................. 71
E. Asignación de Almacenamiento............................................................. 73
1. Función............................................................................................... 73
2. Bases de Datos .................................................................................. 74
F. Generación de Código ........................................................................... 74
1. Función............................................................................................... 74
2. Definición de un Código Ensamble Ilustrativo .................................... 74
3. Optimización Dependiente ................................................................. 76
4. Contenido del programa objeto .......................................................... 78
G. Análisis Semántico ................................................................................ 78
1. Función............................................................................................... 78
H. Manejador de Errores de Compilación .................................................. 78
1. Función............................................................................................... 78
2. Tipos de Errores ................................................................................. 79
I. Otras Salidas del Compilador ................................................................... 79
J. Pases del Compilador ............................................................................ 79

VI. CALIDAD A TRAVES DE LA INGENIERIA DE SOFTWARE .................. 81

VII. SOLUCION DE PROBLEMAS COMPUTARIZADOS ........................... 86


A. Técnicas de Programación .................................................................... 87
B. Algoritmos .............................................................................................. 90
1. Análisis de Algoritmo .......................................................................... 91
2. Marco de Referencia para el Análisis de Algoritmos .......................... 92
3. Clasificación de los Algoritmos ........................................................... 94
4. Implementación de Algoritmos ........................................................... 98
5. Selección de un Algoritmo .................................................................. 99
6. Análisis Empírico .............................................................................. 102
7. Optimización de un Programa .......................................................... 104
C. Estándares........................................................................................... 107
1. Identificadores y Literales ................................................................. 107
2. Nombres de Programas, Archivos de Datos, etc. ............................. 108
3. Pantallas ........................................................................................... 110
4. Otras Estandarizaciones .................................................................. 112

VIII. SELECCION DE UN LENGUAJE DE PROGRAMACION .................. 114

IX. LA CALIDAD DEL SOFTWARE EN 45 TERMINOS .............................. 118

BIBLIOGRAFIA ............................................................................................... 126

viii
INDICE DE ILUSTRACIONES

Página

1. CICLO DE VIDA DE UN SISTEMA ............................................................................... 11


2. CARACTERÍSTICAS DE LOS LENGUAJES ................................................................... 15
3. COMPARACIÓN DE UNA INSTRUCCIÓN EN LENGUAJE DE ALTO NIVEL Y BAJO NIVEL. ...... 19
4. COMPLEJIDAD DE SOFTWARE................................................................................. 20
5. APLICACIONES Y LENGUAJES SEGÚN SU UTILIZACIÓN. .............................................. 23
6. COMPOSICIÓN DE LOS LENGUAJES NATURALES Y DE PROGRAMACIÓN........................ 24
7. ENLACE ANÁLISIS LÉXICO Y SINTÁCTICO. ............................................................... 50
8. REPRESENTACIÓN DE REDUCCIONES SINTÁCTICAS ................................................. 54
9. MODELO DE DESARROLLO EN CASCADA DE SOFTWARE ............................................ 82
10.MODELO DE DESARROLLO REITERATIVO DE SOFTWARE............................................ 83
11.ESFUERZO INVERTIDO EN CADA ETAPA DE CICLO DE VIDA DEL SISTEMA.

(SITUACIÓN REAL Y ESPERADA) ............................................................................ 85


12.PLANTILLA SISTÉMICA. .......................................................................................... 86
13.PARADIGMA PROCEDIMIENTAL ............................................................................... 88
14.PARADIGMA BASADO EN OBJETOS. ........................................................................ 89
15.VALORES RELATIVOS APROXIMADOS DE FUNCIONES ............................................... 97
16.PREFIJOS CLASIFICADORES ESTÁNDARES. ............................................................ 109
17.CARACTERÍSTICAS DE ALGUNOS LENGUAJES DE PROGRAMACIÓN ........................... 115

ix
I. INTRODUCCION

Cada vez que los usuarios adquieren una nueva aplicación o un


sistema surgen comentarios sobre la rapidez de funcionamiento, el
espacio ocupado en el medio de almacenamiento, configuración
requerida (gran capacidad de memoria, procesador de alta
velocidad, etc.), sí las pantallas son complicadas de entender o son
poco amigables, que no satisface todas las necesidades; estos y
posiblemente otros comentarios indirectamente evalúan al trabajo
realizado durante el análisis, el diseño y la implementación de los
programas que conforman a la aplicación o al sistema.

La mayoría de las veces se culpa al análisis o al diseño y en


escasas oportunidades se responsabiliza lo que es propiamente el
programa desde el punto de vista de su implementación
(características del lenguaje utilizado, algoritmo implantado, tipo de
instrucciones usadas, proceso de traducción requerido del lenguaje,
manejo del hardware por parte del lenguaje, etc.), además se debe
considerar que el mayor tiempo de la vida útil de un programa se
consume en proceso de mantenimiento (ilustración 1), es por ello
que la utilización de estructuras adecuadas, lenguajes de
programación de fácil entendimiento y poca dificultad, procesos de
traducción poco tediosos y otros factores redundan en la
producción de mejores programas.

Un hecho que guarda cierta semejanza es lo que sucede con la


construcción de una vivienda. El arquitecto o ingeniero debe
levantar información para determinar cuales son las necesidades y
exigencias del propietario (análisis), posteriormente con el análisis

10
11

elabora los planos y maquetas del inmueble a construir (diseño),


repitiendo los pasos anteriores hasta que el propietario indica que
todo se adapta a sus exigencias. Hasta aquí la construcción de la
vivienda no puede fallar ya que sólo se ha diseñado lo exigido por
el usuario. Posteriormente se inicia la etapa del desarrollo de la
obra (implementación), donde se deben considerar algunos
factores como: calidad de los materiales, materiales más
adecuados, experiencia de los obreros y maestro de obra (redunda
en el tiempo de desarrollo y calidad del acabado) y otros factores
no menos importantes. Obsérvese que en cada fase se ha indicado
la similitud que guarda con los pasos de desarrollo de un sistema;
y como no sólo el análisis y el diseño redundan en la óptima
generación de un producto.

Análisis

Madurez y Fases de
Diseño
Mantenimiento Investigación

del Sistema de Sistemas

Implementación

Ilustración 1. Ciclo de vida de un sistema


12

El presente trabajo pretende suministrar una gama de


conocimientos sobre los lenguajes de programación, traductores,
parámetros para seleccionar lenguajes, solución de problemas
computarizados, algoritmos y pautas para la evaluación de
algoritmos, con la finalidad de contar con elementos de juicio
suficientes para la evaluación del proceso de implementación así
como facilitar el mantenimiento de programas.
II. LENGUAJES DE PROGRAMACION

Los seres humanos debido a la necesidad de comunicarse entre


sí han formado sus propios lenguajes, con estructuras muy bien
establecidas, ver Capítulo III; por otra parte el hombre a medida que a
fabricado equipos computarizados a buscado la forma de comunicarse
con ellos y ordenarle las acciones que él desea que realice, de allí
nacen los LENGUAJES DE PROGRAMACION.

Un LENGUAJE DE PROGRAMACION consiste en una serie


específica de caracteres que permiten dar instrucciones a un
computador, con la finalidad de lograr que ella realice ciertas acciones
en la solución de algún problema computarizado. Cada lenguaje tiene
características propias en cuanto a su GRAMATICA Y su SINTAXIS.

El estudio de los lenguajes de programación se puede realizar


desde dos puntos de vista, tomando en cuenta su proximidad al
lenguaje máquina (nivel) y según sea su principal campo de aplicación
(científica, texto, etc.).

A. Propósitos de los Lenguajes

1. Naturales

Los lenguajes naturales o bien conocidos como lenguajes


humanos no son más que un medio para lograr la comunicación de
pensamiento.

13
14

Como lenguaje presenta ciertas características importantes de


mencionar:

 Permite la transmisión de muchos pensamientos a

diferentes niveles y con diferentes significados.

 Una descripción de un pensamiento puede ser impersonal


y objetiva o emocional y subjetiva. Puede utilizarse la
prosa y la poesía o la descripción formal, técnica,
humorística, etc.

2. Lenguajes de Computación

Su propósito es más restringido, son limitados por la capacidad


del hardware y del software.

 No se requiere que comuniquen una gran variedad de


pensamientos, es suficiente que permitan la transmisión
de solo cuatro pensamientos básicos (cuatro tipo de
operaciones).
 Es imprescindible que la comunicación sea precisa y clara,
es decir, no debe admitir ambigüedades.

B. Niveles de los Lenguajes de Programación

Los lenguajes de programación se pueden clasificar según su


cercanía o similitud al lenguaje máquina o lenguaje binario, a esto
se le conoce como nivel del lenguaje. Mientras más parecido sea el
lenguaje al lenguaje máquina o binario su nivel será menor y
15

contrapuesto se tendrá los lenguajes de alto nivel que se asemejan


a los lenguajes naturales. Es por ello que los lenguajes a medida
que son de más alto nivel requieren de procesos de traducción más
complejos, además de presentar mayor similitud a los lenguajes
naturales, tal y como se visualiza en la ilustración 2.

1. Bajo Nivel

Este nivel corresponde al lenguaje de programación


ASSEMBLER o lenguaje Ensamblador que está asociado a un
respectivo lenguaje máquina. El lenguaje ensamblador es
simplemente una representación simbólica del lenguaje máquina
asociado, lo cual permite una programación menos tediosa que con
el lenguaje máquina correspondiente. Este lenguaje requiere que el
programador conozca profundamente la arquitectura del equipo,
además que presenta una complicación de programación mayor
que con los lenguajes de alto nivel, obsérvese la ilustración 3.

Lenguajes Declarativos Amistoso con el usuario Alto Nivel


Menos instrucciones en
programas
era.
3 Generación transparencia de
la máquina

Lenguaje C

Assembler

Código Máquina Eficiencia de procesamiento Bajo Nivel

Ilustración 2. Características de los lenguajes


16

2. Alto Nivel

Son los lenguajes de programación más modernos, presentan


una gran facilidad para el programador en cuanto a la facilidad de
realizar programas debido a que poseen
MACROINSTRUCCIONES.

Se entiende por MACROINSTRUCCION una instrucción escrita


en un lenguaje de diferente a Assembler o lenguaje máquina que
mediante un proceso de traducción es convertida en otro grupo de
instrucciones, en lenguaje máquina, que realizan una acción
específica.

Los lenguajes de alto nivel pueden ser divididos en distintos


grupo, en este caso no existe un criterio único y cada autor los
divide según sea su forma de pensar, siendo una de ellas la
siguiente:

 Lenguajes de Alto Nivel de 4ta. Generación o Lenguajes


Declarativos.
 Lenguajes de Alto Nivel de 3era. Generación.
 Lenguajes de Alto Nivel Especiales o de Mediano Nivel.

a. Lenguajes Declarativos

Estos lenguajes son los de corte más moderno. Entre sus


características tenemos:
17

 Gran parecido con los lenguajes formales en cuanto a su


potencia expresiva y funcional.
 Son lenguajes de ordenes, indican "lo que hay que
hacer" y no "como hacerlo".
 Son fáciles de aprender y de programar, no requiere de
alto conocimiento de computación.
 Requiere traductor.

Entre estos lenguajes tenemos: NATURAL 2, IMS, SQL


(consulta de bases de datos), SAS y SPSS.

b. Lenguajes Alto Nivel 3era Generación

Estos lenguajes presentan como características principal


que son orientados al programador, pero además presentan
otras características propias, que se detallan a continuación:

 Fácil legibilidad y comprensión entre programadores.


 Tienen una alta transportabilidad, son independientes de
la máquina.
 Programación rápida, gracias a las macroinstrucciones.
 Requiere traductor.
 No requiere que el programador conozca de la
arquitectura de la máquina.

Como ejemplo de estos lenguajes tenemos: PASCAL, ADA,


MODULA, COBOL, BASIC, ALGOL, PROLOG, LISP, ETC., en la
actualidad existen más de 200 lenguajes, muchos de ellos con
un elevado número de variantes o dialectos.
18

c. Lenguajes de Mediano Nivel

Estos lenguajes presentan características iguales a los


lenguajes de alto nivel de 3era generación, pero incluyen una
característica especial que los a ayudado a tener una gran
importancia en la actualidad como es la de permitir el acceso a la
arquitectura de la máquina con un potencial similar al de los
lenguajes de bajo nivel, pero con la facilidad de los de alto nivel.
Una de las aplicaciones principales que se está dando en la
actualidad es la de construcción de sistemas operativos (UNIX),
manejadores de bases de datos (DBASE), otros lenguajes de
programación (CLIPPER). Un ejemplo de este tipo de lenguajes
es el LENGUAJE C.

Todos las características que vistas de los lenguajes de alto


nivel pueden decirse que son positivas, pero también presentan
características negativas como:

 Tienen un alto tiempo de traducción (compilación).


 No se aprovechan las posibles ventajas de la arquitectura
interna de la máquina (caso especial los de mediano nivel).
 Se incrementa la ocupación de memoria interna, tanto por
parte del programa compilador como por el propio programa
objeto resultante.
 El tiempo de ejecución es mayor puesto que las
instrucciones generadas por el compilador son más
numerosas que las correspondientes al mismo programa
19

escrito directamente en ASSEMBLER o lenguaje máquina.

A continuación se muestra un ejemplo de la expresión


matemática en lenguaje natural X = W + X * Y, donde se coloca
instrucciones equivalentes en PASCAL, ASSEMBLER y
LENGUAJE MAQUINA.

PASCAL ASSEMBLER LENGUAJE MAQUINA

Z:=W + X * Y L 3,X 41 3 0C1A4


M 2,Y 3A 2 0C1A8
A 3,W 1A 3 0C1A0
ST 3,Z 50 3 0C1A4

Ilustración 3. Comparación de una instrucción en lenguaje de alto nivel y


bajo nivel.

Como muestra la ilustración anterior, a más bajo nivel de


lenguaje más cerca está de las características de un tipo de
máquina particular y más alejado de ser comprendido de un
humano ordinario. Se puede notar una estrecha relación
(correspondiente 1 a 1) entre las sentencias en ASSEMBLER y su
lenguaje máquina asociado.

C. Clasificación de los Lenguajes según su Aplicación

Según sea la utilización u orientación que posean la mayoría de


las aplicaciones elaboradas, los lenguajes de programación se
20

pueden clasificar de la siguiente manera:

 Científico.
 Procesamiento de Datos.
 Procesamiento de Texto.
 Inteligencia Artificial.
 Programación de Sistemas.

Los lenguajes de programación han evolucionado en cuanto a


capacidades y complejidad a la par de las exigencias que se han
originado al desarrollarse requerimientos más complejos de
software, tal y como se visualiza en la siguiente ilustración:

Conocimiento Complejidad
información
Datos
Números
Capacidad
1950 1960 1970 1980 1990 2000

Ilustración 4. Complejidad de software

A continuación se mencionan algunas de las principales


características de cada clasificación según su orientación:
21

a. Científico

Son lenguajes que permiten las solución de problemas


donde existe una alta manipulación de números y arreglos de
números usando principios matemáticos y estadísticos.
Usualmente la cantidad de datos en tales problemas es
relativamente pequeña (del orden de unos cientos de números) y
su estructura es relativamente simple. Frecuentemente los
problemas científicos requieren de alto procesamiento y bajo
proceso de E/S.

Ejemplo de lenguajes científicos PASCAL, FORTRAN, APL,


ALGOL, etc.

b. Procesamiento de Datos

Son lenguajes con el propósito de permitirle al programador


elaborar programas donde el fin principal es el de crear,
mantener y extraer datos contenidos en registros y archivos. El
volumen de datos que se manipula es generalmente grande,
pero por lo contrario la realización de procesos aritméticos es
baja, incrementándose el número de procesos de E/S.

Por lo general los procesos son de tipo BATCH en vez de


iterativo. Ejemplo de lenguajes de procesamiento de datos:
COBOL, PL/I, etc.

c. Procesamiento de Texto

Su principal característica es la de procesar texto en


lenguaje natural. El texto procesado no tiene ninguna restricción
22

en cuanto al lenguaje de programación utilizado para el


desarrollo de aplicaciones de textos.

Ejemplo de lenguajes de procesamiento de textos:


SNOBOL, LENGUAJE C, etc.

d. Inteligencia Artificial

La condición principal de estos lenguajes es la de permitir la


implementación de aplicaciones que simulan un comportamiento
inteligente. Estos lenguajes son muy utilizados en aplicaciones
como juegos de ajedrez, programas de compresión del lenguaje
natural, robótica y sistemas expertos.

Algunos lenguajes de aplicaciones de inteligencia artificial


son: PROLOG, LISP, etc.

e. Programación de Sistemas

Son lenguajes de programación que permiten el desarrollo


de programas que hacen interfaz entre la computadora
(hardware), el programador y/o el operador. Dentro de las
aplicaciones comúnmente desarrolladas en estos lenguajes
tenemos: Compiladores, Ensambladores, Traductores, Rutinas
de E/S, Facilidades de Gestión, Sistemas Operativos, etc.

Dos de las características que poseen las aplicaciones


que se pueden desarrollar con estos lenguajes son: la necesidad
de tratar con sucesos impredecibles o excepcionales y la
necesidad de coordinar actividades de varios programas o tareas
23

ejecutándose asincronamente.

Algunos de los lenguajes de esta clasificación son:


ASSEMBLER, LENGUAJE C, ADA, etc.

En la ilustración 5 se muestran algunos procesos, el área


hacia el cual están orientados y algunos lenguajes que pueden
utilizarse en su implementación.

AREA DE
PROCESO APLICACION LENGUAJES

Inversión de Científico PASCAL, FORTRAN,ETC.


Matrices

Mantenimiento de Procesamiento COBOL, PL/I, ETC.


un archivo de datos
empleados

Formateador de Procesamiento SNOBOL, LENG. C, ETC.


Textos de textos

Misioneros y Inteligencia PROLOG, LISP, ETC.


Caníbales Artificial

Planificador Programación ASSEMBLER, ADA, ETC.


de Trabajos de Sistemas

Ilustración 5. Aplicaciones y lenguajes según su utilización.


III.ESTRUCTURA DE LOS LENGUAJES DE
PROGRAMACION

Los lenguajes de programación al igual que los lenguajes


naturales poseen componentes muy bien diferenciados, es decir,
posee símbolos, palabras, oraciones, párrafos; denominados de
otra manera pero con la equivalencia respectiva, tal y como se
visualiza en la ilustración 6.

LENGUAJES NATURALES LENGUAJES DE PROGRAMACIÓN

Párrafos Proposiciones

Oraciones Expresiones

Tipo de Palabras Elementos Básicos


(verbos, sustantivos, etc.)

Palabras Símbolos Básicos

Caracteres Caracteres

Ilustración 6. Composición de los lenguajes naturales y de programación.

24
25

A. Caracteres

Son los símbolos básicos con los cuales se construyen


estructuras sintácticas más complejas. Los lenguajes naturales
tienen alfabeto constituido por: letras (mayúsculas y minúsculas),
dígitos y caracteres especiales ( ; , :, ? , etc.) estos símbolos son
arbitrarios pero se les da un significado específico por algún
acuerdo. Por otra parte, los lenguajes de computación también
tienen un alfabeto (llamado comúnmente juego de caracteres),
estos alfabetos en algunos casos están directamente ligados a la
capacidad de hardware y en otros caso dependen del lenguaje que
tendrá un subconjunto de los caracteres que el hardware permite.

B. Símbolos Básicos de Caracteres Múltiples

Son las palabras claves, reservadas o no, o los símbolos


básicos delimitados. Tienen su significado específico lo que
implica que en su interpretación no habrá ambigüedad. El
traductor reconoce este significado y el programador debe
emplear el símbolo básico de caracteres múltiples exactamente
en el sentido definido. Se dan casos en el cual dos caracteres
especiales se concatenan para formar una simbología básica.
Ejemplo : PASCAL la asignación es: :=

A continuación se presentan algunos símbolos básicos de


caracteres múltiples de Fortran, Cobol y Pascal.

 FORTRAN: Admite palabras claves (no reservadas) y


las palabras delimitadas. Algunas de ellas son: INTEGER,
26

REAL, COMPLEX, LOGICAL, CHARACTER,


DIMENSION, DATA, FORMAT, IMPLICIT, READ.
DOUBLE PRECISION, WRITE, PRINT, STOP, IF,
THEN, ELSE, ENDIF, DO, ENDDO, END, CALL,
GOTO, RETURN, WHILE.

Palabras Delimitadas:
.EQ., .NE.,.LT., .LE., .GT., .GE.,.TRUE., .FALSE. ,
.AND., .OR., .NOT.

 COBOL: palabras claves reservadas (no admite


delimitadas). Algunas son: ACCEPT, DELETE,
INITIATE, PICTURE, ACCESS, DELIMITED, INPUT,
PLUS, ADD, IS, DELIMITER, POINTER, POSITION,
AFTER, ALL, DISPLAY, DETAIL, ADVANCING,
DESCENDING, TABLE, LINE, DIVIDE, PROCEDURE,
PROCEED, LEFT.

 PASCAL: posee palabras claves reservadas, no admite


delimitadores. Se pueden mencionar: PROGRAM, VAR,
CONST, TYPE, PROCEDURE, FUNCTION, UNIT,
WHILE, DO, BEGIN, END, AND, NOT, OR, IF, THEN,
ELSE, REPEAT, INTEGER, STRING, CHAR,
BOOLEAN, ARRAY.

C. Elementos básicos de un lenguaje de programación

Son las estructuras sintácticas correspondientes a los


conceptos más sencillos de los lenguajes de computación que
27

manejan los algoritmos. Una vez que se tiene el conjunto de


los símbolos básicos, estos pueden ser utilizados para formar
elementos del lenguaje a diferentes niveles de complejidad
(diferentes unidades sintácticas).

Los elementos básicos (E.B.) son: valores, nombres o


identificadores, etiquetas y cadenas.

1. Valores

En cada lenguaje de programación existen valores que


representan números y otras que indican resultados o situaciones
lógicas.

a. Valores Numéricos

Formados por dígitos y algunos caracteres especiales


reglamentados por el álgebra (+ - . ,)

Ejemplo de valores numéricos:

 FORTRAN: clasifica los valores numéricos en enteros,


reales y complejos.

 Enteros: secuencia de dígitos sin punto decimal,


opcionalmente puede estar precedido por un signo +

o -.

 Reales: secuencia de dígitos que pueden estar


expresados en la notación ordinaria (con un
28

punto decimal) o en notación científica (la mantisa


puede llevar o no punto decimal), el exponente se
indica con la letra E o D, según se quiera la
precisión sencilla o doble, seguida de una
constante entera de no mas de dos dígitos.

 Complejos: es un par ordenado de valores reales


encerrados entre paréntesis. El primer valor
representa la parte real y el segundo la parte
imaginaria.

 Ejemplos:
ENTEROS: 40 +25 -1232
REALES: 1.555 +0.0001 25 E+45 .34 D-10
COMPLEJOS: (1.55, 2.3)

 COBOL: define todos sus valores como literales


(literal es una constante que no es identificada por un
nombre de dato o identificador). El valor de un literal
puede ser numérico, no numérico o una constante
figurativa.

 Literal Numérico: secuencia de dígitos (0,1,...,9)


opcionalmente precedidos por un signo + o - y un
punto decimal, cuando no lleva punto decimal se
considera entero. Puede contener hasta 18 dígitos.

 Literal No Numérico: secuencia de caracteres


pertenecientes al conjunto de caracteres del
COBOL y deben estar encerrados entre comillas y
29

no mayor de 120 caracteres (depende de la


versión). El conjunto de caracteres del COBOL se
limita a: A..Z, 0..9, Blanco, <, , , +, -, $, *, <, (, /, ), >, ;,
=, "

 Ejemplos:
Numéricas: 100 4.46 -8.97 +78 +0.99
No Numéricas: "Barquisimeto"

 Constantes Figurativas: es un tipo especial de


literales que a través de un palabra clave del
COBOL representa a un valor constante definido por
el lenguaje. A continuación se presentan algunas
constantes figurativas del COBOL.

Const. Figurativa Valor que representa


ZERO, ZEROS, ZEROES Literal numérico o no numérico
constituido únicamente por
ceros. "0", "000"
SPACE, SPACES Literal no numérico formado
únicamente por blancos.
HIGH-VALUE, Literal no numérico formado
HIGH-VALUES por el valor mas alto de la
secuencia de cotejo para el
conjunto de caracteres. "9",
"999"
LOW-VALUE, Similar al anterior pero por
LOW-VALUES el valor más pequeño. " "
ALL literal Literal no numérico formado
por la secuencia de lo indicado
por el literal.
30

 Ejemplo:
WORKING-STORAGE SECTION.
77 TOTAL-SUMA PIC 999 VALUE ZERO.
77 LINEA-PUNTO PIC X(20) VALUE IS ALL ".".
01 ENCABEZADO.
02 FILLER PIC X(10) VALUE SPACES.
02 SUELDO PIC ZZ9V99.

b. Valores Lógicos

Están representados por valores de verdadero o falso y


el manejo de estos valores está controlado por el álgebra de
BOOLE.

Ejemplo de valores lógicos:

 FORTRAN: .TRUE. .FALSE.


 COBOL: No posee valores lógicos.

2. Nombres Simbólicos o Identificadores

Formados por uno o más caracteres válidos generalmente


letras o dígitos, el primero de los cuales debe ser alfabético
(excepto en el COBOL que puede ser un dígito), no acepta
caracteres especiales salvo algunas excepciones. Los
identificadores son utilizados para representar nombres de
31

variables simples o con subíndice (arreglos), para señalar


nombres de procedimientos o subprogramas y constantes. Un
identificador generalmente representa una dirección simbólica.

Ejemplo de identificadores:

 FORTRAN: los identificadores pueden tener hasta 6


caracteres alfabéticos (A..Z) y/o dígitos (0..9), el
primero debe ser alfabético y no se aceptan caracteres
especiales. Además sí el carácter de inicio del
identificador es I,J,K,L,M la variable automáticamente
será asumida como tipo entero. Ej.: SUELDO, X23, A, N.

 COBOL: es una secuencia de caracteres alfabéticos


(A..Z), y/o dígitos (0..9) y/o el guión (-), hasta una
longitud máxima de 30, donde al menos uno debe
ser alfabético. El primero puede ser un dígito. El guión
no puede ser ni el primero ni el último. Ej.: VENTAS-
NETAS, 1SALDO-ANTERIOR, 123N.

3. Etiquetas

Utilizadas para señalar ciertas instrucciones de un programa,


cuando se requiere bifurcar la secuencia de ejecución del
programa desde alguna instrucción a la indicada por la
etiqueta.

 FORTRAN: admite etiquetas formadas por números


enteros sin signo. Ej. 40, 80, 1, 10, 14, 12 ...
32

 COBOL: permite etiquetas formadas por números o


identificadores. Ej.: 20, LECTURA-REGISTRO.

Es importante comentar que tanto el FORTRAN como el


COBOL por ser lenguajes de formato fijo las etiquetas deben
aparecer en ciertas posiciones de la instrucción a la que señalan.
FORTRAN entre la columna 1 y la 5. COBOL en el MARGEN A,
es decir a partir de la columna 8.

4. Cadenas

Conjunto de caracteres que no representan ningún significado


especifico para el compilador, es decir, no representa dirección
simbólica. Las cadenas se utilizan para dar mensajes o
encabezados en la salida de algún medio visual.
Ejemplos:
 FORTRAN:
' ESTA ES UNA CADENA EN FORTRAN'
52HESTA ES OTRA FORMA DE EXPRESAR UNA
CADENA EN FORTAN

 COBOL:
"CADENA EN COBOL"

Es de hacer notar que los elementos básicos se utilizan


para manejar los conceptos mas simples del calculo que se
presentan en la practica, por eso:
33

 Se cuenta con:
a) Medios para nombrar las variables.
b) Medios para escribir las constantes.

 Se tienen medios para:


a) Señalar una instrucción cuando se quiere bifurcar la
secuencia de ejecución desde un punto del
programa a la instrucción señalada.
b) Almacenar cadenas de caracteres que luego
pueden ser utilizados para dar ciertos mensajes de
interés.

D. Expresiones

Son unidades sintácticas mayores formadas por la


agrupación definida de los símbolos básicos y los elementos
básicos. Constituyen una regla que define el calculo de ciertos
resultados. Si el resultado es:

 numérico: se trata de una expresión aritmética.


 lógico: se denomina expresión booleana.
 etiqueta: se denomina expresión de designación.
 cadena: se denomina expresión de cadena.

1. Expresiones Aritméticas Simples

Corresponden en estructura y significado al uso algebraico


del término. Los lenguajes de alto nivel definen la sintaxis de
las expresiones aritméticas simples de tal manera que
34

permiten el uso de la notación matemática casi similar a la


usada por el lenguaje natural, salvo algunas diferencias se
usa una sola línea para indicar el texto de la expresión
aritmética, las operaciones deben indicarse explícitamente, se
requiere algunas diferencias para indicar las fracciones,
subíndices, potencias, etc. la evaluación de las expresiones
aritméticas siguen las reglas de la aritmética salva algunas
excepciones.

2. Expresiones Booleanas

La regla para la evaluación de la condición especificada


es determinada por la expresión booleana simple que define
la condición.

Ejemplos:
A .LT. B (FORTRAN)
A < B (FORTRAN versiones recientes)
A IS GREATER THAN B (COBOL)
A GREATER THAN B (COBOL)
A IS GREATER B (COBOL) las tres son equivalentes.

E. Proposiciones

Grupo de instrucciones consecutivas que describen un


algoritmo. Las instrucciones se ejecutan secuencialmente hasta
tanto no se diga lo contrario.

Todo programa implica cuatro áreas esenciales y


35

especificas:

 Evaluación y almacenamiento del valor calculado.


(Proposición de Asignación)
 Bifurcación a una instrucción diferente a la que
correspondería ejecutar en la secuencia normal.
(Proposición de Bifurcación)
 Ejecución de prueba de un valor para tomar una
decisión según el resultado de dicha prueba.
(Proposición de Prueba o Decisión).
 Provisión de E/S.

Todo lenguaje de alto nivel tiene proposiciones que


permiten comunicar las tareas básicas de EVALUACION,
BIFURCACION y TOMA DE DECISIONES, las cuales operan
de manera similar en los diferentes lenguajes. En cambio las
proposiciones de E/S son diferentes, porque se refieren a rutinas
comunes a ser utilizadas por todos los programadores y usuarios.

1. Proposiciones de Asignación

Implica la evaluación de una expresión a la derecha de una


símbolo que indica la asignación y el almacenamiento del valor
calculado en una dirección simbólica indicada por un identificador
a la izquierda del operador de asignación.

Ejemplos:
PASCAL B := X + Y - Z * K;
FORTRAN B=X+Y-Z*K
COBOL COMPUTE A = X + Y - Z * K.
36

ADD X, Y TO W GIVING TOTAL.

2. Proposición de Bifurcación

Permiten alterar el orden lógico de ejecución de las


instrucciones, es decir permite que el programa no se ejecute
secuencialmente. Su instrucción básica es el GO TO.

 COBOL. 11111111112
..8901234567890... Posición (Margen)

ETIQUETA.
COMPUTE W = A + B - C - D.

GO TO ETIQUETA.

 FORTRAN 1111
1234567890123... Posición (Margen)

45 W=B+C-D

GO TO 45

3. Proposición de Prueba y Decisión o Condicional

Se utiliza para tomar una decisión basándose en el


resultado de una prueba de una valor el cual debe ser
verdadero o falso.
37

 FORTRAN:
a) IF ARITMETICO: IF (A-B) 10,100,20

Esta proposición de bifurcación es equivalente en


Pascal a la siguiente instrucción:

IF (A-B)<0 THEN GOTO 10


ELSE IF (A-B)=0 THEN GOTO 100
ELSE GOTO 20;

b) IF LOGICO: IF (A.EQ.B) X=B+C

c) BLOQUE: IF (A<B) THEN ....


ELSE ....

 COBOL:

IF SUELDO-BRUTO IS LESS THAN 100 GO TO


CALC.
IF SUELDO<100 COMPUTE X = B+C, ELSE
COMPUTE X=0.
IF SUELDO IS GREATER 100 AND HORAS LESS
THAN 40
NEXT SENTENCE, OTHERWISE PERFORM
CALCULAR.
IV.TRADUCTORES

Cada vez que se requiere ejecutar un programa debe encontrarse


en un lenguaje que el computador pueda entender y que le permita a
este realizar las acciones indicadas; como se mencionó en el
CAPITULO II, los lenguajes se pueden clasificar en niveles y a medida
que el nivel es mayor el traductor requerido es más complejo, al igual
que el proceso de traducción.

El número de pasadas de un traductor puede depender de la


gramática del lenguaje fuente, de sí optimiza o no
(especialmente de las técnicas de optimización independiente
utilizadas) y de la asignación dinámica del almacenamiento. Una regla
sintáctica que influye para que un traductor realice más de una
pasada es la de que el lenguaje fuente admita que la declaración de
variables puede hacerse después que ellas hayan sido utilizadas,
como es el caso de algunas versiones de PL/I y de ciertos Assembler.

El proceso que realizan los traductores se puede resumir en


los siguientes pasos:
 Leer programa fuente.
 Realizar verificación de errores del programa fuente
 De no existir error realizar traducción a programa objeto
de lo contrario almacenar errores
 repetir pasos anteriores hasta finalizar programa fuente

Existen casos especiales donde el traductor realiza la


ejecución de cada instrucción una vez finalizada la traducción de esta
y no genera programa objeto.

38
39

A. Compiladores

Los compiladores son programas especializados en la


traducción de programas fuentes escritos en lenguaje de alto nivel a
programas objetos en código máquina.

Para ejecutar un programa escrito en un lenguaje de alto


nivel es necesario, por tanto compilarlo primero. No obstante, con la
profusión actual de los procesos iterativos, este método de traducción
no tiene mucho sentido real.

Generalmente cada instrucción ejecutable en lenguaje de alto


nivel genera más de una en lenguaje máquina. Cuando el módulo
objeto es generado en un lenguaje diferente al de máquina, se
origina un código objeto intermedio, se requiere un paso adicional para
traducir este módulo a la forma que acepta todo computador. Si la
salida del compilador es en Assembler se necesita de un ensamblador
simple, una sola pasada (similar a la segunda del ensamblador que
es descrito posteriormente) que prácticamente lo que hace es una
traducción literal, pues el compilador ya ha realizado todo el trabajo
de análisis y síntesis. Solo podría agregársele las funciones de un
cargador.

Hoy en día cuando existe una gran variedad de arquitecturas del


computador y se pretende utilizar lenguajes iguales en cada una de
ellas, compartiendo así programas fuentes, por lo que se hace más
común procesos de compilación donde se genere un código objeto
intermedio, es decir independiente del hardware, esto permite trasladar
el código de una arquitectura a otra para posteriormente utilizar un
40

traductor-cargador que se encargará de considerar el hardware de


la máquina.

1. Funciones de un Compilador

Aún cuando en el próximo capítulo se describen a


profundidad las funciones del compilador, a continuación se explicará
de forma sintetizada las funciones más comunes de los compiladores:

a) Análisis Léxico: lo realiza la fase léxica y debe reconocer los


elementos básicos del lenguaje y generar las tablas de
símbolos y de literales. Para cumplir esta misión, debe leer
cada instrucción y separarla en TOKENS. El Token es
comparado en una tabla de palabras claves, y si lo consigue
se mueve al siguiente Token, es decir, esta fase no
reconoce cual es la palabra clave. Por tal razón la fase
léxica no está en capacidad de evaluar la sintaxis de la
instrucción fuente como tal, en cambio si evalúa la validez de
los elementos básicos. En caso de conseguir algún error en
un elemento básico lo reporta con un mensaje apropiado.

Los elementos básicos se clasifican en:


identificadores (nombres de variables, arreglos, etc.)
etiquetas y cadenas. La evaluación de estos elementos
depende de la sintaxis del lenguaje. En la tabla de símbolos
solo incluye el nombre del identificador valido, no incluye
atributos (entero, real, char, etc.) en cambio los literales los
incluye, en la tabla respectiva, con todos sus atributos
excepto la dirección relativa.
41

b) Análisis Sintáctico: realizado por la fase sintáctica y su


función básica es la averiguar la validez sintáctica de las
instrucciones fuentes, para cual debe reconocer, primero, el
tipo de la instrucción o sentencia (si es IF, WHILE, etc.) para
poder conocer las reglas sintácticas que debe aplicar. Al
determinar un error se reporta al final de la compilación y no
se generará el programa objeto. Al corregir el o los errores
deberá llamar de nuevo al compilador y se comenzará el
proceso desde el principio.

c) Análisis Semántico: lo ejecuta la fase de interpretación


la que se encarga de determinar el sentido de la
instrucción siempre y cuando no haya tenido error de
sintaxis. La interpretación depende de si la instrucción es o
no ejecutable. En el primer caso, la interpretación consiste
en generar una forma intermedia que se guarda en una
estructura de datos apropiada. Por otra parte si se trata
de una instrucción no ejecutable, la interpretación se hace
obteniendo los atributos de la(s) variable(s) y se incluye
en la tabla de símbolos creada en la fase léxica.

d) Optimización: minimiza el número de instrucciones del


programa objeto o hace mejor uso de los recursos (tipo de
registros) con que cuenta la máquina. La optimización
puede dividirse en dos fase:

 Fase de Optimización Independiente: la cual se puede


realizar en varias pasadas dependiendo de la técnica
aplicada. Esta optimización se realiza sobre la matriz
de formas intermedias.
42

 Fase de Optimización Dependiente: la cual hace un


mejor uso de los registros que dispone la máquina,
también hay la posibilidad de generar un menor número
de instrucciones.

e) Asignación de Almacenamiento: la ejecuta la fase de


asignación de almacenamiento y su propósito es asignar la
dirección relativa a todos los identificadores y a los literales.

f) Generación de Código Objeto: su propósito es obvio.

Al finalizar la compilación no se requiere mantener el traductor


en memoria principal. Si el proceso de traducción ha sido exitoso y
se desea correr el programa objeto, en memoria solo debe estar el
módulo obtenido.

B. Interpretadores

Es un programa traductor que tiene como entrada un programa


fuente codificada en lenguaje de alto nivel y por cada instrucción leída
la analiza sintácticamente y si no hay error la interpreta y la ejecuta
directamente. Es decir este traductor NO GENERA PROGRAMA
OBJETO como lo hacen otros traductores (compiladores,
ensambladores).

Un interpretador es considerado menos eficiente que un


compilador porque debe llevar a cabo tanto el análisis sintáctico,
interpretación y la ejecución de la instrucción procesada. Esto
significa que cada vez que se requiere ejecutar el programa el
43

interpretador debe repetir todo el proceso como si fuese la primera


vez, aún cuando ya el programa fuente no tenga errores de sintaxis.
Además, el interpretador permanece en memoria durante todo el
tiempo de ejecución lo que no sucede con los traductores que
generan un programa objeto, ya que luego de la generación
desocupan el almacenamiento principal y no es necesario que estén
en memoria mientras se realiza la ejecución del módulo objeto.

Otra razón que influye en la eficiencia del interpretador es que las


instrucciones dentro de los ciclos iterativos deben ser interpretados
cada vez que se ejecuta el ciclo, con un compilador no ocurre esto
porque la procesa una sola vez.

Al ejecutar instrucción por instrucción, el interpretador permite


conocer resultados parciales antes de que finalice la ejecución de la
ultima sentencia y como reporta los errores de sintaxis aceptando la
corrección de los mismos en forma iterativa, facilita
convenientemente la codificación de programas experimentales
y/o educativos. Igualmente es especialmente útil para programas
cortos o cuando la frecuencia de la ejecución no es rutinaria.

El interpretador minimiza el tiempo requerido para la preparación


de las instrucciones objetos a ser ejecutadas. Es decir un compilador o
un ensamblador necesita más tiempo para generar el programa
objeto que el requerido por el interpretador para generar la
primera instrucción ejecutable.

El interpretador, por su forma de trabajo, solo realiza una pasada


sobre el programa fuente lo que implica que no optimiza y obliga a
que la gramática del lenguaje establezca que los atributos que
44

puedan asignársele a los identificadores deban estar declarados


antes que el símbolo (variable, arreglo, etc.) sea utilizada.
Tampoco, podrá asignarse almacenamiento de tipo dinámico
(asignación de la memoria durante la ejecución y liberación de la
misma una vez que no se necesite más el identificador).

1. Pasos de un Interpretador

El interpretador opera más o menos así:


a) Lee una instrucción fuente.
b) Examina la instrucción para determinar la validez
sintáctica tomando en cuenta la gramática del lenguaje
fuente.
c) Si y solo si, la instrucción es correcta
sintácticamente, la interpreta para determinar su
semántica, pudiendo producir una estructura de datos
intermedias (por ejemplo un árbol binario) que establezca la
secuencia lógica de ejecución, facilitando la generación de
las instrucciones de máquina equivalente.
d) Inmediatamente, el mismo interpretador (el traductor)
ejecuta las instrucciones de máquina generadas. Si es el
caso, se almacenan los resultados parciales para un
posible uso posterior.
e) Si se determina algún error de sintaxis se detiene el
proceso, se reporta el error y hasta tanto este no se corrija,
no se interpretará ni ejecutará la instrucción en proceso.
f) Se repite el ciclo a partir del paso a.
g) La traducción finaliza cuando se lee la última
instrucción fuente.
45

El interpretador es un programa, residente en memoria central,


que lee las instrucciones en lenguaje de alto nivel, detecta los errores,
los comunica, y, si no hay errores, convierte las instrucciones a
código interno y las ejecuta cuando se le indica.

2. Interpretadores VS Compiladores

Ya se han indicado algunas diferencias entre los


compiladores e interpretadores, pudiéndose además destacar
como diferencias las siguientes:

 La zona de memoria necesaria para operar con un


interpretador es menor que la que se precisa para operar
con un compilador.

 El programa compilado se ejecuta más rápidamente que el


interpretado. Ello se debe a que el programa interpretado es
ejecutado por otro programa que, a su vez, lo es por el
computador.

 Es más fácil programar contando con interpretadores, ya


que nos avisa de los errores tan pronto como los
cometemos.

En la actualidad, la solución generalmente adoptada, sobre todo


para lenguajes de gran difusión como el BASIC y el PASCAL, consiste
en ofrecer ambas posibilidades: INTERPRETADORES Y
COMPILADORES. La primera opción facilita la programación, prueba
y depuración de los programas. La segunda agiliza su ejecución en la
46

fase de producción.

C. Ensambladores

Los ensambladores son los encargados de convertir los


programas fuentes, escritos en lenguaje ASSEMBLER, a programa
objeto en lenguaje máquina.

Como en todo proceso de traducción, junto con el programa


objeto aparecen los listados de errores sintácticos y de
correspondencia entre el programa fuente y el objeto.

El trabajo del ensamblador se reduce a traducción palabra a


palabra, cambiando por códigos de operación numéricos y
direcciones reales los símbolos del programa. Para ello emplea tablas
de traducción de símbolos, localizadas en la memoria, así como la
cuenta de la memoria ocupada. Como a veces la definición de los
símbolos puede venir detrás de la sentencia en que aparecen por
primera vez, la traducción se realiza repitiendo el proceso dos veces,
cada una de estas veces se denomina Paso.

1. Pasos de un Ensamblador

Primera pasada: básicamente debe asignar una dirección


relativa a cada símbolo que defina un almacenamiento, a la
etiquetas y literales debe inicializarse en cero y este valor será la
posición relativa de la primera instrucción ejecutable del programa
fuente. Además, debe crear la tabla de símbolos y de literales.
1. Inicializa contador posiciones en cero.
47

2. Lee una instrucción fuente.


3. Selecciona un TOKEN del campo operador y determina si se
encuentra en la tabla de pseudo operadores, en la de
operadores o en la de las macros. De no encontrarse en
ninguna de ellas, se trata de un error de sintaxis, el cual debe
reportarse para información del programador. Cuando lo
consigue se procede según sea el tipo de operador
aproximadamente así:

a) PSEUDO operador (instrucción no ejecutable). Averigua si


es una definición de almacenamiento (por ejemplo pseudo
código DS) o la de un literal (pseudo código DC) De ser así,
examina el operando de la instrucción para conocer su
longitud (nro. de bytes) de almacenamiento, guarda el valor
para luego incrementar al contador de posiciones.
Dependiendo de dicho valor, puede ser necesario ajustar
al contador antes de incrementarlo con fines de alineamiento.

b) OPERADOR (instrucción ejecutable). El formato de la base


de datos que contiene los operadores del lenguaje
Assembler puede ser:
 Cada operador del lenguaje Assembler.
 El código en binario equivale del operador.
 La longitud (número de bytes) de la instrucción de
maquina.
 El tipo de formato de la instrucción.

Si se trata de un operador, se guarda la longitud


correspondiente para el posterior incremento del contador.
Luego se investiga el operando para conocer si hay algún
48

literal, en cuyo caso se le almacena en la tabla de literales.


Después, se examina el campo de etiqueta, si se encuentra,
se le incluye en la tabla de símbolos conjuntamente con el
valor actual del contador de posiciones.

4. Cuando se trata de una macro se expande dicha macro y se


considera cada instrucción. que incluye esta rutina de acuerdo a
cada operador, similar a los pasos a y b.
5. Se incrementa el contador de posiciones con el valor de la
longitud guardado en "a" o "b".
6. Se repite el ciclo hasta conseguir el pseudo operador que indique
el final del programa fuente (por ej. END).
7. Antes de pasar el control a la siguiente pasada, deben realizarse
algunas funciones más simples que no se describen. Tampoco
se describen otros pseudo operadores que incluyen los lenguajes
de ensambles los cuales no son de interés para esta primera
pasada.

Segunda pasada: este paso utiliza la tabla de símbolos, la de


literales y el programa fuente (forma original), además, de la base de
datos que contienen los pseudo operadores y la que incluye a los
operadores ya empleadas en pasada anterior.

Luego de la definición de todos los símbolos, referenciados en el


programa fuente, por la primera pasada, la segunda se encarga de
completar el ensamblaje leyendo cada instrucción fuente para:

a) Si es un pseudo operador, procede de igual manera que en la


pasada anterior con código DC y DS. Cuando se trata de un
literal lo incluye en el programa objeto que esta ensamblando.
49

Además ahora si se tratan el resto de lo pseudo operadores.


b) Cuando sea operador, obtiene su código en binario y el formato
(tipo de registro que el corresponde). Luego configura la
respectiva instrucción de maquina incluyendo su dirección
relativa, que ya se sabe viene dada por el contador de
posiciones. El proceso de los operadores implica otros detalles,
que dependen de los diferentes tipos de registros, por ej. si su
formato indica un movimiento de registro a registro, o si
requiere un registro base, etc. Pero estos detalles no serán
descritos.
c) Cuando sea el caso, procesa la instrucción que equivalen a
las macros de manera a las ya comentadas.
d) El contador de posiciones se incrementas de la manera que en el
paso anterior.
e) Finalmente, puede listarse el programa fuente y el objeto
equivalente expresado en notación hexadecimal.

También existen ensambladores de una sola pasada donde


realizan todo en ella.
V. FUNCIONAMIENTO DEL COMPILADOR

Las fases del compilador que a continuación se detallan de ninguna


manera deben considerarse como procesos aislados o secuenciales,
sino todo lo contrario la función que cada una de ellas cumple está muy
bien definida y delimitada, tal y como se mencionó en el capítulo
anterior; aún cuando pueden ocurrir simultáneamente buscando
aprovechar al máximo posible el tiempo y los procesos que se le
aplican a cada una de las instrucciones del programa fuente.

Para cada fase se estudiará su función, bases de datos utilizadas y


creadas y el algoritmo empleado para su realización.

Componente Léxico
Programa ANALIZADOR ANALIZADOR
Fuente LEXICO Siguiente Componente SINTACTICO
Léxico

TABLA DE
SIMBOLO

Ilustración 7. Enlace Análisis Léxico y Sintáctico.

A. Análisis Léxico
1. Función

Es la primera fase del compilador y su principal función


consiste en leer los componentes de entrada y elaborar como

50
51

salida una secuencia de componentes léxicos (Token) que luego


utiliza el analizador sintáctico para su trabajo.

También realiza ciertas funciones secundarias tales como:


 Eliminación de comentarios.
 Eliminación de espacios en blanco.

 Eliminación de caracteres TAB y de salto de línea.

Por otra parte durante esta etapa se construye la Tabla de Literales,


la Tabla de Identificadores y la Tabla de Símbolos Uniformes (TSU).

2. Base de Datos

 Programa Fuente: forma original del programa, presentado


en texto.

 Tabla de Terminales: estructura permanente que contiene un


registro para cada símbolo terminal ( operadores aritméticos,
palabras clave, etc.). Cada entrada consta de un símbolo
terminal, una indicación de su clase (operador, carácter de
interrupción, etc.)

Indice Símbolo Indicador Precedencia

 Tabla de Literales: Se describen todos los literales utilizados


en el programa fuente.

Nombre Base Escala Precisión Almacenamiento Indice


52

 Tabla de identificadores: creada para describir todos los


identificadores del programa fuente, variables, estructuras de
datos, etc.

Nombre Base Escala Precisión Almacenamiento Indice

 Tabla de Símbolos Uniformes (TSU): creada con el fin de


presentar el programa como una serie de elementos básicos.
Los elementos son TRM (símbolo terminal), IDN
(identificador), LIT (literal).

Tabla Indice

En el campo tabla se indica el tipo de elemento, el valor


índice indica la posición que ocupa el elemento en la tabla
respectiva.

3. Algoritmo

 Analizar gramaticalmente la serie de caracteres de entrada,


descomponiéndola en elementos básicos o señales (token).
 Reconocer el tipo de token ( IDN, LIT, TRM).
 Asentar las entradas en las tablas:
 Comparar las señales con la tabla de terminales, si
coincide con alguna, se clasifica como TRM y se inserta en
la TSU.
53

 Si no es catalogado como TRM se procede a clasificarlo


como posible IDN o LIT. Para ello se compara la señal
contra las reglas lógicas de construcción, tanto de
identificadores como de literales (longitud, caracteres
permitidos, etc.). Si no cumple con alguna se emite el
respectivo ERROR.
 Si se determina que es un identificador, se examina la tabla
de identificadores y si no se encuentra, se crea una nueva
entrada (según las reglas del lenguaje). Luego se incorpora
un símbolo uniforme IDN en la TSU.
 Si se determina que es un literal (números, series de
caracteres entre comillas o apóstrofes u otros datos
autodefinidos) se examina la tabla de literales y se crea una
nueva entrada cuando este no se encuentra. Durante esta
fase se asientan todos los atributos del literal. Luego se
incorpora un símbolo uniforme LIT en la TSU.

B. Análisis Sintáctico

1. Función

a.- Identificar el tipo de instrucción.


b.- Validar la estructura sintáctica.
c.- Emitir errores respectivos.
d.- Agrupar token en estructuras sintácticas (árboles).
e.- Completar información en tabla de identificadores.
f.- Realizar parte del análisis semántico.
54

2. Bases de Datos

 Tabla de Símbolos Uniformes (TSU).

 Reducciones: son las reglas sintácticas del lenguaje fuente


utilizadas por el reconocedor.

3. Reconocedores

Existen múltiples métodos de reconocer o validar la construcción


de una instrucción, resultando los más sencillos y comunes el
Descendente (TOP-DOWN), Ascendente (BOTTOM-UP) y el árbol
sintáctico, los cuales utilizan el sistema de reducción.

N
<IDN> <LETRA> | <LETRA><LETRA_DIGITO>
1

<LETRA_DIGITO> <LETRA> | <DIGITO>

<LETRA> A | B | C | D | E | F | G | H | ... | X | Y | Z

< DIGITO> 0|1|2|3|4|5|6|7|8|9


N
<LIT> <DIGITO>
1

<TRM> <OP> | Begin | End | Procedure | ( | ) ...

<OP> + | - | * | / | ** | =

Ilustración 8. Representación de Reducciones Sintácticas

Para ilustrar los ejemplos se establecerán las siguientes


55

condiciones para las reducciones a utilizar: Los elementos definidos


se representarán por letras mayúsculas y los elementos finales
serán representados por letras minúsculas. En la ilustración 8 son
elementos definidos LETRA, LETRA_DIGITO, LIT, IDN, TRM, OP y
los símbolos finales A,B, C, D,... Z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, +, - , *,
etc. Por otra parte el símbolo | significa que el elemento definido
puede formarse por un término o por otro, es decir IDN puede ser
una LETRA o una combinación de LETRA y LETRA_DIGITO.

a. Reconocedor Descendente (TOP-DOWN)

Este método es realizado partiendo desde alguna reducción


hasta llegar al elemento que se está verificando. Si resulta
imposible llegar al elemento que se está chequeando se producirá
el respectivo mensaje de error. El método parte de la reducción
correspondiente al tipo de expresión detectada previamente. A
continuación se plantea un ejemplo con la finalidad de ilustrar mejor
el funcionamiento del reconocedor.

Supóngase las siguientes reglas o reducciones:


S cAd
A aB
B b|c

Se desea verificar sintácticamente la siguiente expresión:


w=cabd

Para realizar la verificación se debe partir de algunas de las


reducciones que están previamente definidas. Se seleccionará la
expresión S por poseer una cierta similitud a la expresión a validar
56

(c a b d), claro está que el compilador realizará la identificación de


la expresión y las reducciones correspondiente al tipo identificado,
de manera científica y no de manera empírica.

S cAd caBd cabd w

Al reemplazar S por su estructura se obtiene c A d, donde A


debe ser sustituido por su respectivo reducción, de donde se
produce c a B d, al igual que en el paso anterior B debe ser
sustituido por su respectiva reducción, originando la siguiente
expresión c a b d, esta expresión no posee letras mayúsculas por
lo que se ha llegado a una expresión final. Solo resta comparar con
la expresión que se está validando, comprobándose que se ha
llegado a la expresión buscada por lo que se concluye que ésta es
válida.

b. Reconocedor Ascendente (BOTTOM-UP)

Este método es totalmente contrario al anterior, puesto que


parte de la expresión a validar y debe lograrse formar alguna
reducción porque de lo contrario el chequeo de la expresión
producirá el error correspondiente. Se debe mantener en cuenta
que a este nivel ya la expresión ha sido convertida a símbolos
uniformes, lo que produce facilidad al buscar algunas reducciones
al momento de construir una regla general para la expresión.
57

A continuación se plantea un ejemplo con la finalidad de


ilustrar mejor el funcionamiento del reconocedor.

Supóngase las siguientes reglas o reducciones:


S aAcBe
A Ab | b
B d | dB | f

Se desea verificar sintácticamente la siguiente expresión:


x=abb cde

El reconocedor debe partir desde x y llegar a alguna de las

reducciones planteadas (S, A o B). Para ello deberá sustituir uno o


varios elementos de la expresión por los que forman a una
reducción.

x abbcde aAbcde aAcde aAcBe S

Apreciese que la primera sustitución que se realiza es el


elemento b por una reducción equivalente A, formandose otra

expresión (aAbcde). Posteriormente se toma la combinación Ab

y se sustituye por una reducción, resultando a A c d e, de manera

visual se puede observar que sólo resta sustituir d por B para

generar S, así que se busca la reducción que permita generar la

expresión requerida, de esa manera se obtiene a A c B e que no

es más que S, por consiguiente la expresión x es válida.


58

c. Arboles Sintácticos

El árbol sintáctico resulta un método sencillo una vez


construidos los árboles, el inconveniente radica en construir los
árboles ya que se debe generar uno por cada combinación de
reducciones que posea el lenguaje. Una vez construido el árbol el
proceso se limita a recorrer los nodos terminales del árbol de
izquierda a derecha e ir comparando con la expresión a validar, si
se llega al mismo resultado significa que la expresión es válida de
lo contrario de debe repetir el proceso para cada una de los árboles
generado hasta no existir más comparaciones, produciéndose así
un error.

A continuación se mostrará el ejemplo del reconocedor


descendente utilizando árboles sintácticos.

Supóngase las siguientes reglas o reducciones:


S cAd
A aB
B b|c

Se desea verificar sintácticamente la siguiente expresión:


w=cabd

Para las reducciones planteadas se generan dos árboles


sintácticos, que son los siguientes:
59

S S

c A d c A d

a B a B

b e

Las letras en negrillas muestran cuales son los elementos


finales o nodos terminales del árbol sintáctico, así que el primer
árbol genera la expresión c a b d y el segundo genera c a e d. Al
comparar la expresión a validar con las generadas por los árboles
se obtiene que el primer árbol satisface la comparación,
significando esto que la expresión es válida.

C. Generación de Código Intermedio o Fase de


Interpretación

1. Función

a.- Utilizar estructuras Parser.


b.- Generar secuencia de instrucciones simples.

Para la simplificación del programa fuente el compilador genera


un código intermedio, que puede variar entre cada tipo de
compilador, para posteriormente enviarlo a la fase de optimización
donde este código es mejorado, para producir mejor tiempo de
respuesta, menos almacenamiento intermedio, etc.
60

2. Bases de Datos

 Tabla de Símbolos Uniformes (TSU)


 Tabla de identificadores
 Tabla de Literales
 Formas Intermedias

3. Tipos de Código Intermedio

Algunos de los códigos intermedios más utilizados son:


 Notación Posfija o Notación Polaca.
 Arbol Analítico.
 Código de tres direcciones.
 Matriz de formas intermedias.

Aún cuando el objetivo del presente trabajo no es profundizar


en los algoritmos internos del compilador, se mostrará de manera
sencilla en que consisten estas formas de código intermedio y como
se generan.

a. Notación Posfija o Polaca.

La forma en que usualmente se colocan las expresiones


aritméticas se denomina notación infija debido a que el operador
aparece entre los operandos; por consiguiente la notación posfija
no es más que el operador después de los operandos.

Partiendo de las siguientes condiciones, toda expresión


aritmética es evaluada de izquierda a derecha y manteniendo la
61

prioridad de los operadores (signos de agrupación, potencia,


división y multiplicación, suma y resta); en caso de las expresiones
lógicas se mantiene de izquierda a derecha y la prioridad de los
operadores (signos de agrupación, not, and y or).

A continuación se presenta una expresión aritmética en infijo


para su respectiva conversión a posfijo:
a=b*(c+d/e)

Lo primero es determinar de izquierda a derecha la mayor


prioridad, resulta el paréntesis. Por un momento la expresión se
reduce al la existente dentro del paréntesis (c + d / e).

De izquierda a derecha la mayor prioridad la posee la división


por lo resulta la primera operación a realizar. Queda así: (c + d e / ).
Ahora se asume como si d / e ya ha sido resuelto, queda c + un
resultado, convirtiéndolo a posfijo y se obtiene: c d e / +. Es
conveniente recalcar que en la notación posfija no existen signos
de agrupación. Sustituyendo en la expresión original se tiene:
a = b * c d e / +.

Aplicando lo antes expuesto tenemos: a = b c d e / + * y


posteriormente se obtiene: a b c d e / + * = (expresión posfija).

b. Arbol Analítico

De manera similar a los árboles sintácticos se pueden


generar árboles analíticos de alguna expresión aritmética o lógica.
Para construir el árbol correspondiente se procede de forma
análoga a la creación del posfijo; es decir se debe proceder de
62

acuerdo a la prioridad de los operadores y sus signos de


agrupación partiendo de izquierda a derecha. El árbol se forma
colocando al operando 1 y el operando 2 como hijos del operador,
el operando 1 como hijo izquierdo y el operando 2 como hijo
derecho. En algunos caso un operador puede convertirse como
hijo de algún otro operador, indicando esto que el operador hijo
debe resolverse previamente para proseguir con la resolución de la
expresión.

A continuación se resolverá la expresión del ejemplo anterior


utilizando árbol analítico.
a = b * (c + d / e)

Recuérdese que se deben considerar los signos de


agrupación y la prioridad de los signos quedando el orden de las
operaciones de la siguiente manera: / + * =.

/
a = b * (c + d e)

a =b* +

c / operando2
c + operando2
d e
d/e
63

a= *

b + operando3
b * operando3
c /
c + operando2
d e

a * operando4
a = operando4
b +

c / b*operando3
RESULTADO FINAL
d e

c. Código de Tres Direcciones

El código de tres direcciones consiste en resolver las


expresiones utilizando almacenamiento temporal para cada
operación realizada, según la prioridad de los signos y la
agrupación correspondiente partiendo de izquierda a derecha.
En el ejemplo anterior al ir realizando cada subárbol se mostró
64

un resultado parcial en un rectángulo denominándose


operando n.

Retomando el ejemplo anterior, observe como se resuelve la


expresión en cuestión: a = b * (c + d / e)

a = b * (c + d / e)
a = b * (c + t1) t1 = d / e
a = b * t2 t2 = c + t1
a = t3 t3 = b * t2 (paso no necesario)

d. Matriz de Formas Intermedias

Como su nombre lo indica para cada expresión le es


creada una matriz con la siguiente estructura:

Indice Temporal Operando1 Operando2 Operador Sig. Ant

 Indice: valor que identifica la entrada a la matriz.


 Temporal: variable donde es almacenado el valor de la
subexpresión resuelta.
 Operando 1 y Operando 2: elementos de la operación que
forman la subexpresión.
 Operador: operación a realizar con los operandos.
 Siguiente (Sig.): apuntador a la siguiente entrada de la
matriz, establece la secuencia de solución de las
subexpresiones. Se utiliza en el proceso de optimización.
65

 Anterior (Ant): Indica el índice de la operación realizada


inmediatamente anterior. Utilizado en el proceso de
optimización.

Esta forma complementa al código de tres direcciones,


utilizándolo como proceso central. Retomando el ejemplo
anterior se resolverá utilizando la matriz de formas intermedias.

Indice Temporal Operando1 Operando2 Operador Sig. Ant

1 M1 d e / 2 0
2 M2 c M1 + 3 1
3 M3 b M2 * 4 5
4 a M3 = 0 3

D. Optimización

1. Función

a.- Minimizar el número de instrucciones.


b.- Generar un código objeto más eficiente.
c.- Producir ahorro en tiempo de ejecución.
d.- Optimizar la utilización de la memoria.
e.- Producir un código objeto mejorado.
f.- Reducir el número de instrucciones LOAD y STORE.
g.- Optimizar el uso de los registros e instrucciones rápidas.
h.- Minimizar el uso de temporales.
66

2. Tipos de Optimización

Los compiladores realizan dos procesos de optimización, el


primero es realizado sobre el código intermedio generado y se
denomina optimización independiente, debido a que sólo se
toman en consideración la disminución de operaciones
innecesarias o redundantes y no la arquitectura del computador. El
segundo se realiza sobre el código objeto ya generado y se limita a
optimizar la utilización de los registros del computador, es decir
toma en consideración la arquitectura de la máquina, por lo que se
denomina optimización dependiente.

En la actualidad los compiladores vienen diseñados con fases


de optimización bien diferenciadas, es decir, un proceso de
compilación hasta la optimización independiente con generación de
código intermedio cobra gran importancia porque permite la
implementación y prueba de aplicaciones en equipos de bajo costo,
de donde luego se trasladan los códigos a equipos más costosos
donde se genera posteriormente el código objeto definitivo y se
optimiza dependientemente, para así generar la aplicación en
cualquier arquitectura distinta a la utilizada en la implementación.
Esto explica el porque algunos de los principales lenguajes de
programación modernos permiten migrar aplicaciones de un
equipo personal hacia una equipo mainframe o minicomputador,
con arquitecturas totalmente diferentes. El principal objetivo de esta
facilidad es la disminución de costos de desarrollo, ya que es más
económico tener un centro de desarrollo con n equipos personales
que poseerlo con cada arquitectura para la cual se desarrollará.
67

3. Criterios de Optimización Independiente

Como ya se ha indicado la optimización independiente se


realiza sobre el código de formas intermedias sin tomar en
consideración la arquitectura del computador, para ello utiliza
básicamente los siguientes criterios:

 Eliminación de subexpresiones comunes.


 Eliminación de operaciones entre constantes o cálculos en
tiempo de compilación.
 Optimización de expresiones booleanas.
 Eliminación de redundancias.
 Reducción de potencias.
 Reducción de frecuencias.

a. Eliminación de Subexpresiones Comunes

Al resolver una expresión pueden generarse


subexpresiones iguales que pudiesen ser calculadas previamente
para así proceder una sola vez y no repetir n veces la operación.
Observe la siguiente expresión y como pudiese ser optimizada:
a = (b + e) * f / d + (b + e) * c

Al analizar la expresión se puede apreciar que b + e se


repite en la expresión, así que para evitar realizar dos veces el
cálculo se realiza dicho operación previamente y se almacena en
una variable temporal, sustituyéndose en la expresión original.
t=b+e
a=t*f/d+t*c
68

Cuando se procede de esta manera primero el compilador


chequea sí la subexpresión es igual y en segundo caso sí la
operación acepta conmutatividad (suma y multiplicación) ya que la
subexpresión puede aparecer con los operandos invertidos. En una
expresión se pueden hallar varias subexpresiones comunes.

b. Eliminación de Operaciones entre Constantes o Cálculos


en Tiempo de Compilación

En algunos casos al programar se generan operaciones entre


elementos constantes, lo que requiere que el compilador realice los
cálculos en el momento de compilación, esto trae como
consecuencia que estas subexpresiones sean resueltas y
sustituidas por la constante resultante. A continuación se muestra
un ejemplo:
a = 2 * 276 / 92 + b

Todas las operaciones existentes entre valores constantes


serán realizadas y posteriormente sustituidas, es decir, 2 * 276 y
luego el resultado que es constante (552) será dividido entre 96
resultando otra constantes (6) el cual sustituye las operaciones
anteriores y resulta:
a=6+b

c. Optimización de Expresiones Booleanas

Cuando se presentan expresiones booleanas con varios


operandos, el compilador busca si se conocen los valores para así
resolver previamente la expresión y de lo contrario construye una
secuencia de instrucciones que faciliten el proceso y eviten tener
que realizar todas la operaciones, estas instrucciones son
69

realizadas aprovechando las propiedades de las expresiones


booleanas. Si se tiene por ejemplo la siguiente expresión IF a OR b
OR c, esta expresión es verdadera siempre y cuando al menos un
operando sea verdad entonces sí a es verdadero no es necesario

realizar las operaciones contra b y c, en cambio si es falso deberá

que chequear que valor posee b y de ser este verdadero no se

requiere de c, caso contrario se debe chequear c. Si por otra parte


se tiene a AND b AND c, esta expresión es verdadera solo si los
tres operandos son verdaderos por lo que sí a es falso no requiere

verificar b ni c, de lo contrarios chequea b y si este es falso no

compara c, y en caso contrario si.

d. Eliminación de Redundancia

Este criterio es similar al explicado en eliminación de


subexpresiones comunes, solo que se aplica a expresiones
distintas a las aritméticas o lógicas. Un ejemplo de ello es el
siguiente:
A[ b + c, 1] = A[ b + c, 3 ] * A[ b + c, 4]

En esta expresión las operaciones comunes se refieren a


la posición del elemento en la matriz y no a los elementos
propiamente de la expresión aritmética en cuestión. El compilador
procede de igual manera que en la eliminación de subexpresiones
comunes, resuelve primero la expresión común y la almacena en un
temporal y luego sustituye la operación por el temporal, quedando
así:
t=b+c
A[ t , 1] = A[ t, 3 ] * A[ t , 4 ]
70

e. Reducción de Potencia

Este tipo de optimización consiste en sustituir el proceso de


potenciación por multiplicaciones sucesivas. La operación
multiplicación existe como operación básica, en cambio la potencia
no; esto no es más que sustituir operaciones complejas por
equivalentes más sencillas.

Ejemplo: b = c ** 2 b=c*c

c2

f. Reducción de Frecuencias

Existen casos donde el programador por descuido o por


mala implementación coloca instrucciones que generan el mismo
valor dentro de algún ciclo, produciendo esto que durante la
ejecución se produzca el mismo resultando tantas veces como se
repita el ciclo, generándose una pérdida de tiempo, por lo que el
compilador para evitar esto ubica las instrucciones mencionadas
antes del código del ciclo. A continuación se muestra un ejemplo:

HAGA PARA M = 1 HASTA 30


A=C+2
B=D+5
N = (L * M) / 2
ESCRIBE( N )
FIN DEL HAGA

Observe que tanto A como B obtienen el mismo valor para


71

cada repetición del ciclo, en vista que las variables implicadas en su


cálculo no dependen del ciclo ni de alguna variable generada en él,
así que lo lógico es que estas sean colocadas previas al ciclo.

A=C+2
B=D+5
HAGA PARA M = 1 HASTA 30
N = (L * M) / 2
ESCRIBE( N )
FIN DEL HAGA

4. Optimización de la Matriz de Formas Intermedias

Una vez generada la matriz de formas intermedias se procede a


optimizarla considerando los criterios anteriormente detallados;
subexpresiones comunes, operaciones entre constantes, reducción
de potencia y optimización de expresiones booleanas. A
continuación se presenta una expresión aritmética con su
respectiva matriz de formas intermedias y posteriormente las
optimizaciones realizadas:
a=c*d*(d*c+b)-4*3

La matriz de formas intermedias generada es la siguiente:

Indice Temporal Operando1 Operando2 Operador Sig. Ant

1 M1 d c * 2 0
2 M2 M1 b + 3 1
3 M3 c d * 4 2
4 M4 M3 M2 * 5 3
5 M5 4 3 * 6 4
6 M6 M4 M5 - 7 5
7 a M6 = 0 6
72

Iniciando la optimización aplicando el criterio de eliminación de


operaciones entre constantes, se encontrará la entrada 5 donde se
multiplica 4 * 3, así que se eliminará dicha entrada, donde aparezca
M5 será sustituido por el valor 12 y se actualizarán los apuntadores
sig. y ant.

Indice Temporal Operando1 Operando2 Operador Sig. Ant

1 M1 d c * 2 0
2 M2 M1 b + 3 1
3 M3 c d * 4 2
4 M4 M3 M2 * 6 3
5
6 M6 M4 12 - 7 4
7 a M6 = 0 6

Se continúa chequeando si existen más operaciones entre


constantes o se han generado nuevas operaciones al sustituir M5
por 12.

De no existir más operaciones entre constantes se aplica el


criterio de eliminación de subexpresiones comunes. Como se indicó
al realizar eliminaciones de subexpresiones comunes se debe
considerar la conmutatividad de la suma y la multiplicación. En la
matriz resultante la expresión de la entrada 1 y la 3 son iguales al
aplicarle la propiedad conmutativa, por lo que se procede a eliminar
la segunda aparición y se sustituye M3 en cada aparición por M1 y
luego se actualizan los apuntadores sig. y ant.
73

Indice Temporal Operando1 Operando2 Operador Sig. Ant

1 M1 d c * 2 0
2 M2 M1 b + 4 1
3
4 M4 M1 M2 * 6 2
5
6 M6 M4 12 - 7 4
7 a M6 = 0 6

Se debe chequear que no existan más subexpresiones comunes


o que no se hayan generado nuevas al sustituir M3 por M1, de no
existir más y al finalizar los criterios utilizables según la expresión la
matriz quedará optimizada.

Los temporales generados en la matriz de formas intermedias


luego de optimizada son incorporados a la tabla de identificadores
como almacenamiento temporal.

E. Asignación de Almacenamiento

1. Función

a.- Asignar almacenamiento a todos los identificadores.


b.- Asignar almacenamiento a todas las variables temporales.
c.- Asignar almacenamiento a los literales.
d.- En algunos lenguajes asegurarse del almacenamiento de
literales e identificadores que posean un atributo inicial.
74

2. Bases de Datos

1.- Tabla de Identificadores. (IDN)


2.- Tabla de Almacenamiento Temporal. (puede ser parte de la
tabla de identificadores IDN).
3.- Tabla de Literales. (LIT)
4.- Matriz de Formas Intermedias.

Todas las direcciones de almacenamiento asignadas en esta


fase son direcciones relativas.

F. Generación de Código

1. Función

a.- Generar programa objeto (máquina-ensable-pseudocódigo)


b.- Invocar la fase de optimización dependiente.

2. Definición de un Código Ensamble Ilustrativo

Con la intensión de ilustrar el proceso de generación del código


ensamble se definirá una simbología hipotética de operaciones de
carga, almacenamiento, suma, resta, multiplicación y división. Se
supone que este ensamble se realiza con solo dos registros.

L Load ( Cargar. Utiliza un registro R1 )


S Store ( Almacenar. Utiliza un registro R1 )
A Sumar ( utiliza un registro R1 )
R Restar ( utiliza un registro R1 )
M Multiplicar ( utiliza un par de registros R0 y R1 )
D Dividir ( utiliza un par de registros R0 y R1 )
75

Cada una de las operaciones aritméticas (suma, resta,


multiplicación, división) requiere que el primer operando sea
cargado (L) en el registro de uso de la operación, es decir, suma y
resta en R1; multiplicación y división en R0, luego se opera el
registro con el segundo operando y se requiere el resultado se
almacena (S) del registro a una variable. El resultado de la
multiplicación y la división queda en R1. En forma general cada
operación aritmética sigue la siguiente sintaxis:

L R1, operando1
operación (A, R, M, D) R1 o R0 (según el caso), operando2
S R1, resultado

La asignación o igualdad se logra con la carga (L) y el posterior


almacenamiento (S), si deseamos A = M1 esto se traduce:

L R1, M1
S R1, A

A continuación se presenta una expresión aritmética, su matriz


de formas intermedias y el código objeto generado sin optimizar.
a=b*(c+d/e)

La matriz de formas intermedias generada y optimizada es la


siguientes:

Indice Temporal Operando1 Operando2 Operador Sig. Ant

1 M1 d e / 2 0
2 M2 c M1 + 3 1
3 M3 b M2 * 4 2
4 a M3 = 0 3
76

El código objeto generado será:

L R1, d
D R0, e M1 = d / e
S R1, M1
L R1, c
A R1, M1 M2 = c + M1
S R1, M2
L R1, b
M R0, M2 M3 = b * M2
S R1, M3
L R1, M3
S R1, a a = M3

Como ya se indicó el código objeto generado no está optimizado,


por lo que se hace necesario aplicar la fase de optimización
dependiente.

3. Optimización Dependiente

La optimización dependiente se realiza con la intensión de


disminuir el número de operaciones de carga y almacenamiento de
funcionan con el mismo operando y registro. Es conveniente
considerar la conmutatividad de las operaciones aritméticas.

Todas aquellas operaciones de almacenamiento y carga donde


se involucre el mismo operando son eliminadas siempre y cuando
no alteren alguna operación posterior, de lo contrario se eliminará
sólo la carga. Obsérvese a continuación la optimización del código
objeto generado anteriormente:
77

Código Original Código Optimo


L R1, d L R1, d
D R0, e D R0, e
S R1, M1 S R1, M1
L R1, c L R1, c
A R1, M1 A R1, M1
S R1, M2 S R1, M2
L R1, b L R1,b
M R0, M2 M R0, M2
S R1, M3 pueden ser S R1, a
L R1, M3 eliminadas
S R1, a

El código objeto obtenido está optimizado, aún cuando si se


analiza profundamente se puede determinar que tanto la suma (A)
como la multiplicación (M) son operaciones conmutativas por los
que se pueden intercambiar los operandos y optimizar aún más el
código objeto.

Código Original Código Modificado Código Optimo


L R1, d L R1, d L R1, d
D R0, e D R0, e D R0, e
S R1, M1 S R1, M1 A R1, c
L R1, c L R1, M1 M R0, b
A R1, M1 A R1, c S R1, a
S R1, M2 S R1,M2
L R1, b L R1, M2
M R0, M2 M R0, b
S R1, M3 S R1, M3
L R1, M3 L R1, M3
S R1, a S R1, a
78

4. Contenido del programa objeto

a.- Diccionario de símbolos externos (áreas common,


secciones de control, referencias externas, etc.)

b.- Texto (instrucciones en código máquina y datos).

c.- Diccionario de direcciones relocalizables.

d.- Instrucción END.

G. Análisis Semántico

Esta fase se realiza paralelamente a algunas fases de las ya


estudiadas.

1. Función

a.- Verificar tipo de operando.


b.- Control de flujo (chequeo de operaciones aisladas).
c.- Detectar declaraciones duplicadas de variables.
d.- Detectar declaraciones omitidas de variables.
e.- Detectar uso duplicado de identificadores (módulos,
subprogramas, variables, etc.)

H. Manejador de Errores de Compilación

1. Función

a.- Detección. Indicar tipo y ubicación de errores.


b.- Recuperación. Capacidad de detectar errores múltiples.
79

2. Tipos de Errores

a.- Léxico.
b.- Sintáctico.
c.- Semántico.
d.- Carga (revisión interfaz con subprogramas).
e.- Ejecución.

I. Otras Salidas del Compilador

a.- Listado del programa fuente.


b.- Listado del programa objeto en lenguaje ensamble.
c.- Información sobre la compilación.
d.- Listado de errores.

J. Pases del Compilador

Los pases de un compilador se refiere a las veces que debe leer el


programa fuente o la base de datos generada en alguna fase previa,
para realizar su tarea de traducción. Así que:

a.- Un compilador puede tener uno o varios pases.


b.- Cada pase puede incluir una o varias fases del compilador.
c.- Cada pase almacena su salida en un archivo temporal.
d.- El número de pases depende de:
 Naturaleza del lenguaje.
 Tamaño de la memoria de la máquina.
f.- Máquinas con poca memoria requieren compiladores de varias
pasadas.
80

g.- Compiladores de pocos pases son más rápidos (crean y leen


menos archivos temporales).
VI. CALIDAD A TRAVES DE LA INGENIERIA DE
SOFTWARE

Desde finales de los años 60 se han realizado esfuerzos por


perfeccionar y sistematizar la forma de crear programas. La
mayoría de estas técnicas están basadas en el método tradicional
de cascada. Esto divide el desarrollo de software en cinco etapas.
En un mundo ideal, las aplicaciones se construyen con una
progresión lógica a través de los cinco pasos siguientes:

 Análisis: se definen y describen las características


externas del sistema en un documento de requisitos o
requerimientos.

 Diseño: se reparte el diseño del sistema en subsistemas y


se define la interfaz y la funcionalidad de cada
subsistema.

 Codificación: se codifican y ensamblan los subsistemas


en el sistema completo.

 Comprobación: se realiza para verificar que el sistema


hace lo que se supone que debe hacer.

 Mantenimiento: se revisa el programa para acoplar


nuevas especificaciones y corregir errores una vez
iniciada su producción.

81
82

Esta progresión lógica queda reflejada en la ilustración


siguiente:

Análisis

Diseño

Codificación

Comprobación

Mantenimiento

Ilustración 9. Modelo de desarrollo en cascada de software

Desgraciadamente, el modelo en cascada no describe con


precisión la forma verdadera de desarrollo de software. El mundo
real de la creación de programas no procede uniformemente desde
el primero al último paso sino que más bien, en cualquier etapa, es
típica la necesidad de dar un paso o atrás antes de seguir
progresando. La interacción es más regla que excepción en el
desarrollo de software. Los problemas que afloran durante la
comprobación pueden exigir una modificación del documento de
requisitos. No hay nada especialmente procedimiental u orientado a
83

objeto en este proceso repetitivo, tal y como se muestra en la


ilustración siguiente:

Análisis

Diseño

Codificación

Comprobación

Mantenimiento

Ilustración 10. Modelo de desarrollo reiterativo de software

Por otra parte asegurar la calidad (en algún tiempo llamado


control de calidad), ha sido desde siempre un motivo de interés
para las empresas, como debería ser para los analistas y
diseñadores de los sistemas y las diferentes aplicaciones. Es
riesgoso considerar tanto el análisis como el proceso del diseño, sin
el enfoque que permite asegurar la calidad del producto. Para ello
la ingeniería de software pone a disposición el diseño de sistemas y
de software, con enfoques modulares descendientes, objetos y
84

otros; la documentación del software con instrumentos apropiados;


la evaluación, el mantenimiento y la auditoría de software.

Son dos las ideas subyacentes al aseguramiento de la calidad.


La primera consiste en que el usuario, del sistema de información
para la administración, o del sistema de apoyo para la toma de
decisiones o de la aplicación, es el elemento más importante para
establecer y evaluar la calidad. El segundo reside en que
definitivamente es mucho menor el costo de corregir los problemas
cuando estos se encuentran en sus etapas iniciales que esperar a
que estos se expresen mediante quejas de los usuarios o la
aparición de crisis.

El aseguramiento de la calidad total (ACT) es esencial en cada


uno de los pasos del desarrollo de los sistemas. El concepto de la
calidad se ha ampliado con el transcurso del tiempo, para reflejar
un enfoque de la organización, más que exclusivamente de
producción. En lugar de ubicar la calidad como el control del
número de productos defectuosos, se entiende como la evolución
hacia la perfección, que se denomina el aseguramiento de la
calidad total. El énfasis en la calidad ha englobado una
combinación de factores que reflejan la preocupación corporativa
para asegurar la calidad a todos los niveles. Estos factores
principalmente muestran el interés genera hacia el incremento de la
productividad del personal.

En la ilustración 11, en el primer gráfico se expresa el nivel de


esfuerzo invertido en cada etapa del ciclo de vida del software, por
otra parte en el segundo gráfico se muestran los niveles óptimos de
esfuerzo si se emplean criterios de calidad a lo largo de la vida del
85

sistema.

ESFUERZO
SITUACION REAL
Demasiado
Esfuerzo

Rango
deseable
de esfuerzo

Mínimo
Esfuerzo

Análisis Diseño Desarrollo Mantenimiento


ETAPAS

ESFUERZO

Demasiado SITUACION ESPERADA


Esfuerzo

Rango
deseable
de esfuerzo

Mínimo
Esfuerzo

Análisis Diseño Desarrollo Mantenimiento


ETAPAS

Ilustración 11. Esfuerzo invertido en cada etapa de ciclo de vida del


sistema. (Situación Real y Esperada)
VII. SOLUCION DE PROBLEMAS COMPUTARIZADOS

Todas las situaciones de la vida pueden ser enmarcadas en el


enfoque de sistemas, por lo que para resolver problemas uno de los
mecanismos que facilita su resolución es dicho enfoque.

Un sistema se puede resumir como Entrada, Proceso y Salida,


por lo que al resolver un problema podemos llevarlo a:

 Datos con los que se cuentan (Entradas).


 Información que se generará (Salida)
 Entre ellos existe una transformación de datos a
información (Proceso).

ENTRADAS SALIDAS
PROCESO
(Datos) (programa) (Información)

Ilustración 12. Plantilla sistémica.

Lógicamente existen procesos complejos que a su vez pueden


ser resueltos como agrupaciones de sistemas aplicándose lo ya
explicado, que no es más que la conocida metodología
descendente.

Un ejemplo sencillo de la solución a través del enfoque de


sistemas es el utilizado por los niños en la escuela para resolver

86
87

problemas matemáticos, donde lo realizan colocando una columna


con los datos del problema, otra que indica las operaciones y al
final una columna con la repuesta.

Claro está no sólo basta colocar al problema sobre la plantilla


sistémica sino que se hace necesario utilizar las mejores
herramientas para optimizar los procesos que permitirán obtener
soluciones oportunas y ciertas; para ello se deben seguir algunos
criterios que a continuación se mencionan:

 Seleccionar una técnica de programación idónea.


 Estructurar según la técnica un algoritmo descriptivo de los
procesos a seguir.
 Estándares de presentación de pantallas, salidas, etc.;
Nomenclatura que rija los identificadores, literales,
algoritmos, etc.
 Identificar los datos y sus respectivas operaciones.

Obsérvese que los criterios mencionados anteriormente no


están ligados a ningún lenguaje de programación, pero si influyen
como parámetro en su selección.

A. Técnicas de Programación

En la actualidad se practican muchas técnica de programación. La


programación estructurada introdujo características de lenguajes
conocidas como estructuras de control que dan apoyo a la ejecución
condicional (construcciones if, if / else y case) e iteración
(construcciones for y while).
88

La programación modular o procedimiental (programación


basada en procedimientos) está a favor de la organización de
programas basados en procedimientos abstractos, los cuales agrupan
muchas acciones pequeñas en una acción mayor y ocultan los
detalles de instrumentación de los procedimientos de quienes lo
utilizan. La interfaz a un procedimiento es el conjunto de argumentos
que este procedimiento recibe y regresa con respecto a la proposición
que realiza la llamada.

Procedimiento 1

Procedimiento 2 DATOS

Procedimiento 3

Ilustración 13. Paradigma Procedimiental

La programación basada en reglas se emplea con frecuencia en la


inteligencia artificial y por lo común se asocia con sistemas basados
en conocimiento. La programación basada en reglas va más allá de
los objetivos de este trabajo, pero se menciona para ilustrar el hecho
de que se dispone de muchas metodologías diferentes de
programación y que distintas metodologías resultan adecuadas para
resolver diferentes tipos de problemas. Es importante estar
familiarizado con una variedad de metodologías de programación y
con los problemas a que se orientan.
89

La programación basada en objetos (abstracción de datos)


equilibra la programación basada en procedimientos al otorgar gran
importancia a acciones y datos. Los grupos de programación basada
en objetos relacionan los datos y procedimientos en tipo de nueva
definición y permiten que los objetos se creen y manipulen como
instancias de estos tipos definidos por el usuario. Los detalles de
instrumentación de las acciones, así como los datos, se ocultan a los
usuarios de los tipos abstractos de datos.

DATOS DATOS
Métodos Métodos

OBJETO OBJETO

DATOS DATOS
Métodos Métodos

OBJETO OBJETO

Ilustración 14. Paradigma Basado en Objetos.

La programación orientada a objetos agrega la especificación de


jerarquía y herencia a la programación basada en objetos.
90

B. Algoritmos

La escritura de un programa en computadora consiste


normalmente en implementar un método de resolución de un
problema, que se ha diseñado previamente. Con frecuencia este
método es independiente del computador utilizado. En cualquier
caso es el método no el programa, el que debe estudiarse para
comprender cómo está siendo abordado el problema. El término
algoritmo se utiliza en informática para describir un método de
resolución de un problema que es adecuado para su
implementación como programa de computador.

Muchos algoritmos interesantes llevan implícitamente


complicados métodos de organización de los datos utilizados. Los
objetos creados de esta manera se denominan estructuras de
datos, y también constituyen un tema principal de estudio en la
informática.

Cuando se desarrolla un programa muy grande, una gran


parte del esfuerzo se destina a comprender y definir el problema a
resolver, analizar su complejidad y descomponerlo en
subproblemas más pequeños que puedan realizarse fácilmente.
Con frecuencia sucede que muchos de los algoritmos que se van a
utilizar son fáciles de implementar una vez que se ha
descompuesto el problema. Sin embargo, en la mayor parte de los
casos, existen unos pocos algoritmos cuya elección es crítica
porque su ejecución ocupará la mayoría de los recursos del
sistema.
91

El compartir programas en los sistemas informáticos es una


técnica cada vez más difundida. Algunos de los mecanismos de
software compartidos de los sistemas dificultan a menudo la
adaptación de los programas estándares a la resolución eficaz de
tareas específicas, de modo que muchas veces surge la necesidad
de reimplementar algunos algoritmos básicos.

1. Análisis de Algoritmo

Para la mayoría de los problemas existen varios algoritmos


diferentes, como partida cada persona presenta una manera
distinta de resolver cualquier problema. ¿Cómo elegir uno que
conduzca a la mejor implementación? Esto actualmente constituye
un área de estudio muy desarrollada de la informática. Con
frecuencia se tendrá la oportunidad de investigar los resultados que
describen el comportamiento de algoritmos fundamentales. De
cualquier forma, la comparación de algoritmos puede ser un
desafío, por lo que serán útiles ciertas pautas generales.

Normalmente los problemas a resolver tienen un tamaño


natural (en general, la cantidad de datos a procesar), al que se
denominará N y en función del cual se tratará de describir los
recursos utilizados (con frecuencia, la cantidad de tiempo utilizado).
El punto de interés es el estudio del caso medio, es decir, el tiempo
de ejecución de un conjunto tipo de datos de entrada, y el del peor
caso, el tiempo de ejecución para la configuración de datos de
entrada más desfavorable.
92

El análisis del algoritmo conduce a una función matemática


que en algunos casos resulta fácil de determinar, pero por otra
parte existen algoritmos que sus propiedades son totalmente
desconocidas; quizás porque su análisis conduce a funciones
matemáticas no resueltas, o porque se sabe que las
implementaciones son demasiado complejas para analizarlas al
detalle de una manera razonable, o (la mayoría de las veces)
porque los tipos de entrada que se encuentran no pueden
caracterizarse adecuadamente. La mayoría de los algoritmos caen
entre estos extremos: se conocen hechos sobre su rendimiento,
pero en realidad no se han llegado a analizar por completo.

2. Marco de Referencia para el Análisis de Algoritmos

El primer paso del análisis de un algoritmo es establecer las


características de los datos de entrada que utilizará y decidir cuál
es el tipo de análisis más apropiado. Idealmente, sería deseable
poder obtener, para cualquier distribución de probabilidad de las
posibles entradas, la correspondiente distribución de los tiempos
empleados en la ejecución del algoritmo. Desgraciadamente no es
posible alcanzar este ideal para un algoritmo que no sea trivial, de
manera que por lo regular, se limita el desarrollo estadístico
intentando probar que el tiempo de ejecución es siempre menor
que algún límite superior sea cual sea la entrada, e intentando
obtener el tiempo de ejecución medio para su entrada aleatoria.

El segundo paso del análisis de un algoritmo es identificar


las operaciones abstractas en las que se basa, con el fin de separar
el análisis de la implementación. Así, por ejemplo, se separa el
93

estudio del número de comparaciones que realiza un algoritmo de


ordenamiento del estudio para determinar cuantos microsegundos
tarda un computador en ejecutar un código máquina cualquiera
producido por un compilador determina para el fragmento de código
IF A[I] < V .... Ambos casos se necesitan para determinar cuál es el
tiempo de ejecución real del programa en un computador en
particular. El primero dependerá de las propiedades del algoritmo,
mientras que el segundo dependerá de las propiedades del
computador. Esta separación permite a menudo comparar
algoritmos, independientemente de las implementaciones
particulares o de los computadores que se puedan utilizar.

Mientras que el número de operaciones abstractas


implicadas puede ser, en principio, grande, normalmente se da el
caso de que el desarrollo de los algoritmos que se consideran
depende sólo de unas cuantas cantidades. En general, es fácil
identificar las cantidades significativas para un programa particular,
una forma de hacerlo consiste en utilizar una opción de detección
de perfiles para realizar estadísticas de la frecuencia de llamada
de una instrucción en algún ejemplo de ejecución.

El tercer paso del análisis de un algoritmo es analizarlo


matemáticamente, con el fin de encontrar los valores del caso
medio y del peor caso para cada una de las cantidades
fundamentales. No es difícil encontrar un límite superior del tiempo
de ejecución de un programa, el reto es encontrar el mejor tiempo
límite superior, aquel que se encontraría sí se diera la peor entrada
posible; esto produce el peor caso; el caso medio requiere
normalmente un estudio matemático más sofisticado. Una vez
desarrollados con éxito tales análisis para las cantidades
94

fundamentales, se puede determinar el tiempo asociado a cada


cantidad y obtener expresiones para el tiempo total de ejecución.

En principio, el rendimiento de un algoritmo se puede


analizar a menudo con un nivel de precisión detallado, limitado sólo
por la incertidumbre sobre el rendimiento del computador o por la
dificultad de determinar las propiedades matemáticas de algunas
de las cantidades abstractas. Sin embargo, rara vez, será útil un
análisis detallado completo, de manera que siempre será una
estimación mejor que un cálculo preciso.

El análisis de un algoritmo es un proceso cíclico,


estimándolo y refinándolo hasta que se alcanza una respuesta al
nivel de precisión deseado. Realmente, el proceso también debería
incluir mejoras en la implementación y desde luego el análisis
sugiere a menudo mejoras.

Teniendo en cuenta estas advertencias, el modo de proceder


será buscar estimaciones aproximadas del tiempo de ejecución de
los programas con el fin de clasificarlos, sabiendo que se puede
hacer un análisis más completo para programas importantes
cuando sea necesario.

3. Clasificación de los Algoritmos

Como se mencionó antes, la mayoría de los algoritmos


tienen un parámetro primario N, normalmente el número de
elementos de datos a procesar, que afecta muy significativamente
el tiempo de ejecución. El parámetro N podría ser el grado de un
polinomio, el tamaño de un archivo a ordenar o en el que se va a
95

realizar una búsqueda, el número de nodos de un grafo, etc.


Algunas de las funciones que permiten obtener una evaluación
algoritmica son las siguientes:

1 La mayor parte de las instrucciones de la mayoría de los


programas se ejecutan una vez o muy pocas veces. Si todas
las instrucciones de un programa tienen esta propiedad, se
dice que su tiempo de ejecución es constante. Obviamente,
esto es lo que se persigue en el diseño de algoritmos.

 LOG(N) Cuando el tiempo de ejecución de un programa es


logarítmico, éste será ligeramente más lento a medida que
crezca N. Este tiempo de ejecución es normal en programas
que resuelven un problema de gran tamaño transformándolo
en uno más pequeño, dividiéndolo mediante fracción
constante. El tiempo de ejecución puede considerarse menor
que una gran constante. La base del logaritmo cambia la
constante, pero no mucho: cuando N vale 1000, si la base es
10, LOG N es 3 y si la base es 2 es aproximadamente 10;
cuando N vale un millón, LOG N se multiplica por 2. Cuando
se dobla N, LOG N crece de forma constante, pero no se
duplica hasta que N llegue a N2.

N Cuando el tiempo de ejecución de un programa es lineal,


eso significa generalmente que para cada elemento de
entrada se realiza una pequeña cantidad de procesos. Cuando
N vale un millón, este es también el tiempo de ejecución, que
se duplica al hacerlo N. Esta es la situación ideal para un
algoritmo que debe procesar N entrada u obtener N salidas.
96

 N LOG(N) Este tiempo de ejecución es el de los algoritmos


que resuelven un problema dividiéndolo en pequeños
subproblemas, resolviéndolos independientemente, y
combinando después las soluciones. Ante la falta de un
adjetivo mejor (¿lineal - aritmético?), se dice que el tiempo de
ejecución de tal algoritmo es N LOG(N). Cuando N vale un
millón N LOG(N) es aproximadamente veinte millones.
Cuando N se duplica, el tiempo de ejecución es más del doble
(aunque no mucho más).

 N2 Cuando el tiempo de ejecución de un algoritmo es


cuadrático, sólo es práctico para resolver relativamente
pequeños. El tiempo de ejecución cuadrático normalmente
aparece en algoritmos que procesan pares de elementos de
datos (por ejemplo, en un ciclo anidado doble). Cuando N vale
mil, el tiempo de ejecución es un millón. Cuando N se dobla, el
tiempo de ejecución se multiplica por cuatro.

 N3 De igual manera, un algoritmo que procesa tríos de


elementos de datos (por ejemplo, en un ciclo anidado triple)
tiene un tiempo de ejecución cúbico y no es útil más que en
problemas pequeños. Cuando N vale cien, el tiempo de
ejecución es un millón. Cuando N se duplica, el tiempo de
ejecución se multiplica por ocho.

 2N Pocos algoritmos con un tiempo de ejecución exponencial


son susceptibles de poder ser útiles en la práctica, aunque
aparecen de forma natural al aplicar el método de la fuerza
bruta en la resolución de problemas. Cuando N vale veinte, el
97

tiempo de ejecución es un millón. Cuando N dobla su valor, el


tiempo de ejecución se eleva al cuadrado.

El tiempo de ejecución de un programa particular es


probablemente igual a alguna constante multiplicada por una de
sus términos (el término principal) más algunos términos más
pequeños. Los valores del coeficiente constante y de los términos
incluidos dependen de los resultados del análisis y de los detalles
de la implementación. De forma esquemática, el coeficiente del
término principal está condicionado por el número de instrucciones
que hay en el ciclo interno; en cualquier nivel del diseño del
algoritmo, es prudente limitar el número de estas instrucciones.
Para grandes valores de N se impone el efecto del término
principal; para pequeños valores de N o para algoritmos diseñados
minuciosamente pueden contribuir más términos, y la comparación
de algoritmos es más difícil. En la mayoría de los casos,
simplemente se dice que el tiempo de ejecución de los programas
es lineal, N LOG(N), cúbico, etc., entendiéndose implícitamente que
en los casos donde sea muy importante la eficacia, debe realizarse
un análisis más detallado o un estudio empírico.

N Log(N) N log(N) N2
10 3 30 100
100 6 600 10.000
1.000 9 9.000 1.000.000
10.000 13 130.000 100.000.000
100.000 16 1.600.000 10 mil millones
1.000.000 19 19.000.000 un billón

Ilustración 15. Valores Relativos aproximados de funciones


98

En la ilustración anterior se indican los tamaños relativos de


algunas funciones para diferentes valores de N. La función
cuadrática domina ampliamente, en especial para N grande, pero
las diferencias entre las funciones pequeñas no son las que podría
esperarse para una N pequeño. Se sobreentiende que no se
muestra esta tabla para que se realicen comparaciones lineales de
las funciones para todos los valores de N; números, tablas y grafos
relativos a los algoritmos específicos pueden mejorar esta tarea,
pero proporciona una primera aproximación bastante realista.

4. Implementación de Algoritmos

Al elaborar un algoritmo se debe tratar su rendimiento como


el factor crucial en la realización correcta de tareas mayores. Este
punto de vista se justifica porque estas situaciones aparecen con
todos los algoritmos, y porque la búsqueda cuidadosa de
soluciones eficaces para una problema conducen frecuentemente a
otros algoritmos más elegantes (y más eficaces). Por supuesto,
este planteamiento restrictivo es muy poco realista, ya que cuando
se resuelve un problema complicado en un computador deben
tomarse en cuenta muchos factores.

Las propiedades del algoritmo, después de todo, son sólo


una cara de la moneda, ya que un computador puede utilizarse
para resolver un problema de forma eficaz sólo si está
suficientemente comprendido. El considerar cuidadosamente las
propiedades de las aplicaciones está más allá del alcance de este
trabajo. La extensión de los algoritmos para resolver problemas
depende de la extensión de las necesidades de las diversas
99

aplicaciones. No existe el mejor algoritmo de búsqueda (por


mencionar un ejemplo), pero un método puede ser idóneo en un
sistema de reservación de una línea aérea y otro podrá ser mejor
para utilizarlo en el ciclo de un programa de facturación.

Los algoritmos rara vez existen en condiciones ideales,


excepto posiblemente en la mente de sus diseñadores teóricos que
inventan métodos sin pensar en ninguna implementación definitiva,
o en la mente de los programadores de aplicaciones, que amañan
métodos ad hoc para resolver problemas que por otra parte están
bien delimitados. El diseño adecuado de un algoritmo supone tener
en cuenta el posible impacto que éste tendrá en las
implementaciones posteriores, y la reprogramación adecuada de
las aplicaciones implica el tener en cuenta las características de
rendimiento de los métodos empleados.

5. Selección de un Algoritmo

En la gran mayoría de las veces se dispondrá de varios


algoritmos para resolver un problema, todos con diferentes
características de rendimiento, variando desde la simple solución
de fuerza bruta (aunque probablemente ineficaz) hasta la solución
compleja bien definida (e incluso óptima). En general, no es cierto
que el algoritmo más eficiente sea el que tiene la implementación
más complicada, ya que algunos de los mejores algoritmos son
bastantes elegantes y concisos. Como se argumentaba
anteriormente, no se puede decidir qué algoritmo utilizar para un
problema sin analizar las necesidades del mismo: ¿Con qué
frecuencia se utilizará el programa?, ¿Cuáles son las
100

características generales del computador a utilizar?, ¿Es el


algoritmo una parte pequeña de una gran aplicación, o viceversa?.

La primera regla de la implementación es que se debe


implementar el algoritmo más simple que resuelva un problema
dado. Si el problema particular con el que se tropieza se resuelve
fácilmente, entonces el algoritmo sencillo podría resolver el
problema y no sería necesario hacer nada más; pero si se requiere
un algoritmo más sofisticado, entonces la implementación sencilla
proporciona una forma de comprobación para casos puntuales y
una línea básica para la evaluación del rendimiento.

Sí sólo se va a ejecutar un algoritmo pocas veces, en casos


que no sean demasiado grandes, entonces seguramente es
preferible que el computador tarde una poco más de tiempo en la
ejecución de un algoritmo un poco menos eficaz, en lugar de que el
programador emplee excesivo tiempo desarrollando una
implementación sofisticada. Por supuesto, existe el peligro de que
se pueda terminar utilizando el programa más de lo que se suponía
originalmente, y por tanto conviene estar siempre preparado para
volver a empezar e implementar un algoritmo mejor.

Si el algoritmo se va a integrar en un gran sistema, la


implementación del método de la fuerza bruta proporciona la
funcionalidad que se requiere de una manera fiable, y
posteriormente podrá mejorarse el rendimiento (de manera
controlada), sustituyendo el algoritmo por otro más refinado. Por
supuesto, cuando se estudie el comportamiento completo del
sistema, se debería tener cuidado para no excluir opciones al
implementar el algoritmo, de tal manera que sea difícil mejorarlo
101

más adelante, y se debería tener especial cuidado con aquellos


algoritmos que dan a lugar cuellos de botellas en la ejecución. En
grandes sistemas, es frecuente que las especificaciones de diseño
del sistema dicten desde el comienzo cuál es el mejor algoritmo, es
decir establezca algunas pauta en cuanto a la utilización de
instrucciones de decisión y cíclicas, procesos de entrada y salida,
etc.. Por ejemplo, puede que una estructura de datos compartida
por el sistema sea una lista encadenada o un árbol, por lo que son
preferibles los algoritmos basados en estas estructuras particulares.
Por otro lado, cuando se tomen decisiones a la escala del sistema
del sistema se debe prestar mucha atención a elegir los algoritmos
a utilizar porque al final es muy frecuente el comportamiento de
todo el sistema dependa de algún algoritmo básico.

Sí el algoritmo sólo se va a ejecutar unas cuantas veces,


pero sobre problemas muy grande, entonces se deseará
asegurarse que se obtendrá una salida coherente, y tener una
estimación de cuánto tiempo tardará. Aquí otra vez, una
implementación sencilla puede ser a veces bastante útil para la
resolución de una tarea larga, incluyendo el desarrollo de todo lo
necesario para la comprobación de los resultados.

El error más común a la hora de seleccionar un algoritmo es


ignorar las características de rendimiento. Los algoritmos más
rápidos suelen ser los más complicados, y también es frecuente
que las personas que los desarrollan estén dispuestas a aceptar un
algoritmo más lento para evitar trabajar con una complejidad
añadida. Pero, a menudo, un algoritmo más rápido no tiene por qué
ser mucho más complicado, y trabajar con una pequeña
102

complicación añadida es un pequeño precio a pagar para evitar


manejar una algoritmo lento.

El segundo error más común que se comente, a la hora de


seleccionar un algoritmo, es dar excesiva importancia a las
características de rendimiento. Un algoritmo en N LOG(N) podría
ser ligeramente más complicado que un algoritmo cuadrático para
resolver el mismo problema; pero un algoritmo en N LOG(N), más
eficaz, podría dar a lugar a un incremento sustancial de la
complejidad (y en realidad podría ser más rápido sólo para valores
de N muy grandes). También ocurre que muchos programas de
hecho sólo se ejecutan pocas veces; el tiempo necesario para
implementar y depurar un algoritmo optimizado podría ser
considerablemente mayor que el tiempo que se necesita para
ejecutar uno sencillo que es un tanto más lento.

6. Análisis Empírico

Frecuentemente se da el caso que el análisis matemático


aporta muy poca luz sobre cómo es el comportamiento esperado de
un algoritmo particular en una situación concreta. En tales casos,
es necesario un análisis empírico, en el que se implementa
cuidadosamente un algoritmo y se controla su ejecución con una
entrada típica. De hecho, esto debería realizarse incluso cuando se
disponga de los resultados matemáticos completos, con el fin de
comprobar su validez.

Dados dos algoritmos que resuelven el mismo problema, el


método es muy claro; se ejecutan los dos para ver cual de ellos
lleva más tiempo; decir esto podría parecer muy obvio, pero
103

probablemente sea la omisión más común en el estudio


comparativo de algoritmos. El hecho que un algoritmo sea diez
veces más rápido que otro es muy improbable que se le escape a
alguien que espera que uno de ellos acabe en tres segundos y que
otro acabe en treinta, pero es muy fácil que se le pase por alto
como un pequeño factor constante en un análisis matemático.

Sin embargo, también es fácil cometer errores cuando se


comparan implementaciones, en especial si intervienen diferentes
máquinas, compiladores o sistemas, o si están comparando
programas muy grandes con entradas mal especificadas. Desde
luego, uno de los factores que condujo al desarrollo del análisis
matemático de los algoritmos fue la tendencia a confiar en los
modelos estándar, cuyo comportamiento probablemente se
entienda mejor a través de un análisis cuidadoso.

El principal peligro que se corre al comparar empíricamente


programas es que una implementación puede ser más optimizada
que otra. Es probable que el creador de un nuevo algoritmo preste
una atención muy cuidadosa a cada aspecto de su implementación,
pero no a los detalles de la implementación del algoritmo clásico
rival del suyo. Para confiar en la precisión de un estudio de
comparación empírico hay que estar seguro de que se ha prestado
la misma atención a ambas implementaciones. Afortunadamente, el
caso más frecuente es el siguiente: muchos algoritmos excelentes
se han obtenido haciendo modificaciones relativamente pequeñas
de otros algoritmos creados para resolver el mismo problema o
similares, siendo válidos los estudios comparativos.
104

Un caso particular importante surge cuando se compara un


algoritmo con otra versión de sí mismo o cuando se comparan
implementaciones ligeramente diferentes. Una buena manera de
comprobar la eficacia de una modificación en particular, o de otra
idea de la misma implementación, es ejecutar ambas versiones con
alguna entrada tipo y en consecuencia seleccionar la más rápida.
De nuevo, parece obvio mencionarlo, pero ¡el usuario debe tener
cuidado!, ¿porqué? hay un sorprendente número de investigadores
dedicados al diseño de algoritmos que nunca implementan sus
diseños.

Como antes se esbozó, el criterio adoptado aquí es que


diseño, implementación, análisis matemático y análisis empírico
(todos ellos conjuntamente) contribuyen de forma crucial al
desarrollo de unas buenas implementaciones de algoritmos. Se
utilizan las herramientas disponibles para obtener toda la
información sobre las propiedades de los programas, y después se
les modifica o se desarrollan nuevos programas, a partir de dicha
información. Por otro lado, no siempre está justificado hacer un
gran número de cambios pequeños con la esperanza de mejorar
ligeramente la ejecución.

7. Optimización de un Programa

El procedimiento general para modificar un programa, con la


finalidad de conseguir otra versión más rápida en su ejecución, se
denomina optimización del programa. Este quizás no sea el
término adecuado, porque es poco probable encontrar una
implementación que sea la mejor, pero, aunque no se pueda
optimizar el programa, se espera mejorarlo. Normalmente, la
105

optimización del programa se realiza de forma automática, como


parte del proceso de compilación, (ver CAPITULO V), para mejorar
el rendimiento del código compilado. Aquí se utiliza el término para
referirse a las mejoras específicas del algoritmo. Por supuesto, el
proceso también depende bastante del entorno de programación y
de la máquina utilizada; por tanto aquí se consideran cuestiones
generales y no técnicas específicas.

Este tipo de actividad está justificada sólo si se está seguro


de que el programa se empleará muchas veces o sobre grandes
conjuntos de datos y si la experimentación demuestra que el
esfuerzo dedicado a mejorar la implementación será recompensado
con una mejor ejecución. La mejor forma de perfeccionar el
rendimiento de un algoritmo es mediante un proceso gradual de
transformación en mejores programas y mejores implementaciones.

El primer paso en la implementación de un algoritmo es


desarrollar una versión inicial en su forma más simple. Esto
proporciona una línea básica para posteriores refinamientos y
mejoras y, como se mencionó anteriormente, es muy frecuente que
haya que realizar todo esto. Se deben contrastar los resultados
matemáticos disponibles con los resultados de la implementación;
por ejemplo, si el análisis indica que el tiempo de ejecución es
O(LogN), pero el tiempo de ejecución real es de varios segundos,
entonces algo falla, o bien la implementación, o bien el análisis, y
ambos deben estudiarse con más cuidado.

El siguiente paso es identificar el ciclo interno y tratar de


minimizar el número de instrucciones que lo componen. Quizás la
manera más fácil de encontrar el ciclo sea ejecutar el programa y
106

comprobar que instrucciones se ejecutan más a menudo. Cada


instrucción del ciclo debería someterse a un cuidadoso examen;
¿es realmente necesaria?, ¿Existe una manera más eficaz de llevar
a cabo la misma tarea?. Por ejemplo, por lo general merece la pena
codificar algo más, para eliminar llamadas a procedimientos desde
el ciclo interno. Existen otras técnicas automáticas para hacer eso,
muchas de las cuales están implementadas en compiladores
estándar. A final de cuentas, el mejor rendimiento se logra
traduciendo el ciclo interno a lenguaje máquina o lenguaje
Assembler, pero esto suele ser el último recurso.

En realidad no todas las mejoras producen ganancias en el


rendimiento de modo que es sumamente importante comprobar el
alcance del ahorro obtenido en cada caso. Además, a medida que
la implementación se perfecciona más y más, es aconsejable volver
a comprobar si está justificada esta profundización en los detalles
del código.

No resulta conveniente embarcarse en un intento serio de


acelerar un programa, sin tener un conocimiento bastante detallado
del sistema operativo y del entorno de programación. La versión
óptima de un programa se puede volver bastante frágil y difícil de
modificar, y un compilador o un sistema operativo nuevos (por no
nombrar un computador nuevo) podrían arruinar por completo una
implementación cuidadosamente optimizada.

La implementación de un algoritmo es un proceso cíclico del


desarrollo de un programa: se concibe, se depura, se estudian sus
características y después se refina la implementación hasta que se
alcanza el nivel de rendimiento deseado. El análisis matemático
107

puede ayudar en el proceso: primero, para sugerir que algoritmos


son susceptibles de llevar a cabo una cuidadosa implementación;
segundo, para ayudar a comprobar que la implementación se
desarrolla como se esperaba. En algunos casos este proceso
puede conducir al descubrimiento de ciertas propiedades, que
hacen posible un nuevo algoritmo o mejoras sustanciales de una
versión más antigua.

C. Estándares

Otra manera de lograr eficiencia en el momento de implementar


algoritmos es manteniendo una misma simbología entre cada uno
de los programadores, con la intensión de facilitar el entendimiento
y acoplamiento de los módulos y esto se logra utilizando
estándares. La creación de estándares no es un proceso sencillo de
realizar, es por ello que a continuación se plantean algunas
consideraciones que puedan servir de guía a quienes les
corresponda tan compleja función.

1. Identificadores y Literales

Es importante recordar que los programas a lo largo de su vida


útil sufren modificaciones por lo que es importante mantener reglas
claras que permitan diferenciar los identificadores (variables de
almacenamiento, campos, constantes, acumuladores, contadores,
literales, etc.), es decir, colocarles nombres que indiquen su
función, valor almacenado, etc. Al trabajar independientemente del
lenguaje de programación se pueden colocar nombres
suficientemente largos como para saber de que se trata, ejemplo:
acumulador_de_notas, código_de_la_asignatura. Lógicamente esto
108

trae problemas al pasar estos identificadores a cualquier lenguaje


de programación, por lo que se recomienda reducir su longitud a 10
caracteres que normalmente son aceptados como mínimo por los
lenguajes de programación modernos.

Una estructura de identificador aceptada para 10 caracteres de


longitud puede ser:
 Colocar indicador de variable de memoria o campo de
archivo en los casos que aplique. Esto se realiza
colocando una letra que indique su origen.
 Colocar prefijo calificador estándar. (Ver ilustración 16)
 Usar abreviaturas o acrónimos usuales para combinación
de palabras. De sobrepasar el máximo permitido, se
deberá eliminar letras de derecha a izquierda. De formarse
nombres de identificadores iguales al realizar abreviaturas,
utilizar sinónimos.

2. Nombres de Programas, Archivos de Datos, etc.

Al igual que los identificadores la manera de reconocer un


archivo de datos, de salida o un programa fuente debe regirse por
reglas de construcción para así facilitar a los distintos
programadores su acceso e inequívoca identificación. En algunos
sistemas operativos los nombre pueden ser tan extensos como el
usuario lo requiera, pero esto no es la generalidad por lo que se
recomienda mantener una estructura donde se formen los nombres
con los siguientes tópicos.
 Identificación del sistema al que pertenece el archivo.
109

 Identificador del tipo de fuente: Programas, librerías,


procedimientos, pantallas, datos (maestro, transacciones,
histórico, temporal, etc.), índice, salidas, etc.
 Consecutivo.

PREFIJO SIGNIFICADO PREFIJO SIGNIFICADO

COD Código NUM Número


SWI Switch, Indicador DES Descripción
MON Monto CST Costo
VAL Valor NOM Nombre
APE Apellido CED Cédula
FAC Factor CAN Cantidad
FEC Fecha DIA Día
MES Mes AÑO Año
HOR Hora MIN Minuto
SEG Segundo POR Porcentaje
SEC Sección DIV División
CAP Capítulo TOP Tópico
ANX Anexo TXT Texto
UOR Unidad Organizativa ACU Acumulador
UME Unidad de Medida PRM Promedio
DIF Diferencia CNT Contador
PRM Parámetro INX Indice
IDN Identificador DIR Dirección
TIT Título ENC Encabezado
DPT Departamento FUN Función
PRC Procedimiento PRO Producción
ESC Escuela UNI Unidad
UBI Ubicación CRG Cargo
JEF Jefe DEP Dependencia
ABO Abono PAG Pago
PGN Página PER Período
CMP Compromiso CTB Contable
CAU Causado HAB Haber
DEB Debe SAL Saldo

Ilustración 16. Prefijos clasificadores estándares.


110

3. Pantallas

El manejo adecuado de las pantallas y el área de trabajo de


ellas resulta importante en la medición de la amigabilidad de la
aplicación hacia el usuario, existen distintas maneras de distribuir el
área de la pantalla dependiendo del enfoque utilizado (ventanas o
pantallas planas), por lo que se plantean las siguientes condiciones
para su implementación:

 Encabezados: Para ello se utilizan máximo 6 líneas


contadas desde la parte superior de la pantalla y con un
ancho de 80 columnas. En esta área se coloca la
identificación del sistema, de la organización y del módulo
que se está ejecutando, opcionalmente la fecha. Sí el
sistema trabaja bajo la modalidad de ventanas esta
información puede estar en una ventana que se muestra
por selección del usuario, por lo que se utiliza esta área
para trabajo.

 Área de trabajo: Es el espacio disponible o área


efectiva de utilización del usuario. Está constituida por 13 o
14 líneas y todo el ancho de la pantalla y sobre ella
ocurrirán todas las interacciones USUARIO-SISTEMA;
presentándose allí todos los menúes, pantallas de entrada
o de salida. Esta área se incrementa cuando se utiliza la
modalidad de ventanas ya que el menú será mostrado en
la parte superior de la pantalla y el área de entradas y
salidas puede llegar a ser de 20 a 22 líneas.
111

 Implementación de Diálogos en Línea: Los


menúes de opciones podrán estructurarse de forma
vertical u horizontal, moviéndose con el sistema de flechas
o ratón, aceptando la opción con RETURN o CLICK del
ratón y dejando la opción con ESC o señalando un
extremo de la ventana.

 Ayudas: deben estar implementadas con explicaciones


claras del funcionamiento del módulo o descripción del
dato solicitado, presentándose a través de ventanas. Allí
se deben definir los objetivos de principales, limitaciones
de entradas, rangos de valore permitidos y válidos. Podrá
colocarse en cualquier parte del área de trabajo.

 Errores y Mensajes: La emisión de mensajes


informativos y mensajes de errores deberá canalizarse a
través de un archivo maestro donde estarán contenidos los
mensajes codificados y con sus respectivas ayudas. Estos
mensajes se muestran al usuario en la parte inferior de la
pantalla utilizando las últimas cuatro líneas y con su
respectiva ventana.

 Definición de teclas funcionales: El uso de teclas


funcionales es una manera práctica de estandarizar la
ubicación y personalización de las actividades presentes
en el sistema. En la gran mayoría de los sistemas las
ayudas se ubican en F1. Las otras teclas se distribuyen
según criterios de programador, pero siempre
manteniéndose fijas a lo largo del sistema, es decir, si F2
112

es grabar en inclusión de materiales, también lo será en


inclusión de pedidos.

4. Otras Estandarizaciones

Otras consideraciones importantes que deben mantenerse


pendientes son las siguientes:

 Cada módulo debe estar claramente documentado,


comenzando por un encabezado que especifique: Nombre
del módulo, fecha de creación, fecha de la última
modificación, autor, librerías usadas, objetivo. Por otra
parte si este módulo está formado por subprogramas se
debe colocar comentarios a cada uno de ellos indicando:
Objetivo del subprograma y parámetros de entrada, salida
y entrada/salida.

 No debe existir direccionamiento alguno en las llamadas a


librerías, programas o archivos, esto es para facilitar el
mantenimiento y para evitar cualquier inconveniente
durante la instalación. De lo contrario debe incorporarse
un programa que se encargue del proceso de creación de
vías e instalación y copia de los archivos y programas
respectivos.

 Como se mencionó durante las consideraciones de


evaluación de los algoritmos, el programador debe crear
un paquete formal de datos de prueba, con los cuales
debe comprobar o medir la confiabilidad y eficiencia del
sistema, así como verificar los controles de entradas
113

(validaciones). Estos datos de prueba deben cubrir la


mayor cantidad de alternativas.

 Cuando se implementan aplicaciones con capacidad


multiusuario deben considerarse todos los aspectos de
seguridad y bloqueo de datos, así como las visiones de
usuarios dependiendo de claves de seguridad y
privilegios.
VIII. SELECCION DE UN LENGUAJE DE
PROGRAMACION

A lo largo del presente trabajo se han mostrado consideraciones


importantes en cuanto a los lenguajes de programación, estructura,
traductores y características, proceso de compilación y fases,
incorporación de calidad y evaluación de los algoritmos como punto
de apoyo en la elaboración de aplicaciones óptimas. Es bien sabido
que las aplicaciones deben cumplir requerimientos, ciertos niveles
de rendimiento, ser amigables y de fácil mantenimiento, entre otros;
por lo que se hace necesario realizar algunas consideraciones al
seleccionar el lenguaje de programación a utilizar en la
implementación y es por ello que se presentan algunos parámetros
que pueden servir para seleccionar un lenguaje idóneo a la
aplicación a desarrollar.

Todos los programadores tienen un lenguaje favorito, al cual


están unidos emocionalmente; por lo cual pocas personas pueden
discutir en forma objetiva acerca de los méritos relativos de los
lenguajes. Dadas estas condiciones, es probable que cualquier
comparación entre lenguajes de programación basada en cualquier
criterio sea estudiada por muchos expertos en lenguajes de
programación. Sin embargo, la ilustración 17 y la siguiente
discusión tratan de comparar los lenguajes de programación para
poder proporcionar un entendimiento elemental de los atributos de
varios de los más importantes.

114
115

La ilustración 17 compara los lenguajes en términos de


varias características. La primera, es lo amistoso con el usuario, es
una calificación global de la facilidad con que se usa y se aprende
el lenguaje; por ejemplo, sí un lenguaje es parecido al inglés, es
más fácil de seguir y entender su lógica. La facilidad de aprendizaje
es un compuesto de varios aspectos del lenguaje, como lo estricto
de las reglas de procedimientos usados (algunos lenguajes
“perdonan” menos los errores menores de sintaxis que otros); en
general, este criterio se relaciona a la cantidad de tiempo requerido
para convertirse en un programador experto en el lenguaje.

COBOL FORTRAN BASIC PL/I RPG ADA

Que tan amistoso es:


Parecido al Inglés Si No Si Mezclado Si Mezclado
Fácil de Aprender Difícil Medio Fácil Difícil Fácil Medio

Longitud de programas Largo Corto Medio/ Medio Medio


Largo

Poder (en términos de Alto Medio Medio Alto Bajo Alto


capacidades)

Estructura de programas Regular Malo Malo Bueno Malo Bueno

Eficiencia en el procesam. Mediano Alto Bajo Alto Bajo Alto


Portabilidad (ANSI) Alto Alto Medio/ Bajo Medio Medio
Alto
1 2
Orientación B/D Científico PG PG B/D PG

Ilustración 17. Características de algunos lenguajes de programación

1
B/D : Sistemas de bases de datos.
2
PG: Propósito general; es decir, puede ser utilizado prácticamente en cualquier tipo de
aplicación.
116

La longitud de los programas de la ilustración pertenece al


número de instrucciones que deben codificarse para realizar una
tarea. En general, los programas en lenguajes que requieren
menos instrucciones pueden escribirse con mayor rapidez, usando
el tiempo del programador con mayor eficiencia, sin olvidar que
requieren de procesos de traducción más complejos.

El poder de un lenguaje que se menciona está relacionado


con lo bien que se puede realizar el rango completo de actividades
de procesamiento de datos de sistemas sin tener que recurrir a
técnicas raras, no convencionales y que consumen mucho tiempo.
El poder evaluaría en forma distinta si el criterio fuera todo el rango
de actividades científicas en lugar administrativas; por ejemplo,
COBOL (calificado alto en la ilustración) estaría calificado bajo, y
FORTRAN (calificado medio) estaría calificado alto.

La característica de la estructura del programa se refiere a la


capacidad de cada lenguaje para producir programas que se
conforman a los principios de la programación estructurada, la cual
es un enfoque sistemático al diseño de programas que facilita el
desarrollo de los mismos, los hace más fáciles de comprender a
alguien que está tratando de entender la lógica, y aumenta la
capacidad de modificar un programa rápidamente. De los lenguajes
que se muestran, sólo dos (ADA y PL/I) proporcionan una buena
estructura de programa, y COBOL puede considerarse
medianamente bueno con respecto a esta característica.

La eficiencia de procesamiento se mide en términos de la


cantidad de tiempo de CPU requerido para traducir y procesar un
programa usando cada uno de los lenguajes; mientras que el
117

resultado final de esta característica está en términos de la


eficiencia del lenguaje, en la práctica, la forma en que se haya
diseñado e implantado el compilador o interpretador puede ser por
lo menos tan importante como determinar la eficiencia de
procesamiento.

La portabilidad se mide principalmente en términos de cuantos


sistemas de computadores tienen un programa traductor
(compilador o interpretador) para ese lenguaje; como cada uno de
estos lenguajes es de alto nivel y se conforma a los estándares
ANSI, cualquier computador que tenga el programa de traducción
para el lenguaje puede traducirlo.
IX.LA CALIDAD DEL SOFTWARE EN 45 TERMINOS

- Software: Es el conjunto de todas las instrucciones y de los


datos que deben cargarse en un computador para realizar un
determinado proceso. Por tanto, comprende los sistemas
operativos, compiladores, rutinas de prueba, programas de
aplicación, etc.

- Programa: Un programa A es un conjunto de pares de


estados (s,t) en el que s es el estado inicial del propio cálculo
de A y t es el estado final.

- Ingeniería de Software: Es el conjunto y el uso de


potentes y eficaces métodos y principios de ingeniería
orientados a la producción de programas fiables y
funcionando correctamente.

- Comprensibilidad: Un software es comprensible siempre


que su objetivo resulte claro al usuario.

- Plenitud: Un software está completo sí todas sus partes


están disponibles y sí cada parte se ha desarrollado
completamente.

- Concisión: Un software es conciso si no tiene redundancia


de información.

118
119

- Portabilidad: Un software es portátil si puede transferirse


fácilmente y hacerlo funcionar correctamente en sistemas
diferentes al que se había desarrollado originalmente.

- Coherencia: Un programa es coherente si tiene


uniformidad de notaciones, simbologías y terminologías.
Además es coherente exteriormente si su contenido puede
referirse a las especificaciones del proyecto.

- Mantenibilidad: Un software puede mantenerse a


condición de que pueda adaptarse fácilmente a nuevos
requisitos.

- Demostrabilidad: Un software es demostrativo sí pueden


definirse fácilmente los criterios de aprobación y evaluación
de sus prestaciones técnicas. La demostrabilidad comprende
por lo tanto dos aspectos:
a) La definición de los criterios de demostración.
b) La verificación del cumplimiento de los criterios
definidos anteriormente.

- Utilidad: Un software es útil si resulta adecuado y práctico


con respecto al uso a que va destinado. También la utilidad
presenta dos aspectos diferentes:
a) La posibilidad de reutilización parcial o total en otros
proyectos (no confundir con la portabilidad).
b) La calidad de la interfaz hombre - máquina y de las
salidas y la correcta definición de los archivos y de
los datos de entrada.
120

- Fiabilidad: Definida en sentido llano es la probabilidad que


un software pueda asumir, en los tiempos y en las
condiciones ambientales previstas, las funciones para las que
ha sido proyectado.

- Estructuración: un producto está estructurado sí la


interdependencia entre sus partes están organizadas en una
combinación de tipo camino – tipología.

- Eficiencia: Un software es eficiente sí responde al objeto


para el cual ha sido proyectado sin desaprovechamiento de
los recursos.

- Recursos de software: En sentido llano son las


magnitudes de la memoria, el número total de las
instrucciones, los archivos, las capacidades de los canales de
entrada / salida, etc.

- Independencia: Un producto es independiente de los


dispositivos funcionales sí puede hacerse funcionar sobre
configuraciones de hardware diferentes a la que se ha
proyectado inicialmente.

- Precisión: Un software es preciso sí los resultados que


proporciona son suficientemente exactos con relación al
empleo a que van destinados.
121

- Accesibilidad: Un producto de software es accesible sí los


datos de entrada están claramente definidos y sí los de salida
son fácilmente comprensibles y útiles tanto en la forma como
en el contenido.

- Legibilidad: Un software es legible si funciona bien y sí su


lógica puede deducirse fácilmente de la lectura del código.

- Incrementabilidad: Un producto es incrementable sí


puede acoger fácilmente una expansión de los datos o de las
funciones de ensamblaje.

- Ingeniería Humana: Un software se dice que tiene


cantidad de ingeniería en sus relaciones con el usuario que
satisface el fin previsto sin hacerle perder tiempo o energía y
sin influir negativamente en su moral.

- Modificabilidad: Un producto es modificable sí es que


puede aceptar fácilmente la inclusión de una variante.

- Comunicatividad: Un software es comunicativo sí facilita


la definición de las entradas y sí proporciona resultados
fácilmente comprensibles y útiles tanto en la forma como en
el contenido.

- Autodescriptibilidad: Un software es autodescriptible sí


contiene informaciones suficientes para que un lector pueda
determinar fácilmente los objetivos, las asunciones, los
122

vínculos, las entradas y las salidas, los componentes y los


estados de los programas sencillos.

- Cobertura funcional: Es la inserción de flags, etiquetas y


otros símbolos de identificación (eventualmente acoplados
con frases de aserción) después de cada función de un
programa para señalar, durante la fase de ejecución, sí todas
esas funciones se han realizado en la secuencia adecuada.

- Verificación del software: Sirve para determinar si un


producto funciona de manera que satisfaga los requisitos
contractuales.

- Convalidación del software: Sirve para establecer sí un


producto funciona de manera satisfactoria en el ámbito de un
sistema global (y, por tanto, también comprende una
evaluación de los requisitos del software).

- Certificación del software: Ratifica la cantidad


(fiabilidad, etc.) del software. Puede realizarse solamente por
una entidad que tenga la autoridad.

- Prueba del software: Consiste en una ejecución del


software con el fin de determinar sí los resultados producidos
son correctos.

- Depuración del software: Es el proceso de


identificación, localización y corrección de los errores.
123

- Inspección del software: Es un examen del software y


de los correspondientes materiales de soporte.

- Aprobación del software: Es un proceso de control final


y de mejora del software para hacerlo utilizable al usuario.

- Prueba de las prestaciones: Es un proceso de


demostración de la respuesta de un producto de software
ante ciertos requisitos de ejecución, que por ejemplo pueden
corresponder a los tiempos de máquina o a la dimensión de la
memoria ocupada.

- Prueba de aceptación: Es el conjunto de todos los


procesos formales que permiten establecer sí un producto es
aceptable por el usuario.

- Ciclo de vida del software: Comprende la definición de


los requisitos y de las especificaciones, la fase del proyecto,
la implantación de un código legible y asequible para el
computador, el ensamblaje, el ejercicio a gestionar, la
manutención.

- Modelo de software: Es una entidad que representa


útilmente un proceso o un producto.

- Algoritmo: Es un conjunto finito de símbolos, que describe


un conjunto de secuencias de ejecución o de procesos
secuenciales.
124

- Intervalo de fiabilidad: Es la probabilidad de que en un


determinado espacio de tiempo T, el sistema sea operativo y
continúe funcionando durante un intervalo de duración X.

- Disponibilidad: Se divide en dos funciones:


a) La disponibilidad puntual es la probabilidad de que en
un determinado instante de tiempo T, el producto
pueda funcionar entre los límites previstos de
tolerancia técnica y ambiental.
b) El intervalo de disponibilidad es el período de tiempo
durante el cual se puede esperar probabilísticamente
que el producto funcione dentro de las tolerancias
previstas.

- Error (causativo): Es una discrepancia conceptual,


sintáctica u operativa (de menor nivel) que puede conducir a
uno o más fallas de software.

- Fault (sistemático): Es una manifestación específica de


error debido a una discrepancia del software que impide la
posibilidad de realizar la función pedida. Un error puede ser la
causa de diferentes fault.

- Falla: Se produce cuando un dato de entrada produce un


fault del programa que, en consecuencia, no consigue realizar
correctamente la función pedida. Una falla puede ser:
a) Detectada, sí se ha detectado efectivamente.
b) No detectada, en caso contrario.
c) Crítica, sí crea una situación crítica.
125

d) Hard, si provoca la caída de sistema.


e) Soft, si es crítica pero no hard.

- Safe software: Es un software a prueba de errores, es


decir, tan seguro que ningún error de software puede causar
fallas críticas.

- Código estructurado: Es el código de las soluciones de


software en que el acento se coloca sobre las estructuras
lógicas directas.

- Metodología del árbol de las fallas: Es una


metodología de análisis de la propagación de los errores,
primitivos y causativos, a lo largo del programa y de la
consiguiente aparición de fallas de nivel superior.
BIBLIOGRAFIA

AHO ALFRED, SETHI R., ULLMAN J. 1990. Compiladores. Principios,


técnicas y herramientas. Addison-Wesley Iberoamericana. Estados
Unidos.

COLINA CARLOS. Inédita. Notas de Clases de Lenguajes de Programación.


Universidad Centroccidental “Lisandro Alvarado”. Decanato de
Ciencias. Barquisimeto. Venezuela.

CURCIO ARMANDO. 1985. BASIC. Enciclopedia de la Informática.


Microprocesadores y Computadores Personales. Editorial Planeta.
Barcelona. España.

EDICIONES NUEVA LENTE. 1983. Enciclopedia de la Informática. Ingelek,


S.A. España.

KENDALL y KENDALL. 1991. Análisis y Diseño de Sistemas. Prentice-Hall


Hispanoamericana, S.A. México.

MATHISON LUIS E. Inédita. Notas de Lenguajes de Programación.


Universidad Centroccidental “Lisandro Alvarado”. Decanato de
Ciencias. Barquisimeto. Venezuela.

__________________. 1995. Conceptos Básicos sobre Modularidad en


Pascal. Trabajo de Ascenso. Universidad Centroccidental “Lisandro
Alvarado”. Decanato de Ciencias. Barquisimeto. Venezuela.

ROMERO DARWIN y MATHISON LUIS. 1991. Inédito. Manual de


Operacionalización de Sistemas. Universidad Centroccidental
“Lisandro Alvarado”. Dirección de Informática. Barquisimeto.
Venezuela.

SCOTT GEORGE. 1988. Principios de Sistemas de Información. McGraw


Hill. México.

SEDGEWICK ROBERT. 1995. Algoritmos en C++. Addison Wesley


Iberoamericana, S.A. Wilmington. USA.

VOSS GREG. 1994. Programación Orientada a Objetos: Una Introducción.


McGraw Hill. México.

126
127

WINBLAD ANN, EDWARD S. y KING D. 1993. Software Orientado a Objetos.


Addison Wesley Iberoamericana, S.A. Wilmington. USA.

View publication stats

Você também pode gostar