Lenguajes de programación En realidad los primeros ordenadores eran máquinas "desnudas", es decir, sin ningún software. La programación se hacía directamente en binario. Para resolver esta incomodidad, se introdujo pronto la posibilidad de utilizar nombres simbólicos en los programas, que pasaron a escribirse en un lenguaje muy próximo al de la máquina, pero que ya permitía escribir con símbolos decimales y nombres simbólicos (palabras) para identificar las posiciones de memoria. Nacían así los lenguajes de programación. Así pues, un lenguaje de programación es cualquier notación para la descripción de algoritmos y estructuras de datos, aunque suele exigirse que esté implementado en un ordenador. El lenguaje más simple es el denominado lenguaje máquina, que utiliza como representación de datos e instrucciones los elementos de representación del sistema binario, y utiliza como única estructura de datos la palabra de memoria. En este caso, las únicas operaciones que pueden aparecer en los algoritmos son las instrucciones que entiende la unidad central de proceso. Ya se ha señalado lo engorroso que sería programar en tales condiciones o, lo que es lo mismo, con este lenguaje de programación tan limitado. Por ello se han diseñado nuevos lenguajes, más próximos al programador pero que exigen la presencia de un programa traductor (procesador de lenguaje) para convertir a lenguaje máquina el programa escrito en el lenguaje simbólico de alto nivel (que suele denominarse programa fuente). Procesadores de lenguaje Con ello surgieron los programas procesadores de lenguaje, que traducían directamente del lenguaje simbólico utilizado por el programador al lenguaje binario de la unidad central de proceso. En realidad, un procesador de lenguaje es un "metaprograma" que actúa sobre datos que, a su vez, son programas escritos en un lenguaje simbólico de alto nivel. Si nos basamos en la manera como convierten un lenguaje de programación en lenguaje máquina, podemos establecer dos grandes familias de procesadores de lenguaje: los intérpretes y los traductores. Intérpretes Un programa intérprete es un procesador de lenguaje que convierte cada instrucción o expresión del programa redactado en lenguaje simbólico en la instrucción o instrucciones correspondientes del lenguaje máquina, y las ejecuta inmediatamente antes de proceder a la traducción de la instrucción siguiente del lenguaje simbólico fuente. De hecho, un intérprete es realmente un simulador software que, para el programador, simula la existencia de una máquina con una unidad central de proceso capaz de entender el lenguaje simbólico de programación utilizado. Traductores Un programa traductor es un procesador de lenguaje que traduce a lenguaje máquina todas las expresiones o instrucciones de que consta el programa fuente, con lo cual genera un nuevo programa escrito en lenguaje máquina o, a veces, en un lenguaje intermedio, que recibe el nombre de programa objeto. Podemos distinguir varios tipos de traductores de lenguajes:
Los ensambladores (assembler) son los que actúan sobre lenguajes de programación muy cercanos al lenguaje máquina. La traducción se hace símbolo a símbolo, efectuando una correspondencia directa entre las instrucciones del lenguaje ensamblador y las instrucciones del lenguaje máquina. En todo caso, se trata de lenguajes muy directamente ligados a cada máquina y por ello se dice que son de muy bajo nivel, porque su uso es muy poco generalizable. Los compiladores son los auténticos traductores, alejados ya de la servidumbre de la máquina. Los lenguajes que se compilan pueden tener sentencias, frases y expresiones complejas totalmente al margen de la concreción de un lenguaje máquina y un procesador en particular. De ahí su generalidad, ya que, para ejecutarlos en una máquina u otra, basta con disponer del programa traductor (el compilador), que traduce el lenguaje simbólico, bien a lenguaje máquina directamente o bien al ensamblador concreto de la máquina en cuestión. Suelen corresponder a los lenguajes llamados de alto nivel, por el mayor grado de simbolismo posible. Otro nivel diferente lo presentan los procesadores de lenguaje de tipo traductor, que están orientados a generar un nuevo programa a partir de unas especificaciones en un lenguaje de especificación de problemas. Podríamos decir que cuando se utiliza un generador de programas lo que se hace es acogerse a unos módulos algorítmicos preexistentes, que el propio generador decidirá si tiene que utilizar o no, de acuerdo con unas especificaciones de lo que se supone que debe hacer el programa, de cómo son los datos de entrada y de cómo tienen que ser los datos de salida. Características de los lenguajes de programación Sintaxis y semántica La sintaxis de un lenguaje de programación es la forma en que se escribe un programa utilizando este lenguaje. La constituyen las reglas que indican cómo se escriben las frases (sentencias), las declaraciones de las estructuras de datos y el resto de las construcciones sintácticas del lenguaje. La semántica de un lenguaje es el significado que se da a las diferentes construcciones sintácticas. Así, una misma estructura de datos simple, el número entero por ejemplo, tiene distintas representaciones sintácticas en diferentes lenguajes aunque su significado (su semántica) pueda ser el mismo: un número entero. Entre los elementos sintácticos fundamentales de un lenguaje de programación encontramos:
Un buen lenguaje debe a su sintaxis el grado de legibilidad y la facilidad para ser utilizado en la escritura de programas, así como la sencillez o la complejidad del compilador que finalmente tiene que traducirlo a lenguaje máquina. El objetivo principal es conseguir un lenguaje que no sea ambiguo; esta necesidad de eliminar la ambigüedad es la razón fundamental por la cual el lenguaje que utilizamos los seres humanos no es adecuado para programar un ordenador. Universalidad de los lenguajes La característica de universalidad de un lenguaje de programación merece una mención aparte. Se dice que un lenguaje es universal cuando su estructura sintáctica y semántica permite expresar cualquier programa posible. En realidad, los lenguajes se consideran más o menos universales; la adecuación específica de un lenguaje a un tipo concreto de problemas (gestión, cálculo científico, etc.) va en detrimento de su universalidad, entendiéndola como una adecuación cualitativa. En todo caso, la presencia de varios lenguajes con vocación de universalidad plantea el problema de si un programa escrito en un lenguaje de programación A puede también ser escrito en un lenguaje de programación B, y si estos programas son totalmente equivalentes. Dos programas serán equivalentes si, respondiendo al mismo conjunto de datos de entrada, proporcionan el mismo conjunto de datos de salida; es decir, si implementan de manera totalmente igual la función algorítmica del programa. Este problema se estudia con la ayuda de máquinas abstractas o autómatas como la conocida máquina de Turing, descrita por Alan Turing en 1936. El efecto práctico del estudio de estos lenguajes universales simples y de las máquinas abstractas lleva a la conclusión de que cualquier lenguaje de programación que pueda ser razonablemente utilizado en la práctica es, sin ninguna duda, un lenguaje universal, siempre que se considere que no hay límites en la capacidad de almacenaje ni en el tiempo de ejecución. De hecho, las diferencias entre los lenguajes de programación no son diferencias cuantitativas sino, esencialmente, diferencias de tipo cualitativo, que indican el grado de elegancia, facilidad y efectividad con que pueden utilizarse para abordar determinados tipos de problemas. Por esta razón, se habla de lenguajes para programación científica (como el Fortran) y de otros claramente orientados a la gestión (como el Cobol) y a otras aplicaciones, aunque esta caracterización es solamente cualitativa. En realidad, la visión actual de la programación está orientada según la idea de la universalidad de los diferentes lenguajes. Una vez conocidas de manera general las estructuras de datos y las estructuras algorítmicas posibles, la programación de un problema tiene que realizarse siempre siguiendo el proceso de diseño descendente por refinamientos sucesivos, típico de la programación estructurada. Esto hace que se supere la reducida visión de antes, en que se "programaba" en un lenguaje determinado, para pasar a la visión actual según la cual se diseña un programa que posteriormente puede codificarse en un lenguaje u otro. Las facilidades que dé un determinado lenguaje en la etapa de codificación son simplemente de tipo cualitativo. La máquina de Turing La máquina de Turing dispone de una sola estructura de datos: un vector lineal denominado cinta (tape), con un único carácter en cada uno de sus elementos. Hay una sola variable, que es el cabezal de lectura (read head), que apunta a un elemento del vector cinta. El programa que controla la máquina de Turing puede realizar muy pocas operaciones:
A pesar de esta simplicidad, la máquina de Turing resulta muy útil, aunque entre sus operaciones primitivas no dispone de la posibilidad de efectuar operaciones aritméticas. Puede demostrarse que cualquier algoritmo puede expresarse como un programa para la máquina de Turing y por ello puede afirmarse que el lenguaje con que se programa la máquina de Turing es un lenguaje universal. Se trataría del lenguaje universal más simple. |