Diseño de Lenguajes
Fundamentos de Computación II
Diseño de los lenguajes de programación
Objetivo
Adquirir habilidad de apreciar y evaluar lenguajes, identificando sus límites y
posibilidades.
Habilidad para elegir, para diseñar, implementar o utilizar un lenguaje.
Enfatizar la abstracción.
Los lenguajes de Programación son el corazón de la Ciencia de la Computación.
Son herramientas que se usa comunicarnos con las máquinas sino también con las
personas.
“el valor de un lenguaje se debe juzgar según la forma en que afecta la producción de
Software y a la facilidad con la que puede integrarse a otras herramientas”
¿Por qué estudiar Lenguajes?
Aumentar la capacidad para producir software.
– Conocer profundamente las características de los lenguajes permite aprovechar su
potencia y evitar errores, minimizando esfuerzo
Mejora la habilidad para desarrollar mejores algoritmos.
– Aprendemos a elaborar los algoritmos más eficientes en concordancia con el
lenguaje que estamos utilizando.
– Ejemplo: Recursión
Mejora el uso de su lenguaje de programación preferido.
– Si se entiende como se implementa cada característica se mejora la capacidad para
escribir programas eficientes.
– Ejemplo: Orientación a Objetos (Herencia)
Incrementar el vocabulario
El lenguaje
equivale a comunicación.
es una ayuda y una limitación al pensamiento.
permite expresar y estructurar el pensamiento
Incrementamos la capacidad para expresar ideas
Incrementa el conocimiento de construcciones posibles.
Libera la mente para poder pensar en términos del problema y no de determinado
lenguaje. Ejemplo: Tratar de resolver todo en termino de las capacidades de C.
Permite elegir mejor el lenguaje a utilizar para cada tarea.
Conocer las fortalezas y debilidades de los diferentes lenguajes nos permite saber que
problema podemos resolver más fácilmente con cada uno y por lo tanto se reduce el
esfuerzo de codificación.
calculo ADA – Fortran
inteligencia artificial Prolog - ML
Hace más fácil aprender nuevos lenguajes de programación.
Un conocimiento de la estructura de los lenguajes de programación reduce
considerablemente la curva de aprendizaje de un lenguaje nuevo.
Hace más fácil el diseño e implementación de lenguajes.
El programador puede convertirse en diseñador o implementador de un lenguaje.
O no necesariamente nuevos lenguajes de programación, pero cuando
programamos muchas veces necesitamos crear códigos de comunicación.
Ejemplo: Manejo de formas en Internet
1. Eficiencia
Eficiencia: capacidad para el aprovechamiento óptimo de los recursos que emplea.
La eficiencia se organiza en tres principios: eficiencia de traducción, de implementación y
de programación.
Los lenguajes OO en un principio tenian la reputación de ser ineficaces debido en gran
medida a que los primeros lenguajes,como Smalltalk, eran interpretados y no compilados.
La existencia de compiladores permite a los desarrolladores ganar rapidez.
Actualmente, usando un buen lenguaje orientado a objetos como C++, Java, etc. junto con
las librerías apropiadas para la realización de un programa, puede que se ejecute más
rápidamente que el mismo programa compilado con un lenguaje procedural.
Eficiencia de compilación
Compilación rápida, usando un compilador de tamaño reducido.
La verificación de errores puede ser un problema grave de eficiencia, ya que verificar
la existencia de un error en tiempo de compilación puede hacer ineficiente al
compilador, y la generación de código para verificar el error durante la ejecución podría
hacer que el código objetivo resulte ineficiente
Ignorar esa verificación, viola otro principio de diseño, la confiabilidad (el
aseguramiento de que un programa no se comportará en formas no esperadas durante
su ejecución
Eficiencia de código
El diseño del lenguaje debe ser tal que un compilador pueda generar un código ejecutable
eficiente . Optimizabilidad.
Eficiencia de la programación
Facilidad para expresar procesos y estructuras complejas (capacidad de expresión
del lenguaje).
Facilidad con la que se puede correlacionar el diseño en la cabeza del programador
con el código real.
Lo conciso de la sintaxis y evitar detalles innecesarios, como son las declaraciones
de variables, a menudo también se consideran factores importantes en este tipo de
eficiencia.
Rapidez y facilidad en la escritura de programas en el lenguaje.
Lo conciso de la sintaxis y evitar detalles innecesarios, como son las declaraciones
de variables, a menudo también se consideran factores importantes en este tipo de
eficiencia.
Facilidad para expresar procesos y estructuras complejas (capacidad de expresión del
lenguaje).
Facilidad con la que se puede correlacionar el diseño en la cabeza del programador
con el código real.
Es un principio que se refiere al comportamiento de las características del lenguaje.
Generalidad
Ortogonalidad
Uniformidad
Si se no se cumple una de ellas, el lenguaje ya se puede clasificar como irregular.
Las irregularidades durante el diseño de un lenguaje son por causa de las prioridades de
diseño, como por ejemplo, las irregularidades cometidas en C++ para conseguir su
compatibilidad con C.
2. Regularidad
La comprensión del procedimiento seguido en la formación de un conjunto de reglas
gramaticales que permiten identificar y nombrar sin ambigüedad acciones y secuencias
ordenadas de acciones sobre el contexto específico de un problema en particular.
Pocas restricciones en el uso de constructores
Menos sorpresas en la forma en que se comportan las características del lenguaje
2.A Generalidad
Un lenguaje logra tener generalidad eliminando casos especiales en la disponibilidad y uso de
los constructores y combinando constructores íntimamente relacionados en uno solo más
general.
Algunos ejemplos de violación de este concepto podrían ser la carencia de:
Variables de procedimientos (Pascal).
Anidación de funciones / procedimientos (C).
Arreglos de longitud variable (Pascal).
Extensión y creación de operadores (==, +, …) hacia nuevos tipos de datos (C) .
Pascal no tiene arrays de longitud variable con lo que carecen de generalidad.
C y Ada sí tienen arrays de longitud variable.
En C no se pueden comparar dos arrays utilizando el operador de igualdad ==,
deben ser comparados elemento a elemento, por lo que es el operador de
igualdad el que carece de generalidad.
Muchos lenguajes no tienen mecanismos para extender el uso de operadores
predefinidos, sin embargo Haskell sí, e incluso permite que se creen nuevos
operadores por parte del usuario (a diferencia de Ada y C++), luego sus
operadores han logrado una generalidad completa.
2.B Ortogonalidad
Los constructores del lenguaje no deben comportarse de manera diferente en contextos
diferentes, por lo que las restricciones que dependen del contexto son no ortogonales,
en tanto que las que se aplican independientemente del contexto son no generales.
Los constructores del lenguaje se pueden combinar en cualquier forma significativa y que
la interacción entre los constructores o el contexto del uso no debe generar restricción o
comportamientos inesperados.
Los constructores no pueden comportarse de manera diferente en contextos diferentes.
2.C Uniformidad
Las cosas similares deben verse de forma similar y tener significados similares, y a la inversa, las
cosas diferentes se tienen que ver de forma diferente.
Las no uniformidades son de dos tipos:
Cosas similares no parecen ser o no se comportan de manera similar.
Cosas no similares, parecen ser o se comportan de manera similar.
Ejemplo de violación de este concepto puede ser:
En C++ es necesario un punto y coma después de la definición de clase, pero está
prohibidodespués de la definición de una función.
class A{ ... };
int f() { ... }
Si una no regularidad no puede justificarse de forma razonable entonces se puede decir que es
un error de diseño
Ayudan a definir un buen diseño o a elegir el mejor lenguaje para un escenario o unas
tareas concretas
Principios adicionales
Simplicidad
La simplicidad parecía ser un principio fácil de lograr, pero en la práctica es asombrosamente
difícil.
No debe confundirse simplicidad con regularidad, ejemplo: Algol68 es uno de los lenguajes
más regulares, pero no es simple.
Tampoco tener muy pocos constructores básicos es simplicidad, ejemplo: LISP y Prolog
tienen unos cuantos constructores básicos, pero dependen de un ambiente de ejecución
complejo.
Un LP demasiado simple puede hacer que la tarea de utilizarlo resulte más compleja
“Todo debería hacerse tan simple como sea posible, pero no más simple”. Albert
Einstein
La sobresimplicidad puede hacerse que el lenguaje sea difícil de utilizar, carente de
expresividad, legibilidad o seguridad y sujeto a demasiadas restricciones.
Es la facilidad con que un lenguaje puede expresar procesos y estructuras complejas
Uno de los primeros adelantos en expresividad fue la adición de la recursión en los
LP (LISP y Algol60)
Es una de las razones del aumento de popularidad de los lenguajes orientados a
objetos.
Expresividad
Extensibilidad
Principio que indica que debería de haber algún mecanismo general que permita al
usuario añadir nuevas características al lenguaje.
Podría significar simplemente el añadir:
Nuevos tipos al lenguaje.
Nuevas funciones a una biblioteca.
Nuevas palabras claves.
La simplicidad sin extensibilidad, prácticamente tiene garantizado el fracaso del
lenguaje.
Un diseño de lenguaje debería dar la posibilidad de que un programador pudiera programar
de una forma útil empleando un conocimiento mínimo del lenguaje, por lo que el diseño de
un lenguaje debería promover la capacidad de definir subconjuntos del lenguaje
Esto resulta útil de dos maneras:
No es necesario que el programador aprenda todo el lenguaje para utilizarlo con
efectividad.
Un escritor de compilador podría elegir implementar únicamente un subconjunto en el
caso de que la implementación de todo lenguaje resultara demasiado costosa e
innecesaria.
Capacidad de restricción
A veces conocida como claridad.
Es la existencia de una definición precisa para un lenguaje, de tal manera que el
comportamiento de los programas pueda ser predecible
Pasos para lograrlo:
Publicación de un manual o informe del lenguaje por parte del diseñador.
Adopción de un estándar emitido por una organización nacional o internacional de
estándares:
*ANSI.
*ISO.
Precisión
Este principio se basa en minimizar los errores de programación y permitir que dichos
errores sean detectados e informados.
La seguridad está íntimamente relacionada con la confiabilidad y con la precisión.
Este es el principio que condujo a los diseñadores del lenguaje a introducir los tipos,
la verificación de tipos y las declaraciones de variables en los lenguajes de
programación.
Seguridad
Portabilidad Facilidad para ser ejecutados en distintos entornos físicos o lógicos
Verificabiildad Capacidad para soportar procedimientos de pruebas, ensayaos y test.
Exactitud Nivel de precisión que alcanzan los resultados obtenidos
Robustez Capacidad para funcionar correctamente en situaciones extremas Integridad
Nivel de protección frente a procesos que traten de alterarlo
Facilidad de uso comodidad y claridad en la interacción con el usuario
Compatibilidad facilidad de poder ser aplicados en conjunción con otros programas
Domains numero = integer predicates nondeterm factorial(numero, numero). clauses factorial(0,1). factorial(N,F) :- N > 0, N1 = N - 1, factorial(N1,F1), F = N*F1. goal write("Ingrese el valor que desea hallar el factorial :"), readint(N), factorial(N,F).
Al ejecutar:
Ingrese el valor que desea hallar el factorial: 5 N=5, F=120 1 solution
En este programa ejecutado en Visual Prolog no se declaran las variables: N, F, N1, F1 en una aparente eficiencia de este Lenguaje de Programación, no es necesario definir variables.
Programa en Visual Prolog, que halla el factorial de un número
/* compara 2 cadenas de igual longitud*/ #include <stdio.h> #include <conio.h> #include <string.h> #include <iostream.h> #define MAXIMO 80 void main() { clrscr(); char cad[MAXIMO], cad1[MAXIMO]; int sw=0, len, i=1; cout<<"\ningrese cadena 1:";gets(cad); cout<<"\ningrese cadena 2:";gets(cad1); cout<<"\nla cadena 1 es:";puts(cad); cout<<"\nla cadena 2 es:"; puts(cad1); /* compara*/ len=strlen(cad); do{ i++; else sw=1; i=len; }while(i<len ); if (sw==1) cout<<"\nlas cadenas no son iguales"; else cout<<"\nlas cadenas son iguales"; }
Al ejecutar:
Ingrese cadena1: hola Ingrese cadena2: peru La cadena1 es hola La cadena2 es peru Las cadenas no son iguales
Observamos, en este programa ejecutado en C++, que no es posible comparar 2 arreglos con el operador =, sino que hay que hacerlo elemento por elemento, por lo tanto no cumple con el principio de Generalidad para el caso de arreglos.
Programa en C++, que compara si son iguales 2 cadenas de igual longitud
/* Cuenta el numero de palabras en una cadena*/ #include <iostream.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> #include <string.h> #define MAXIMO 80 int palabras(char cad[80]); void main() { clrscr(); char s[MAXIMO]; cout<<"\ningrese cadena :"; gets(s); cout<<"\nla cadena es:"; puts(s); cout<<"\nel numero de palabras es :"<<palabras(s); getch(); } int palabras(char cad[80]) { int len,p=0,sw=0,i=0; char ch = ' '; len=strlen(cad); do{ while(cad[i] != ch) {i++; sw=1; } if(sw==1) {p++; sw=0;} while(cad[i]== ch) i++; }while(i<len ); return p; }
Al ejecutar:
Ingrese cadena1: hola mundo La cadena es hola mundo El número de palabras es: 2
En este programa ejecutado en C++, observamos que sólo se pueden enviar arreglos dentro de una función, pero estos no pueden ser devueltos por la función, lo cual es un comportamiento inesperado. Ortogonalidad
Programa en C++, que cuenta el número de palabras en una cadena
// crear la clase triangulo con constructores y destructores #include<iostream.h> #include<string.h> #include<conio.h> #include<math.h> class triangulo{ private: int lado1; int lado2; int lado3; public: triangulo(int lad1, int lad2, int lad3); ~triangulo(void); void mostrar(); }; triangulo::triangulo(int lad1, int lad2, int lad3) { lado1=lad1; lado2=lad2; lado3=lad3; } triangulo::~triangulo(void) { cout<<"\n\ndestructor"<<lado1<<","<<lado2<<","<<lado3<<endl; } void triangulo::mostrar() { float area,p,sp; p=lado1+lado2+lado3; sp=p/2; area=sqrt(sp*(sp-lado1)*(sp-lado2)*(sp-lado3)); cout<<"\n Area:"<<area; cout<<"\n Perimetro:"<<p; }; void main() {triangulo x(6,6,6); x.mostrar(); getch(); }
Al ejecutar:
Área: 15.5885 Perímetro: 18
Observamos que en C++, no se cumple el principio de uniformidad cuando definimos clases y funciones, ya que para clases es necesario el punto y coma y para definir funciones no. Class A {… }; // se necesita Int f ( ) { … } // no se necesita ;
Crear la clase triangulo con constructores y destructores, tendiendo como datos los lados, y un método que calcule el área y el perímetro, en C++.
package lenguaje1; import biblioteca.*; public class cuadrado { int lado; public cuadrado(int l){ lado = l; } public void calcular(){ int area; int perimetro; area=lado*lado; perimetro=4*lado; LE.mostrarInformacion("El área del cuadrado es: "+area); LE.mostrarInformacion("El perimetro del cuadrado es: "+perimetro); } public static void main (String args[]){ int l; l=LE.leerInt("Ingrese lado del cuadrado: "); cuadrado obj = new cuadrado(l); obj.calcular(); } }
Al ejecutar:
Ingrese lado del cuadrado: 6 Aceptar Cancelar
El área del cuadrado es: 36 Aceptar
El perímetro del cuadrado es: 24 Aceptar
En este programa escrito en Java, vemos que este Lenguaje permite adicionar nuevas bibliotecas, en este caso hemos incorporado la biblioteca, llamada igualmente, que nos permite el manejo sencillo de la entrada de datos (con cuadros de texto) y hacer el programa más simple que si usamos solo la biblioteca JDK. import biblioteca.*; Este crecimiento es conocido como extensibilidad porque permite agregar características a un Lenguaje.
Realizar un programa en java, que cree la clase Cuadrado, teniendo como dato el lado y un método que calcule el área y el perímetro.