Introducción a los lenguajes de programación

Table of Contents

1 Bibliografía

El tema está basado en los siguientes capítulos. Os recomendamos que los estudieis y, si os interesa y os queda tiempo, que exploréis en los enlaces que hemos dejado en los apuntes para ampliar información.

  • Introducción capítulo 1 Abelson & Sussman (Building Abstractions with Procedures)
  • Capítulo 1.2 Scott (The Programming Language Spectrum)
  • Capítulo 1.3 Scott (Why Study Programming Languages)
  • Capítulo 1.4 Scott (Compilation and Interpretation)

2 Historia de los lenguajes de programación

Al comienzo sólo existía el código máquina. Al principio de la historia de los computadores no existían los lenguajes de programación. Los computadores se programaban en el lenguaje que entendía la unidad de procesamiento: el código máquina.

El UNIVAC fue el primer computador comercial. La primera unidad se vendió en 1951. Se vendieron varias decenas de instalaciones en organismos oficiales, ejército y empresas importantes. Se daban cursos de programación y apareció la figura del programador (manual de 1959 de programación del UNIVAC). Eran más importantes las horas de funcionamiento del computador que las horas de trabajo del programador.

Ordenador UNIVAC

(Imagen tomada de http://www.computer-history.info/Page4.dir/pages/Univac.dir/index.html)

Los primeros lenguajes de programación aparecieron a finales de la década de los 50.

El FORTRAN fue el primero de ellos. Lo desarrolló un equipo de IBM dirigido por John W. Backus. A finales de 1953, Backus lanzó la propuesta de desarrollar una alternativa más productiva al lenguaje de programación ensamblador: escribir los programas en un lenguaje más expresivo y traducirlos a ensamblador (FORmula TRANslating system). El primer manual de FORTRAN apareció en octubre de 1956 y el primer compilador se terminó en abril de 1956.

Cita de John Backus (tomada de la entrada de la Wikipedia sobre FORTRAN):

Much of my work has come from being lazy. I didn't like writing programs, and so, when I was working on the IBM 701, writing programs for computing missile trajectories, I started work on a programming system to make it easier to write programs.

Desde 1954 hasta la actualidad se han documentado más de 2.500 lenguajes de programación (consultar en The Language List). Entre 1952 y 1972, la primera época de los lenguajes de programación, se desarrollaron alrededor de 200 lenguajes, de los que una decena fueron realmente siginificativos y tuvieron influencia en el desarrollo de lenguajes posteriores.

Una lista parcial de algunos de los lenguajes de programación más importantes, junto con su año de creación:

1957 FORTRAN1975 Pascal1990 Haskell
1958 ALGOL1975 Scheme1991 Python
1960 Lisp1975 Modula1993 Ruby
1960 COBOL1983 Smalltalk-801995 Java
1962 APL1983 Objective-C1995 PHP
1962 SIMULA1983 Ada2000 C#
1964 BASIC1986 C++2003 Scala
1964 PL/I1986 Eiffel2003 Groovy
1970 Prolog1987 Perl2009 Go
1972 C1988 Tcl/Tk

Un diagrama de los lenguajes más importantes y sus dependencias:

Árbol de lenguajes de programación

(Figura tomada de http://people.mandriva.com/%7Eprigaux/language-study/diagram.html)

Éric Lévénez ha hecho el esfuerzo de construir un árbol genealógico de los lenguajes de programación, que indica la fecha en la que cada lenguaje fue creado y la influencia que ha tenido en los lenguajes posteriores. Consultar el documento PDF y la web de Éric Lévénez.

img/Babel.gif

La Torre de Babel, representa el gran número de lenguajes de programación. Esta imagen se usó en la portada de Programming Languages: History and Fundamentals, de J. E. Sauel, 1969, Prentice Hall.

Al principio, los lenguajes se diseñaban únicamente para poder ejecutar los programas eficientemente. Los ordenadores, carísimos, eran el recurso crítico, y los programadores eran bastante baratos. Un lenguaje de alto nivel tenía que ser competitivo con la ejecución de un código en ensamblador. A mediados de los años 60, cuando se habían diseñado FORTRAN, COBOL, LISP y ALGOL, John Backus (creador de FORTRAN) se dio cuenta que la programación estaba cambiando. Las máquinas eran menos caras, el coste de la programación estaba aumentando, aparecía la necesidad de mover o migrar los programas de unas estaciones a otras, y surgía la necesidad de llevar un buen mantenimiento de los programas resultantes. El objetivo de un lenguaje de alto nivel se transformó no sólo en en ejecutar los programas eficientemente, sino también en facilitar el desarrollo de programas para resolver problemas en determinadas áreas de aplicación.

La tecnología de los ordenadores maduró entre 1960 y 1970 y los lenguajes se centraron en resolver problemas específicos de un dominio. Los programas científicos generalmente se implementaban en FORTRAN, las aplicaciones para los negocios en COBOL, las aplicaciones militares en JOVIAL, las de inteligencia artificial en LISP y las militares internas en Ada. Al igual que sucede con los lenguajes naturales, los lenguajes de programación a veces dejan de usarse. ALGOL no se utiliza desde los años 60, se reemplazó por Pascal, el cual se reemplazó a su vez por C++ y Java. COBOL, que se utilizaba para las aplicaciones mercantiles, se sustituyó también por C++.

Los primeros lenguajes que todavía se usan tienen revisiones periódicas para reflejar influencias de otras áreas de computación. Lenguajes como Java, C++ y ML reflejan una gran experiencia obtenida en el diseño y uso de los cientos de lenguajes antiguos. Algunas de esas influencias son:

  • Recursos del ordenador: la evolución de los ordenadores desde los años 50, junto con los modernos sistemas operativos, han influenciado las características de los lenguajes de alto nivel.
  • Aplicaciones: el uso de los ordenadores se ha extendido rápidamente desde las aplicaciones militares, científicas y de negocios a los juegos, los ordenadores personales, internet y cualquier aplicación cotidiana.
  • Métodos de programación: el diseño de los lenguajes tiene que reflejar los buenos métodos para implementar programas grandes y complejos.
  • Estudios teóricos<: Gracias a la investigación durante más de 50 años en el diseño e implementación de lenguajes, se conocen los puntos fuertes y débiles de las características de los mismos, por lo que influye en el diseño de los nuevos lenguajes.
  • Estandarización: cada vez más existe la necesidad de que los programas sean portables de unos sistemas a otros.</li>

La siguiente tabla (extraida del libro Programming Languages. Design and Implementation, de Terrence W. Pratt y Marvin V. Zelkowitz) muestra una pequeña lista de los lenguajes y las influencias que fueron importante a finales del siglo 20.

img/lenguajes-influencias.png

A pesar de la enorme lista de lenguajes de programación que han ido apareciendo, la mayor parte de los programadores sólo utilizan unos pocos lenguajes. Además, los programadores a menudo trabajan en empresas o instalaciones donde se trabaja con un único lenguaje en particular, como Java, C o Ada. Entonces, os preguntarés por qué es importante el estudio de diferentes lenguajes o paradigmas cuando al final sólo trabajaréis con unos pocos. Hay muchas razones que justifican su estudio, entre las cuales las más importantes son:

  • Mejora el uso del lenguaje de programación: si se conoce cómo están implementadas las características en un lenguaje de programación, se mejorará la habilidad para escribir programas eficientes. Por ejemplo, si se conoce cómo crear y manipular listas o cadenas en un lenguaje, por ejemplo Scheme, utilizando recursión se conseguirá construir programas más eficientes.
  • Incrementa el vocabulario de los elementos de programación.
  • Permite una mejor elección del lenguaje de programación: El conocimiento de diversos lenguajes de programación facilitan la elección del lenguaje más adecuado para un proyecto determinado.
  • Mejora la habilidad para desarrollar programas efectivos y eficientes: Muchos lenguajes proporcionan características que, cuando se utilizan correctamente, aportan muchos beneficios a la programación pero, cuando se hace un uso incorrecto, pueden ocasionar un gran coste computacional. El ejemplo típico es la <em>recursión</em>, que permite una implementación elegante y eficiente de funciones. Pero cuando no se sabe utilizar, puede ocasionar un aumento exponencial del tiempo de ejecución.
  • Facilita el aprendizaje de un nuevo lenguaje de programación: Cuando se conocen las estructuras, técnicas de implementación y construcciones de un lenguaje, es mucho más sencillo aprender un nuevo lenguaje de programación que tenga estructuras similares.
  • Facilita el diseño de nuevos lenguajes de programación: Es posible que en un futuro tengais que diseñar vosotros un lenguaje que se adapte a vuestras necesidades. Cuantos más lenguajes y paradigmas se conozcan, más sencillo resultará el diseño y la implementación.

La historia de los lenguajes de programación es <em>dinámica</em> y se encuentra en continua expansión. El gran número de lenguajes de programación que han aparecido desde los años 60 hasta la actualidad no son ni mucho menos los únicos que aparecerán en un futuro.

Por ejemplo, a continuación tenemos una lista extraida de freshmeat donde se puede ver el número de proyectos activos etiquetados con la etiqueta Libraries ordenados por lenguaje de programación (Febrero 2011):

Java (1436)J2ME (35)Groovy (12)
C (1310)Other (30)Visual Basic (11)
C++ (988)Unix Shell (30)ML (10)
PHP (857)Fortran (28)ASP (10)
Python (629)Haskell (23)Pascal (9)
Perl (404)Ada (23)HTML (8)
JavaScript (200)Other Scripting.. (19)Erlang (8)
Ruby (130)Lisp (17)Zope (8)
C# (88)Delphi (15)AJAX (8)
Tcl (86)Lua (14)XML (7)
SQL (67)Eiffel (14).NET (7)
Objective C (41)PL/SQL (13)Qt (6)
Scheme (37)OCaml (13)Emacs Lisp (6)
Assembly (36)Common Lisp (12)bash (5)

En la siguiente web Timeline of Programming Languages tenemos una lista de los lenguajes que han aparecido cronológicamente desde los años 50. Es interesante ver los que han aparecido desde el año 2000 al 2007 (21 lenguajes).

La evolución de los lenguajes de programación nunca se detiene. Continuamente surgen nuevos lenguajes que toman relevancia. Algunos lenguajes de programación son el trabajo de personas individuales con ganas de combinar distintas ideas y solucionar problemas y limitaciones que detectan en los lenguajes conocidos.

Por ejemplo, tenemos el caso de Ruby, un lenguaje de programación ideado en 1993 por un joven japonés llamado Yukihiro Matsumoto. Creó un lenguaje multi-paradigma, interpretado y muy expresivo que actualmente se utiliza tanto para desarrollar aplicaciones web como videojuegos.

img/yukihiromatsumoto.jpg

El propio lenguaje de programación Ruby nunca ha parado de evolucionar. La primera versión pública Ruby 0.95 se anunció en un grupo de noticias en Japón en diciembre de 1995. A continuación se lanzaron tres versiones más y se creó el grupo de noticias ruby-list. La versión Ruby 1.0 apareció en diciembre de 1996, la versión 1.3 en 1999 y la 1.8.7 en junio de 2008. La última versión que se ha lanzado Ruby 1.9.1 ha sido el 25 de Enero de 2009.

Yikihiro Matsumoto

Otro ejemplo es Scala, diseñado en 2003 por el profesor alemán Martin Oderski como una respuesta a los problemas de los lenguajes tradicionales imperativos para manejar la concurrencia. Está implementado sobre Java y corre en la Máquina Virtual Java.

Go

Un último ejemplo de lenguaje muy reciente es Go, el nuevo lenguaje de programación de Google. Una mezcla de C y Python que intenta conseguir un lenguaje de programación de sistemas muy eficiente, expresivo y también multiparadigma.

3 Elementos de los lenguajes de programación

Hemos visto el gran número de lenguajes de programación que se han desarrollado en la historia de la informática. A pesar de la gran variedad de lenguajes desarrollados, hay unos elementos comunes a todos ellos. ¿Cuáles son? ¿Qué hace que algo pueda ser considerado un lenguaje de programación?

Según la definición de la Encyclopedia of Computer Science 1 (Encyclopedia of Computer Science, 4th Edition, Wiley, 2000)

A programming language is a set of characters, rules for combining them, and rules specifying their effects when executed by a computer, which have the following four characteristics:

  1. It requires no knowledge of machine code on the part of the user
  2. It has machine independence
  3. Is translated into machine language
  4. Employs a notation that is closer to that of the specific problem being solved than is machine code

Según Abelson y Sussman, en el libro que vamos a seguir en nuestra asignatura (SICP, p. 1)

We are about to study the idea of a computational process. Computational processes are abstract beings that inhabit computers. As they evolve, processes manipulate other abstract things called data. The evolution of a process is directed by a pattern of rules called a program. […] The programs we use to conjure processes are like a sorcerer's spells. They are carefully composed from symbolic expressions in arcane and esoteric programming languages that prescribe the tasks we want our processes to perform.

Y después, en la página 4, añaden otra idea fundamental:

A powerful programming language is more than just a means for instructing a computer to perform tasks. The language also serves as a framework within which we organize our ideas about processes. Thus, when we describe a language, we should pay particular attention to the means that the language provides for combining simple ideas to form more complex ideas.

Así, entre las características de un lenguaje de programación podemos remarcar las siguientes:

  1. Define un proceso que se ejecuta en un computador
  2. Es de alto nivel, cercano a los problemas que se quieren resolver (abstracción)
  3. Permite construir nuevas abstracciones que se adapten al dominio que se programa

Para Abelson y Sussman, todos los lenguajes de progamación permiten combinar ideas simples en ideas más complejas mediante los siguientes tres mecanismos

  • Expresiones primitivas que representan las entidades más simples del lenguaje
  • Mecanismos de combinación con los que se construyen elementos compuestos a partir de elementos más simples
  • Mecanismos de abstracción con los que dar nombre a los elementos compuestos y manipularlos como unidades

Cuando se habla de elementos en el párrafo anterior nos estamos refiriendo tanto a datos como a programas.

Una breve lista de características comunes a todos los lenguajes de programación:

  • Tienen una sintaxis: un conjunto de reglas que definen qué expresiones de texto son correctas. Por ejemplo, en C todas las sentencias deben terminar en ';'
  • Los lenguajes de programación se ejecutan en un computador y tienen una determinada semántica que define cuál será el resultado de la ejecución de un programa.
  • Definen un conjunto de tipos de datos primitivos que representan los posibles valores que pueden devolver las expresiones del lenguaje.
  • Tienen mecanismos de abstracción para definir nuevos tipos de datos a partir de los primitivos o nuevas funciones y procedimientos.

Otra idea importante es que Los lenguajes de programación son para las personas.

Los lenguajes de programación deben ser precisos, deben poder traducirse sin ambiguedad en lenguaje máquina para que sean ejecutados por computadores. Pero deben ser utilizados (leídos, comentados, probados, etc.) por personas.

Es importante reflexionar sobre el campo de los lenguajes de programación. El estudio general de este campo nos ayuda a:

  • aprender nuevos lenguajes
  • identificar características conocidas en estos nuevos lenguajes
  • conocer sus posibilidades y sus límites
  • diseñar nuevas características cuando los lenguajes que usamos no las tienen
  • valorar y apreciar una de las herramientas fundamentales de los informáticos

4 Abstracción

El concepto de abstracción es fundamental en informática. Para escribir un programa que preste unos servicios es fundemental modelar el dominio (sistema de información de una universidad, sistema de sensores de una planta química, etc.) sobre el que va a trabajar. Para ello es necesario definir distintas abstracciones que nos permitan tratar sus elementos y comunicarnos correctamente con los usuarios que van a utilizar el programa.

Una abstracción agrupa un conjunto de elementos (datos y procedimientos) y le da un nombre. Por ejemplo, cuando hablamos del sistema de información de una universidad identificamos elementos como:

  • Estudiantes
  • Asignaturas
  • Matrícula
  • Expediente académico

Existen abstracciones propias de la computación, que se utilizan en múltiples dominios. Por ejemplo, abstracciones de datos como:

  • Listas
  • Árboles
  • Grafos
  • Tablas hash

También existen abstracciones que nos permiten tratar con dispositivos y ordenadores externos:

  • Fichero
  • Raster gráfico
  • Protocolo TCP/IP

Uno de los trabajos principales de un informático es la construcción de abstracciones que permitan ahorrar tiempo y esfuerzo a la hora de tratar con la complejidad del mundo real.

Tal y como dice Joel Spolsky en su blog Joel on Software

TCP is what computer scientists like to call an abstraction: a simplification of something much more complicated that is going on under the covers. As it turns out, a lot of computer programming consists of building abstractions. What is a string library? It's a way to pretend that computers can manipulate strings just as easily as they can manipulate numbers. What is a file system? It's a way to pretend that a hard drive isn't really a bunch of spinning magnetic platters that can store bits at certain locations, but rather a hierarchical system of folders-within-folders containing individual files that in turn consist of one or more strings of bytes.

Una misión fundamental de los lenguajes de programación es proporcionar herramientas que sirvan para construir estas abstracciones.

5 Paradigmas de programación

Existe una enorme variedad de lenguajes de programación, no sólo en cuanto a su sintaxis, sino también en cuanto a su comportamiento o semántica.

Hemos visto que cada año el número de lenguajes se incrementa, de forma que para los informáticos es prácticamente imposible conocer cada nuevo lenguaje que aparece. Pero eso no es un problema, ya que todos esos lenguajes tienen características comunes y se pueden agrupar en cuatro grandes grupos o modelos computacionales llamados paradigmas.

img/paradigmas.png

Imagen del libro Programming Languages. Design and Implementation de Terrence W. Pratt y Marvin V. Zelkowitz.

Todos los lenguajes pertenecen a uno de esos cuatro paradigmas. De forma que, si se conocen las características de los paradigmas de programación, es muy sencillo aprender a programar en un nuevo lenguaje, porque tendrá las características del paradigma de programación al que pertenezca.

El origen de la palabra paradigma entendida como un marco general en el que se desarrollan teorías científicas se encuentra en el trabajo de 1962 del filósofo e historiador de la ciencia Thomas S. Kuhn, La estructura de las revoluciones científicas. Esa palabra ha sido después adoptada por el mundo de la computación para definir un conjunto de ideas y principios comunes de grandes grupos de lenguajes de programación.

La definición de la palabra paradigma más cercana a lo que se quiere decir en la expresión paradigma de programación es la siguiente:

Un marco filosófico y teórico de una escuela o disciplina científica en el que se formulan teorías, leyes y generalizaciones y los experimentos realizados en soporte de ellas.

Un paradigma define un conjunto de reglas, patrones y estilos de programación que son usados por un grupo de lenguajes de programación.

Podemos distinguir cuatro grandes paradigmas de programación:

  • Paradigma funcional
  • Paradigma lógico
  • Paradigma imperativo o procedural
  • Paradigma orientado a objetos

Una reflexión importante es que la separación entre los paradigmas y los lenguajes no es estricta. Existen ideas comunes a distintos paradigmas, así como lenguajes de programación que soportan más de un paradigma. Por ejemplo, el paradigma funcional y lógico comparten características declarativas, mientras que el paradigma orientado a objetos y procedural tienen características imperativas.

Otros paradigmas de programación menos comunes:

  • Paradigmas de programación paralela y concurrente
  • Paradigmas basados en restricciones
  • Paradigmas visuales

Se puede encontrar más información sobre distintos paradigmas de programación en la Wikipedia.

Veamos algunas características importantes de los paradigmas más importantes.

5.1 Paradigma funcional

Resumen de las características principales:

  • La computación se realiza mediante la evaluación de expresiones
  • Definición de funciones
  • Funciones como datos primitivos
  • Valores sin efectos laterales, no existe la asignación
  • Programación declarativa

Lenguajes: LISP, Scheme, Haskell, Scala, Clojure.

Ejemplo de código (LISP):

(define (factorial x)
   (if (= x 0)
      1
      (* x (factorial (- x 1)))))

(factorial 8)
40320
(factorial 30)
265252859812191058636308480000000

5.2 Paradigma lógico

Características:

  • Definición de reglas
  • Unificación como elemento de computación
  • Programación declarativa

Lenguajes: Prolog, Mercury, Oz.

Ejemplo de código (Prolog):

padrede('juan', 'maria'). % juan es padre de maria
padrede('pablo', 'juan'). % pablo es padre de juan
padrede('pablo', 'marcela').
padrede('carlos', 'debora').

hijode(A,B) :- padrede(B,A).
abuelode(A,B) :-  padrede(A,C), padrede(C,B).
hermanode(A,B) :- padrede(C,A) , padrede(C,B), A \== B.        

familiarde(A,B) :- padrede(A,B).
familiarde(A,B) :- hijode(A,B). 
familiarde(A,B) :- hermanode(A,B).

?- hermanode('juan', 'marcela').
yes
?- hermanode('carlos', 'juan').
no
?- abuelode('pablo', 'maria').
yes
?- abuelode('maria', 'pablo').
no

5.3 Paradigma imperativo

Los lenguajes de programación que complen el paradigma imperativo se caracterizan por tener un estado implícito que es modificado mediante instrucciones o comandos del lenguaje. Como resultado, estos lenguajes tienen una noción de secuenciación de los comandos para permitir un control preciso y determinista del estado.

Características:

  • Definición de procedimientos
  • Definición de tipos de datos
  • Chequeo de tipos en tiempo de compilación
  • Cambio de estado de variables
  • Pasos de ejecución de un proceso
    type 
       tDimension = 1..100;
       eMatriz(f,c: tDimension) = array [1..f,1..c] of real;
       
       tRango = record
          f,c: tDimension value 1;
       end;
       
       tpMatriz = ^eMatriz;
    
    
    procedure EscribirMatriz(var m: tpMatriz);
    var filas,col : integer;
    begin
       for filas := 1 to m^.f do begin
          for col := 1 to m^.c do
             write(m^[filas,col]:7:2);
          writeln(resultado);
          writeln(resultado)
         end;    
    end;
    

5.4 Paradigma orientado a objetos

Características:

  • Definición de clases y herencia
  • Objetos como abstracción de datos y procedimientos
  • Polimorfismo y chequeo de tipos en tiempo de ejecución

Ejemplo (Java):

public class Bicicleta {
    public int marcha;
    public int velocidad;
        
    public Bicicleta(int velocidadInicial, int marchaInicial) {
        marcha = marchaInicial;
        velocidad = velocidadInicial;
    }
        
    public void setMarcha(int nuevoValor) {
        marcha = nuevoValor;
    }
        
    public void frenar(int decremento) {
        velocidad -= decremento;
    }
        
    public void acelerar(int incremento) {
        velocidad += incremento;
    }
}

public class MountainBike extends Bicicleta {
    public int alturaSillin;

    public MountainBike(int alturaInicial, int velocidadInicial, int marchaInicial) {
        super(velocidadInicial, marchaInicial);
        alturaSillin = alturaInicial;
    }   
        
    public void setAltura(int nuevoValor) {
        alturaSillin = nuevoValor;
    }   
}

public class Excursion {
   
        public static void main(String[] args) {
           MountainBike miBicicleta = new MoutainBike(10,10,3);
           miBicicleta.acelerar(10);
           miBicicleta.setMarcha(4);
           miBicicleta.frenar(10);
        }
}

6 Compiladores e intérpretes

Existe una gran variedad de estrategias para conseguir que un programa se ejecute en un computador. Todas se basan en los "meta-programas" (compiladores, intérpretes, etc.) cuyos datos de entrada son el código fuente de otros programas.

Estos meta-programas procesan otros programas y realizan múltiples tareas.

6.1 Compilación

La siguiente figura muestra el proceso de generación y ejecución de un programa compilado.

Proceso de compilación

Ejemplos: C, C++

  • Diferentes momentos en la vida de un programa: tiempo de compilación y tiempo de ejecución
  • Mayor eficiencia

6.2 Interpretación

Proceso de interpretación

Ejemplos: BASIC, LISP, Scheme, Python, Ruby

  • No hay diferencia entre el tiempo de compilación y el tiempo de ejecución
  • Mayor flexibilidad: el código se puede construir y ejecutar "on the fly" (funciones lambda o clousures).

6.3 Ejecución en máquina virtual

Ejecución en una máquina virtual

Ejemplos: Java, Scala

6.4 Enlazado de rutinas y librerías

Enlazado de librerías

6.5 Preprocesamiento

Prepropceso

El preprocesador analiza el código y sustituye macros. Ejemplo: C, C++. Scala hace algo parecido con Java.


Lenguajes y Paradigmas de Programación
Curso 2010-2011
Departamento de Ciencia de la Computación e Inteligencia Artificial
Universidad de Alicante

Sitio web realizado con org-mode y el estilo CSS del proyecto Worg

Footnotes:

1 La Encyclopedia of Computer Science está disponible en la biblioteca politécnica con el identificador: POE R0/E/I/ENC/RAL

Validate XHTML 1.0