Professional Documents
Culture Documents
Teoría de Lenguajes
(Unidad I)
Tema:
Introducción a la teoría de Lenguajes
Dictado por:
DIANA CECILIA MUÑOZ CASANOVA
M.S. en Ingeniería de Sistemas e Informática
CHIMBOTE – PERÚ
2007
CAPÍTULO I: HISTORIA DE LOS LENGUAJES DE PROGRAMACIÓN
1.1. Introducción 2
1.2. Historia de los lenguajes de programación 3
1.3. Tendencias en los lenguajes de programación 7
4.1. Introducción 43
4.2. Lenguajes de bajo nivel 43
4.2.1.Lenguaje máquina. 44
4.2.2.Ensamblador. 44
A) Ventajas del lenguaje ensamblador 45
B) Desventajas del lenguaje ensamblador 48
4.3. Lenguajes de medio nivel 49
4.4. Lenguajes De Alto Nivel 52
4.5. Generadores de aplicaciones o 4º nivel 53
1.1. INTRODUCCIÓN
Una computadora es una máquina que solo comprende las instrucciones que se le
den en un determinado formato. Cada máquina reconoce y ejecuta un número de
instrucciones diferentes que se agrupan en los distintos lenguajes de
programación.
Un lenguaje de programación es un conjunto limitado de palabras y de símbolos
que representan procedimientos, cálculos, decisiones y otras operaciones que
pueden ejecutar una computadora. A pesar de que en este trabajo parte de la
división de lenguajes de programación en imperativos y declarativos (los cuales a
su vez se dividen en numerosos subgrupos), la clasificación más común y básica
que suele hacerse de los lenguajes de programación es la que los divide en
lenguajes de bajo y de alto nivel. Los lenguajes de programación de bajo nivel
fueron los primeros que surgieron y se llaman así porque están directamente
relacionados con el hardware del computador, es decir, el usuario introduce una
serie de códigos numéricos que la máquina va a interpretar como instrucciones.
Para usar este lenguaje, el programador tenía que conocer el funcionamiento de la
máquina al más bajo nivel y los errores de programación eran muy frecuentes.
Los lenguajes de alto nivel surgieron con posterioridad con el primer compilador
de FORTRAN (FORmula TRANslation), que, como su nombre indica, inició
como un "simple" esfuerzo de traducir un lenguaje de fórmulas, al lenguaje
ensamblador y por consiguiente al lenguaje de máquina, facilitando la labor a los
programadores. A partir de FORTRAN, se han desarrollado innumerables
lenguajes, que siguen el mismo concepto: facilitar la vida al programador,
aumentando la productividad. Estos lenguajes usan un número reducido de
instrucciones (normalmente en inglés) que siguen unas estrictas reglas
gramaticales que se conocen como sintaxis del lenguaje. Pero aunque el
programador de esta forma se distancie del hardware del computador, este sigue
trabajando en lenguaje máquina. Por ello se hace necesaria una traducción a una
secuencia de instrucciones interpretables por el computador. Esta labor es llevada
a cabo por los compiladores y los intérpretes.
El compilador es un programa que se encarga de la traducción global del programa
realizado por el usuario. Esta operación recibe el nombre de compilación. El
programa es traducido completamente antes de que se ejecute, por lo que la
ejecución se realiza en un periodo muy breve. El intérprete por el contrario lleva a
cabo una traducción inmediata en el momento de la ejecución, es decir, irá
ejecutando las instrucciones una a una haciendo que el proceso requiera un
periodo de tiempo sensiblemente mayor del que necesitaría un compilador. Los
intérpretes son usados para traducir programas de alta dificultad de
implementación, en estos casos, las órdenes a traducir son de tal complejidad que
no merece la pena crear un compilador ya que este también tendría que ser de una
complejidad por encima de lo normal.
Hay que mencionar la existencia de lenguajes que combinan características de los
de alto nivel y los de bajo nivel (es decir, Ensamblador). Un ejemplo es C:
contiene estructuras de programación de alto nivel; sin embargo, fue diseñado con
muy pocas instrucciones, las cuales son sumamente sencillas, fáciles de traducir al
lenguaje de la máquina; y requiere de un entendimiento apropiado de cómo
funciona la máquina, el uso de la memoria, etcétera. Por ello, algunos consideran a
lenguajes como C (que fue diseñado para hacer sistemas operativos), lenguajes de
nivel medio, si bien, son considerados mayoritariamente de bajo nivel.
Aplicaciones científicas.
En este tipo de aplicaciones predominan las operaciones numéricas o
matriciales propias de algoritmos matemáticos. Lenguajes adecuados son
FORTRAN y PASCAL
Lenguajes de Internet.
HTML, JAVA, Perl, PHP, etc
Lenguajes imperativos
Lenguajes funcionales
Lenguajes lógicos (declarativos)
Lenguajes orientados a objetos
Lenguajes con concurrencia
En este tipo de lenguajes, cuyo origen está ligado a la propia arquitectura de von
Neumann, la arquitectura consta de una secuencia de celdas, llamadas memoria,
en la cual se pueden guardar en forma codificada, lo mismo datos que
instrucciones; y de un procesador, el cual es capaz de ejecutar de manera
secuencial una serie de operaciones, principalmente aritméticas y booleanas,
llamadas comandos. En general, un lenguaje imperativo ofrece al programador
conceptos que se traducen de forma natural al modelo de la máquina.Los
lenguajes imperativos más destacados de la historia han sido: FORTRAN, Algol,
Pascal, C, Modula-2, Ada
El programador, al utilizar un lenguaje imperativo, por lo general tiene que
traducir la solución abstracta del problema a términos muy primitivos, cercanos a
la máquina. La distancia entre el nivel del razonamiento humano y lo expresable
por los lenguajes imperativos causa que sus programas sean más "comprensibles"
para la máquina que para el hombre. Esta desventaja para nosotros, reflejada en la
dificultad que tenemos al construir programas en un lenguaje imperativo, se
vuelve una ventaja en el momento de la generación del código. El programa está
expresado en términos tan cercanos a la máquina, que el código generado es
relativamente parecido al programa original, lo que permite cierta eficiencia en la
ejecución.
Ejemplo:
Este sería el código de un programa que determina el factorial de un número en un
lenguaje imperativo inventado:
procedimiento factorial(entero n){
entero resultado = 1;
mientras (n > 0) {
resultado = resultado * n;
n = n - 1;
}
devuelve resultado;
}
Este procedimiento (o función) recibe un número entero n. Declara una variable
resultado que será el resultado final a devolver. Inicialmente, tiene como valor el
1. Después llega una estructura de control denominada bucle, que se ejecuta
mientras la condición expresada entre los paréntesis (n > 0) sea cierta. Dentro del
bucle se multiplica la variable resultado por n y el valor se deja de nuevo en
resultado. La siguiente sentencia (n = n - 1) es necesaria para ir haciendo el
factorial, así como para salir en algún momento del bucle. Por último, tras salir del
bucle ya podemos devolver el valor final, que estará en la variable resultado.
Nótese que, si en un principio, la variable n es 0, no se entrará en el bucle al no ser
cierta la condición, por lo que pasaría directamente a devolver la variable
resultado, que tiene valor inicial 1 (0! = 1).
Donde:
y: Es la variable dependiente de la función. Se le llama dependiente porque
para que pueda tomar un valor, depende de los valores que pueda tomar la
variable x. También podemos decir que “y esta en función de x”.
x: Es la variable independiente de la función. Se dice que es independiente ya
que puede tomar los valores que quiera y no hay nada que se lo impida; o sea,
tiene independencia.
f: Se puede decir que es el procedimiento o ecuación que tomará a x para
devolverle un valor a y.
Veamos una función muy común:
y = x2 + x
Los programas imperativos están formados por una serie de datos globales y
un conjunto de instrucciones ó código. Estos dos elementos forman una
abstracción de los datos y código de la memoria principal.
El programador trabaja en un nivel cercano a la máquina lo que le permite
generar programas eficientes. Con esta proximidad aparece, sin embargo, una
dependencia entre el algoritmo y la arquitectura que impide, por ejemplo,
utilizar algoritmos programados para arquitecturas secuenciales en
arquitecturas paralelas.
Los algoritmos escritos en lenguajes imperativos se expresan mediante una
secuencia de instrucciones que modifican el estado de un programa accediendo
a los datos globales de la memoria. En este punto es donde empiezan los
problemas:
Ejemplo
Program prueba;
var flag:boolean;
function f (n: integer):integer;
begin
flag:=not flag;
if flag then f:=n;
else f:=2*n;
end;
........
--Programa principal
begin
flag:=true;
......
write(f(1)); retorna 2
write(f(1)); retorna 1
.......
write(f(1) + f(2)); retorna 4
write(f(2) + f(1)); retorna 5
PROLOG
Prolog, proveniente del francés Programation et Logique, sus orígenes se
remotan a los inicios de la década de los 70 con los trabajos del grupo de A.
Colmerauer en Marsella, Francia. Es un lenguaje de programación lógico e
interpretado, bastante popular en el medio de investigación en Inteligencia
Artificial.
Historia
Se trata de un lenguaje de programación ideado a principios de los años 70
en la universidad de Aix-Marseille por los profesores Alain Colmerauer y
Phillipe Roussel. Inicialmente se trataba de un lenguaje totalmente
interpretado hasta que, a mediados de los 70, David H.D. Warren
desarrolló un compilador capaz de traducir Prolog en un conjunto de
instrucciones de una máquina abstracta denominada Warren Abstract
Machine, o abreviadamente, WAM. Desde entonces Prolog es un lenguaje
semi-interpretado.
Prolog se enmarca en el paradigma de los lenguajes lógicos, lo que lo
diferencia enormemente de otros lenguajes más populares tales como
Fortran, Pascal, C.
Retroseguimiento
En todos los mencionados, las instrucciones se ejecutan normalmente en
orden secuencial, es decir, una a continuación de otra, en el mismo orden
en que están escritas, que sólo varía cuando se alcanza una instrucción de
control (un bucle, una instrucción condicional o una transferencia).
Los programas en Prolog se componen de cláusulas de Horn que
constituyen reglas del tipo "modus ponendo ponens", es decir, "Si es
verdad el antecedente, entonces es verdad el consecuente". No obstante, la
forma de escribir las cláusulas de Horn es al contrario de lo habitual.
Primero se escribe el consecuente y luego el antecedente. El antecedente
puede ser una conjunción de condiciones que se denomina secuencia de
objetivos. Cada objetivo se separa con una coma y puede considerarse
similar a una instrucción o llamada a procedimiento de los lenguajes
imperativos. En Prolog no existen instrucciones de control. Su ejecución se
basa en dos conceptos: la unificación y el backtracking.
Gracias a la unificación, cada objetivo determina un subconjunto de
cláusulas susceptibles de ser ejecutadas. Cada una de ellas se denomina
punto de elección. Prolog selecciona el primer punto de elección y sigue
ejecutando el programa hasta determinar si el objetivo es verdadero o
falso.
En caso de ser falso entra en juego el 'backtracking', que consiste en
deshacer todo lo ejecutado situando el programa en el mismo estado en el
que estaba justo antes de llegar al punto de elección. Entonces se toma el
siguiente punto de elección que estaba pendiente y se repite de nuevo el
proceso. Todos los objetivos terminan su ejecución bien en éxito
("verdadero"), bien en fallo ("falso").
Expresiones
Prolog cuenta con operadores para la unificación y comparación sea con
evaluación o sea simbólica como lo siguiente:
X is Y %unificación con evaluación.
X=Y %unificación simbólica
X=:=Y %comparación con evaluación
X==Y %comparación simbólica.
Listas
La representación de hechos simples no es lo común en la clasificación de
elementos, sino que se agrupan los elementos de un mismo tipo en una
lista.
Ejemplo:
% Si queremos hallar la longitud de una lista.
% La longitud de una lista vacia es 0.
% La longitud de cualquier lista es la longitud de la cola + 1.
longitud([],0).
longitud([H|T],N):-longitud(T,N0), N is N0 + 1.
?- longitud([a,b,c],L).
3
?- longitud([a,b,c],4).
No
% Si queremos determinar si un elemento es pertenece a una lista.
% El elemento pertenece a la lista si coincide con la cabeza de la lista.
% El elemento pertenece a la lista si es se encuentra en la cola de la lista.
pertenece(X,[X|_]).
pertenece(X,[_|R]):- pertenece(X,R).
?- pertenece(b,[a,b,c]).
Yes
?- pertenece(b,[a,[b,c]]).
No
?- pertenece([b,c],[a,[b,c]]).
Yes
% Si queremos eliminar un elemento de la lista.
% Si X es la cabeza de la lista, la cola T es la lista sin X
% Si X no es la cabeza de la lista, conservamos la cabeza de la lista
% como parte de la respuesta y continuamos eliminando X de la cola T.
elimina (X,[X|T],T).
elimina (X,[H|T],[H|T1]):- elimina (X,T,T1).
?- elimina(1,[1,2,3,4],R).
R = [2,3,4]
?- elimina(1,R,[2,3]).
R = [1, 2, 3]
R = [2, 1, 3]
R = [2, 3, 1]
% Si queremos calcular la inversa de una lista.
% La inversa de una lista vacia es una lista vacia.
% La inversa de H|T es la inversa de T concatenada con H.
inversa([],[]).
inversa([H|T],L):- inversa(T,R), concatenar(R,[H],L).
?- inversa([a,b,c,d],[d,c,b,a]).
Yes
3.6.1.ABSTRACCIÓN DE CONTROL
Hace referencia a los mecanismos necesarios para representar el flujo de
control de un programa, evitando detalles de bajo nivel. Se subdivide en: nivel
de sentencias básicas y a nivel de subprogramas o unidades de programa.
Sentencias condicionales: If/else, alternativa múltiple (swith).
Sentencias iterativas: For, While y repeat.
Abstracción procedimental
Subprograma: conjunto de líneas de código con cierta independencia del
resto.
Procedimientos: subprogramas que tienen de 0 a n parámetros, sin tipo,
no pueden ser usados en expresiones y pueden devolver de 0 a n resultados
distintos.
Funciones: pueden tener de 0 a n argumentos de entrada, devuelve solo 1
resultado siempre. Tiene tipo y se puede usar en una expresión.
Paso de parámetros:
Formas de paso de parámetros:
Por referencia: se pasa la dirección de memoria del parámetro (E/S).
Por copia:
Copia por valor: se crea una var temporal copiándole el valor del
parámetro.(E)
Copia por resultado: se crea una var local, y al acabar se devuelve el
resultado (S)
Copia por valor-resultado: se crea una var local pasándole el valor, con
la que se trabaja, y al acabar se devuelve el resultado
Formas de asignar los argumentos:
Por posición: se asignan los parámetros formales con los reales por
orden.
Por nombre: en la llamada se añade el nombre del parámetro formal.
Por defecto: toma un valor por defecto especificado en compilación si
no se le da ninguno.
Ejemplo:
Void ordenar (int a[],int n) el usuario no necesita conocer los detalles de
cómo se realiza el ordenamiento, solo debe suministrar un arreglo y su tamaño
y recibirá el arreglo ordenado (en un orden predeterminado como ascendente)
3.6.2.ABSTRACCIÓN DE DATOS.
Una abstracción de datos (tipo de dato o tipo abstracto de dato) es un nuevo
tipo de dato más un conjunto de operaciones que permiten manipular los
objetos de dicho tipo. En esta definición juega un papel importante el uso de la
palabra objeto en lugar de variable. La diferencia entre ambos conceptos se
explicará más adelante.
La correcta utilización de los tipos abstractos de datos (TADs en lo sucesivo)
en el diseño de programas da lugar a programas de gran modularidad y
calidad:
Son más legibles
Son más fáciles de interpretar
Son más fáciles de modificar
3.9.1.PHP
PHP usa una mezcla entre interpretación y compilación para intentar ofrecer a
los programadores la mejor mezcla entre rendimiento y flexibilidad.
PHP compila para tu codigo una serie de instrucciones (llamadas opcodes)
siempre que estas son accedidas. Estas instrucciones son entonces ejecutadas
una por una hasta que el script termina. Esto es diferente a la manera
convencional de compilacion de lenguajes como C++ donde el código es
compilado a código ejecutable que es despues ejecutado. Php es recompilado
cada vez que se solicita un script. Una ventaja importante de interpretar el
código es que toda la memoria usada por tu código es manejada por PHP, y el
lenguaje automáticamente vacía esta memoria cuando el script finaliza. Esto
significa que tu no tienes que preocuparte de las conexiones a la base de datos,
porque PHP lo hará por ti.
¿Por qué deberías aprenderlo?
Es uno de los lenguajes de programación más populares, la gran fluidez y
rapidez de sus scripts y su prometedor futuro, desarrollar aplicaciones Webs
utilizando lenguajes como C o COBOL son cosas del pasado.
3.9.2.C#
C# es un lenguaje de propósito general orientado a objetos creado por
Microsoft para su plataforma .NET.
Su sintaxis básica deriva de C/C++ y utiliza el modelo de objetos de la
plataforma .NET el cual es similar al de Java aunque incluye mejoras
derivadas de otros lenguajes. C# fue diseñado para combinar el control a bajo
nivel de lenguajes como C y la velocidad de programación de lenguajes como
Visual Basic.
¿Por qué deberías aprenderlo?
Es una parte esencial de la plataforma .Net, C# combina los mejores elementos
de múltiples lenguajes de amplia difusión como C++, Java, Visual Basic o
Delphi. De hecho, su creador Anders Heljsberg fue también el creador de
muchos otros lenguajes y entornos como Turbo Pascal, Delphi o Visual J++.
La idea principal detrás del lenguaje es combinar la potencia de lenguajes
como C++ con la sencillez de lenguajes como Visual Basic, y que además la
migración a este lenguaje por los programadores de C/C++/Java sea lo más
inmediata posible.
3.9.3.AJAX
AJAX no es un lenguaje exactamente su nombre viene dado por el acrónimo
de Asynchronous JavaScript And XML y es posiblemente la mayor novedad
en cuanto a programación web en estos últimos años.
El corazón de Ajax es el objeto XMLHttpRequest que nos permite realizar una
conexión al servidor y al enviarle una petición y recibir la respuesta que
procesaremos en nuestro código Javascript, estamos hablando del verdadero
motor de Ajax, por ejemplo gracias a este objeto podemos desde una página
HTML leer datos de una web o enviar datos de un formulario sin necesidad de
recargar la página.
¿Por qué deberías aprenderlo?
La demanda de AJAX no sólo es amplía sino que de calidad debido a la
dificultad de aprendizaje que conlleva, si la herramienta de Microsoft, Atlas,
destinada a la realización de aplicaciones AJAX tiene éxito puede supone un
aumento en la demanda de esta tecnología.
3.9.4.JAVASCRIPT
Se trata de un lenguaje de programación del lado del cliente, porque es el
navegador el que soporta la carga de procesamiento. Gracias a su
compatibilidad con la mayoría de los navegadores modernos, es el lenguaje de
programación del lado del cliente más utilizado.
¿Por qué deberías aprenderlo?
La razón de mayor peso es que es utilizado por millones de páginas webs para
validar formularios, crear cookies, detectar navegadores y mejorar el diseño,
su fácil aprendizaje lo hace un lenguaje muy demandado.
3.9.5.PERL
Perl es la alternativa más popular a PHP, seguramente porque es el lenguaje
más antiguo tambien dentro de las alternativas. En internet nos encontramos
numerosos recursos que utilizan Perl, muchos de las aplicaciones "open
source" requieren tener Perl instalado correctamente. Perl tiene una ventaja y
es que es muy flexible, y tambien tiene un gran cantidad de modulos ya
escritos.
Bien escritos los scripts en Perl se asemejan bastante a PHP. La principal causa
de la sucía apariencia de Perl es por la afición de sus desarrolladores a la
escritura en "una línea" empaquetanto numerosas funcionalidades en una sola
línea de código.
¿Por qué deberías aprenderlo?
La potencía de Perl a la hora de procesar grandes cantidades de datos lo hace
realmente popular a la hora de desarrollar aplicaciones del lado del servidor,
aprender Perl o Php es básico a la hora de desarrollar aplicaciones Web.
3.9.6.C
Es un lenguaje de "medio nivel" pero con numerosas características de bajo
nivel. Dispone de las estructuras típicas de los lenguajes de alto nivel pero, a
su vez, dispone de construcciones del lenguaje que permiten un control a muy
bajo nivel.
¿Por qué deberías aprenderlo?
Aprender C es básico mientras aprendes C estas aprendiendo conceptos
básicos de lenguajes cómo Java o C#, además no sólo es mas sencillo que
estos últimos sino que comporten gran parte de su sintaxis.
3.9.8.ASP
Active Server Pages (ASP) y ASP.NET es un intento de Microsoft para
introducirse en el mercado del desarrollo Web, y viene a ser como su estandar
para su servidor Web, IIS. ASP ha sido atacado por la comunidad open source
desde que este apareció, y dan numerosas razones para ello:
El propietario, una única plataforma, la lentitud...
ASP ha sido implementado en otras plataformas y que cuando esta
funcionando bajo su servidor predeterminado IIS es relativamente rápido.
¿Por qué deberías aprenderlo?
Simplemente porqué en algunas ocasiones no tienes otra opción debido a la
popularidad que ha alcanzado.
CAPÍTULO IV: LENGUAJES DE BAJO, MEDIO Y ALTO
NIVEL
4.1. INTRODUCCIÓN
4.2.2.ENSAMBLADOR.
El lenguaje ensamblador constituye el primer intento de sustitución del
lenguaje máquina por uno más cercano al usado por los humanos. Este
acercamiento a las personas se plasma en las siguientes aportaciones:
Uso de una notación simbólica o nemotécnica para representar los
códigos de operación
direccionamiento simbólico
Se permite el uso de comentarios entre las líneas de instrucciones,
haciendo posible la redacción de programas más legibles.
1. Velocidad
El proceso de traducción que realizan los intérpretes, implica un
proceso de cómputo adicional al que el programador quiere realizar.
Por ello, nos encontraremos con que un intérprete es siempre más
lento que realizar la misma acción en Lenguaje Ensamblador,
simplemente porque tiene el costo adicional de estar traduciendo el
programa, cada vez que lo ejecutamos.
De ahí nacieron los compiladores, que son mucho más rápidos que
los intérpretes, pues hacen la traducción una vez y dejan el código
objeto, que ya es Lenguaje de Máquina, y se puede ejecutar muy
rápidamente. Aunque el proceso de traducción es más complejo y
costoso que el de ensamblar un programa, normalmente podemos
despreciarlo, contra las ventajas de codificar el programa más
rápidamente.
Sin embargo, la mayor parte de las veces, el código generado por un
compilador es menos eficiente que el código equivalente que un
programador escribiría. La razón es que el compilador no tiene tanta
inteligencia, y requiere ser capaz de crear código genérico, que sirva
tanto para un programa como para otro; en cambio, un programador
humano puede aprovechar las características específicas del
problema, reduciendo la generalidad pero al mismo tiempo, no
desperdicia ninguna instrucción, no hace ningún proceso que no sea
necesario.
Para darnos una idea, en una PC, y suponiendo que todos son buenos
programadores, un programa para ordenar una lista tardará cerca de
20 veces más en Visual Basic (un intérprete), y 2 veces más en C (un
compilador), que el equivalente en Ensamblador.
Por ello, cuando es crítica la velocidad del programa, Ensamblador se
vuelve un candidato lógico como lenguaje.
Ahora bien, esto no es un absoluto; un programa bien hecho en C
puede ser muchas veces más rápido que un programa mal hecho en
Ensamblador; sigue siendo sumamente importante la elección
apropiada de algoritmos y estructuras de datos. Por ello, se
recomienda buscar optimizar primero estos aspectos, en el lenguaje
que se desee, y solamente usar Ensamblador cuando se requiere más
optimización y no se puede lograr por estos medios.
2. Eficiencia de tamaño
Por las mismas razones que vimos en el aspecto de velocidad, los
compiladores e intérpretes generan más código máquina del
necesario; por ello, el programa ejecutable crece. Así, cuando es
importante reducir el tamaño del ejecutable, mejorando el uso de la
memoria y teniendo también beneficios en velocidad, puede convenir
usar el lenguaje Ensamblador. Entre los programas que es crítico el
uso mínimo de memoria, tenemos a los virus y manejadores de
dispositivos (drivers). Muchos de ellos, por supuesto, están escritos
en lenguaje Ensamblador.
3. Flexibilidad
Las razones anteriores son cuestión de grado: podemos hacer las
cosas en otro lenguaje, pero queremos hacerlas más eficientemente.
Pero todos los lenguajes de alto nivel tienen limitantes en el control;
al hacer abstracciones, limitan su propia capacidad. Es decir, existen
tareas que la máquina puede hacer, pero que un lenguaje de alto nivel
no permite. Por ejemplo, en Visual Basic no es posible cambiar la
resolución del monitor a medio programa; es una limitante, impuesta
por la abstracción del GUI Windows. En cambio, en ensamblador es
sumamente sencillo, pues tenemos el acceso directo al hardware del
monitor.
Resumiendo, la flexibilidad consiste en reconocer el hecho de que
Todo lo que puede hacerse con una máquina, puede hacerse en el
lenguaje ensamblador de esta máquina; los lenguajes de alto nivel
tienen en una u otra forma limitante para explotar al máximo los
recursos de la máquina.
B) DESVENTAJAS DEL LENGUAJE ENSAMBLADOR
Por otro lado, al ser un lenguaje más primitivo, el Ensamblador tiene
ciertas desventajas respecto a los lenguajes de alto nivel:
1. Tiempo de programación.
Al ser de bajo nivel, el Lenguaje Ensamblador requiere más
instrucciones para realizar el mismo proceso, en comparación con un
lenguaje de alto nivel. Por otro lado, requiere de más cuidado por
parte del programador, pues es propenso a que los errores de lógica
se reflejen más fuertemente en la ejecución.
Por todo esto, es más lento el desarrollo de programas comparables
en Lenguaje Ensamblador que en un lenguaje de alto nivel, pues el
programador goza de una menor abstracción.
4. Falta de portabilidad
Como ya se mencionó, existe un lenguaje ensamblador para cada
máquina; por ello, evidentemente no es una selección apropiada de
lenguaje cuando deseamos codificar en una máquina y luego llevar
los programas a otros sistemas operativos o modelos de
computadoras. Si bien esto es un problema general a todos los
lenguajes, es mucho más notorio en ensamblador: yo puedo reutilizar
un 90% o más del código que desarrollo en "C", en una PC, al
llevarlo a una RS/6000 con UNIX, y lo mismo si después lo llevo a
una Macintosh, siempre y cuando esté bien hecho y siga los
estándares de "C", y los principios de la programación estructurada.
En cambio, si escribimos el programa en Ensamblador de la PC, por
bien que lo desarrollemos y muchos estándares que sigamos,
tendremos prácticamente que reescribir el 100 % del código al
llevarlo a UNIX, y otra vez lo mismo al llevarlo a MAC.
Compilación
No hay que repetir la conversión de la misma
1.
Ventajas instrucción a código maquina cada vez que aparece
2. Los programas corren muy rápido
1. Perdida de claridad e información sobre el programa
Desventajas
2. Dificultad en localizar la fuente exacta de error
Ejemplo ADA, C , C++, FORTRAN, Pascal
Interpretación
No hay pérdida de claridad ni de información sobre
1.
un programa ni sobre donde están los errores.
Ventajas
No hay que decodificar código que no se va a
2.
ejecutar
Los programas corren mucho mas lento – se paga el
Desventajas 1.
coste de decodificar cada instrucción
Ejemplo HTML, Lisp , ML, Perl, Postscrip, Smalltalk
Estos dos casos representan los dos extremos porque existe también lo que se
llama la compilación parcial, que es una mezcla de los dos enfoques, donde se
compila el lenguaje de alto nivel a un lenguaje intermedio (más cerca de las
estructuras presentes en el código máquina que las del código fuente) y luego se
interpreta este lenguaje al ejecutar el programa.
Como puede imaginarse, esta técnica combina las ventajas y desventajas de los
dos enfoques anteriores. Un ejemplo de esta combinación existe en el lenguaje de
programación Java y su entorno.
Como se puede ver en los cuadros superiores, tanto los programas compilados
parcialmente a un lenguaje intermedio (como Java) como los programas escritos
en lenguajes de alto nivel que se interpretan (como Lisp) requieren una MV para
interpretar el programa. La principal ventaja del lenguaje intermedio en este caso
es su proximidad al nivel del código máquina, en el sentido de que supone menos
trabajo a la hora de ejecutarlo y, por lo tanto, los programas corren más
rápidamente que los puramente interpretados.
Además del papel de los lenguajes intermedios en la compilación parcial, se puede
destacar su papel en la compilación estándar. Como ejemplo se puede considerar
C como lenguaje intermedio para un lenguaje compilado nuevo. Si el autor de un
nuevo lenguaje decide utilizar C, por ejemplo, como su lenguaje intermedio, sólo
tendrá que implementar una MV para convertir el código fuente de su lenguaje a
C, ahorrando mucho trabajo.
Ejemplo: (X+Y)* Z
Notación Prefija: [ + XY ] * Z
* + XYZ
Notación Postfija: [ XY +] * Z
XY + Z *
Convención Tradicional.
↑ Exponenciación
* , / Multiplicación, división
+ , - Suma, resta
5.5. REPRESENTACIÓN DE ÁRBOL PARA EXPRESIONES
Ejemplo. INFIJA : b * b - 4 * a * c
PREFIJA : - * b b * * 4 a c
POSTFIJA : b b * 4 a * c * -
Árbol de sintaxis abstracta
7*7- 4*2*3
5.6.2.EVALUACIÓN MEDIANTE UNA PILA
Ejemplo:
Expresión Pila auxiliar Comentario
77*42*3*- 7 Introduzca 7
7*42*3*- 77 Introduzca 7
*42*3*- 49 Multiplique
42*3*- 49 4 Introduzca 4
2*3*- 49 4 2 Introduzca 2
*3*- 49 8 Multiplique
3*- 49 8 3 Introduzca 3
*- 49 24 Multiplique
- 25 Reste
Descripción Solución:
1. La expresión se va leyendo carácter a carácter, los operandos pasan
directamente a formar parte de la expresión en postfija.
2. Los operadores se meten en la pila siempre que esta este vacía o bien
siempre que tengan mayor prioridad que el operador cima de la pila (o
bien igual si es la máxima prioridad). Si la prioridad es menor o igual
se saca el elemento cima de la pila y se vuelve a hacer la comparación
con el elemento cima.
3. Los paréntesis izquierdos siempre se meten en la pila con la mínima
prioridad. Cuando se lee un paréntesis derecho, hay que sacar todos los
operadores de la pila pasando a formar parte de la expresión postfija,
hasta llegar a un paréntesis izquierdo, el cual se elimina, ya que los
paréntesis no forman parte de la expresión postfija.
4. El algoritmo termina cuando no hay más caracteres en la expresión y
la pila esta vacía.
Ejemplo:
A*(B+ C-(D/E ↑ F)-G )–H
C) Algoritmo (pseudocódigo)
1. Obtener caracteres de la expresión y repetir pasos 2 al 4 para cada
carácter
2. Si es operando, pasarlo a la expresión postfija
3. Si es operador
3.1. Si la pila esta vacía meterlo en la pila. Repetir a partir de 1.
3.2. Si la pila no esta vacía.
Si la prioridad del operador leído es mayor que la prioridad del
operador cima de la pila, meterlo en la pila
y repetir a partir de 1.
Si la prioridad del operador es menor o igual que la prioridad del
operador de la cima, sacar de la cima y
pasarlo a la expresión postfija, volver a 3.
4. Si es paréntesis derecho
4.1. Sacar cima de la pila y pasarlo a la expresión postfija
4.2. Si nueva cima es paréntesis izquierdo, suprimir elemento cima.
4.3. Si cima no es paréntesis izquierdo, volver a 4.1
4.4. Volver a partir de 1.
5. Si quedan elementos en la pila, pasarlos a la expresión postfija
6. Fin del algoritmo.