Lenguaje de programación

De MateWiki
Saltar a: navegación, buscar

Los lenguajes de programación permiten escribir programas para que se ejecuten en un ordenador. Existen muchos lenguajes de programación de diferentes tipos, que presentan ventajas e inconvenientes según el tipo de programa que se vaya a desarrollar. Los lenguajes de programación cumplen las mismas reglas que otros lenguajes naturales, aunque son lenguajes artificiales.

1 Tipos de lenguajes de programación

Algunos lenguajes son de propósito general, y otros son más adecuados para tareas especializadas (desarrollo de aplicaciones en la web, en el escritorio, en un móvil, cálculo científico). Existen muchos criterios para clasificar los lenguajes de programación, según las características de los lenguajes. Nosotros los clasificaremos en dos grandes grupos:

  • Lenguajes de bajo nivel
  • Lenguajes de alto nivel

Los ordenadores pueden entender directamente los lenguajes de bajo nivel, pero no los del alto nivel, que requieren de programas que traduzcan un programa de alto nivel al lenguaje que entiende la máquina.

El ordenador es capaz de entender un lenguaje denominado lenguaje máquina o lenguaje ensamblador. En los primeros tiempos de la Informática, los programadores escribían código directamente en este lenguaje máquina. Este tipo de lenguajes eran muy complejos de usar por parte de los programadores, pero muy sencillo de ejecutar por parte del ordenador. Por este motivo, los programas escritos en lenguaje máquina se suelen ejecutar con rapidez y sin requerir muchos recursos.

Por ejemplo, el siguiente programa en ensamblador escribe el mensaje Hola mundo[1] en la pantalla[2]:

; ---------------------------------------------
; Programa que imprime un string en la pantalla
; ---------------------------------------------
	.model small               ; modelo de memoria
	.stack                     ; segmento del stack
	.data                      ; segmento de datos
	Cadena1 DB 'Hola Mundo.$'  ; string a imprimir (finalizado en $)
	.code                      ; segmento del código
; ---------------------------------------------
; Inicio del programa
; ---------------------------------------------	
  programa:
		MOV AX, @data           ; carga en AX la dirección del segmento de datos
		MOV DS, AX              ; mueve la dirección al registro de segmento por medio de AX
		MOV DX, offset Cadena1  ; mueve a DX la dirección del string a imprimir
		MOV AH, 9               ; AH = código de la función del MS DOS para imprimir un string en la pantalla
		INT 21h                 ; llamada al MS DOS para imprimir un string en la pantalla
		INT 20h                 ; llamada al MS DOS para finalizar el programa
  end programa

Como podemos observar es un lenguaje muy complejo, que necesita muchas líneas de código para realizar una tarea muy sencilla. Conforme la tecnología de los ordenadores fue avanzando, también cambiaron los lenguajes de programación. Se crearon lenguajes de alto nivel. Un lenguaje de alto nivel es más fácil de entender por parte de los programadores, pero requiere ser traducido a lenguaje máquina para poder ser ejecutado por el ordenador. Por este motivo, los programas escritos en lenguajes de alto nivel suelen ser más lentos que los programas escritos en lenguajes de alto nivel. Aunque el tiempo de desarrollo requerido para escribir programas en lenguajes de alto nivel es mucho menor.

Por ejemplo, el programa equivalente al de arriba en lenguaje MATLAB, un lenguaje de muy alto nivel, sería

disp('Hola mundo');

Es decir, es tremendamente más sencillo de escribir y de entender, ya que necesita una única línea de código que es bastante auto-explicativa. Sin embargo, el programa MATLAB necesita un intérprete para que el ordenador entienda el programa. El intérprete consume muchos recursos de la máquina, por lo que se ejecutará de manera mucho más lenta que el programa anterior, y también necesitará más memoria RAM y un procesador más potente.

La siguiente tabla muestra un resumen de ventajas e inconvenientes de los lenguajes de alto y bajo nivel:

Lenguajes de alto nivel Lenguajes de bajo nivel
Ventajas Fáciles de entender y usar por los programadores Fáciles de ejecutar por el ordenador
Inconvenientes Requieren traducción a un lenguaje de bajo nivel Difíciles de entender por los programadores. Provoca problemas de mantenimiento de software
Tiempo de desarrollo Corto Muy largo
Tiempo de ejecución Lento Muy rápido

En la práctica se suelen emplear mucho más los lenguajes de alto nivel. Pero esto requiere usar un programa que haga de traductor a lenguaje máquina. Existen dos clases de estos programas traductores:

  • Compiladores
  • Intérpretes

Un compilador es un programa que toma el código fuente escrito en un lenguaje de alto nivel, y lo transforma en un archivo escrito en lenguaje de bajo nivel, que es ejecutable directamente por el sistema operativo. Una vez generado el programa por parte del compilador, ya no se necesita usar el compilador cada vez que se quiera ejecutar el programa, se puede ejecutar directamente. Ésta es la principal ventaja de los compiladores.

Un intérprete es un programa que lee el código fuente escrito en un lenguaje de alto nivel, y lo ejecuta línea a línea, directamente. Es decir, no genera ningún programa nuevo escrito en lenguaje máquina. Por este motivo, es necesario usar el intérprete cada vez que queramos ejecutar el programa de alto nivel. A pesar de esta desventaja respecto a los compiladores, es más fácil depurar y revisar la ejecución de un programa con un intérprete que con un compilador.

Los lenguajes por sí mismos no son ni interpretados ni compilados. El mismo lenguaje puede usarse con un compilador o con un intérprete. Existen algunas limitaciones que hacen que algunos lenguajes no se puedan compilar. Además, es cierto que algunos lenguajes son más sencillos de implementar usando un compilador, y otros usando un intérprete. Por eso en ocasiones se suele decir que un lenguaje es compilado y otro es interpretado. Siguiendo estos criterios, el lenguaje M de MATLAB y Octave UPM es interpretado, aunque por ejemplo, MATLAB incorpora un compilador que es capaz de transformar el código M a código ejecutable directamente.

En el cálculo científico los intérpretes presentan ventajas, ya que es más fácil explorar de manera interactiva cómo se comporta un programa, lo que facilita las tareas de desarrollo. Sin embargo, una vez que se termina el programa en lenguaje interpretado, se suelen usar herramientas que los transformen a un lenguaje compilado, como el lenguaje C, para posteriormente generar un fichero ejecutable que se ejecute más rápidamente que el programa interpretado. Ésta es una manera habitual de trabajar con MATLAB, por ejemplo.

2 Elementos principales de un lenguaje de programación

En un lenguaje de programación tenemos tres elementos principales:

  • Sintaxis
  • Semántica
  • Sistema de tipos

2.1 Sintaxis

La sintaxis define cómo deben escribirse los programas (qué símbolos hay que usar, en qué orden, etc). Por ejemplo, si queremos escribir un bucle que repita varias veces el mensaje Hola mundo, en lenguaje MATLAB hay que escribir

for k=1:10
 disp('Hola mundo');
end

En cambio, en lenguaje Python habría que escribir

for k in range(1,10):
  print("Hola mundo")

La sintaxis es uno de los elementos principales que hacen que un lenguaje de programación sea más sencillo de entender y cómodo de usar. En el caso particular de MATLAB y Octave UPM, la sintaxis del lenguaje está pensada para que sea sencilla de entender, y para que sea fácil trabajar con matrices que contengan muchos elementos. Las operaciones matriciales se pueden expresar con muy poco código, al contrario que con la mayoría de los lenguajes de programación.

2.2 Semántica

La semántica del lenguaje de programación asigna significado a las expresiones escritas del lenguaje. Por ejemplo, en el caso anterior, la semántica nos diría que tenemos una variable contador k, que tomará los valores de 1 a 10, de manera consecutiva, y que repetirá el comando que muestra Hola mundo tantas veces como valores diferentes tome la variable k. El proceso de asignar un significado a un determinado trozo de código lo realiza el intérprete o el compilador. Antes de asignar el significado, el intérprete también comprueba que el código es sintácticamente correcto, según las reglas del lenguaje.

Un ejemplo de regla sintáctica del lenguaje MATLAB es que todos los bucles que comienzan por for tienen que acabar con un end. Otro ejemplo de regla sería que las cadenas de texto, que comienzan por comilla simple, tienen siempre que terminar por comilla simple. Cuando un programa no es sintácticamente correcto, el intérprete de MATLAB/Octave UPM genera un error del tipo Syntax error. Por ejemplo, un error de sintaxis es usar corchetes para intentar referirnos a un elemento de un vector:

>> v = [1 4 1 2 1 4];
>> v[4]
parse error:
  syntax error
>>> v[4]
     ^

Pueden producir también errores de tipo semántico. En lenguaje MATLAB un error típico de esta clase es intentar obtener el elemento 0 de un vector. En este lenguaje, según la semántica, todos los vectores comienzan siempre por el elemento número 1. Por ejemplo:

>> v(0)
error: subscript indices must be either positive integers or logicals

El error es de tipo semántico porque no hemos usado un entero positivo o un valor lógico para direccionar en el vector.

Otro error de tipo semántico es intentar recuperar un elemento de un vector más allá del límite del vector. Siguiendo con los ejemplos anteriores, el vector tiene 6 elementos. Si intentamos recuperar el elemento 8 del vector ocurre lo siguiente:

>> v(8)
error: A(I): index out of bounds; value 8 out of bound 6

El código es sintácticamente correcto, pero semánticamente no es posible asignar un significado a esa línea de código, porque el vector tiene 6 elementos, y nosotros estamos pidiendo el octavo elemento.

2.3 Sistema de tipos

En un lenguaje de programación podemos asignar valores a las variables. Pero además de asignar un valor, estamos también asignando un tipo. Algunos lenguajes de programación no tienen tipos, pero la mayoría de los lenguajes asignan también un tipo a los valores.

2.3.1 Tipado estático y dinámico

El tipado puede ser de dos tipos

  • Estático
  • Dinámico

Cuando el tipado es estático, es necesario declarar las variables antes de usarlas, y decir de qué tipo van a ser. Si el tipado es dinámico, una variable puede tener diferentes tipos dentro del mismo programa. Normalmente, los lenguajes con tipado dinámico no requieren que las variables se declaren antes de usarlas.

El lenguaje M tiene tipos y es de tipado dinámico. Si no elegimos un tipo de manera explícita, las variables en el lenguaje M tendrá tipo double, que corresponde a números reales con doble precisión. Un valor real de doble precisión usa 64 bits de almacenamiento, y usa más decimales para representar el número real que otros tipos. Es posible también tener otros tipos dentro del lenguaje M:

  • Reales de precisión sencilla, que usan 32 bits de almacenamiento (float)
  • Enteros de diferentes tamaños (8, 16, 32 bits)
  • Enteros sin signo
  • Valores lógicos
  • Cadenas de texto

Existen también otros tipos más complejos (vectores, matrices, estructuras, etc.). En Octave UPM y MATLAB podemos ver los tipos de las variables usando el workspace, y el comando whos.

El sistema de tipos define también las reglas que permiten combinar valores de diferentes tipos. Por ejemplo, en un lenguaje con tipado estático, si se asigna un valor del tipo equivocado a una variable, se produce un error. Los lenguajes con tipado dinámico son más laxos y permisivos, pero aún así también tienen sus normas. Por ejemplo, en el lenguaje M no es posible realizar una operación aritmética entre enteros de diferentes tamaños:

>> a = int8(3)
a = 3
>> b = int16(2)
b = 2
>> a+b
error: binary operator '+' not implemented for 'int8 scalar' by 'int16 scalar' operations

Estas normas de tipado permiten a un compilador descartas posibles valores y operaciones sobre algunas variables, por lo que es posible generar un ejecutable optimizado, que se ejecute de manera más rápida. También ayudan a capturar errores en un programa, al cambiar de manera inadvertida de un tipo a otro. Sin embargo, no existen evidencias empíricas de que los programas escritos en lenguajes con tipado estático contengan menos errores que los escritos en lenguajes con tipado dinámico. La mayoría de lenguajes modernos (Python, Ruby) tienen tipado dinámico.

En el caso particular del lenguaje M, es muy habitual trabajar siempre con valores de tipo double, sin cambiar a otros tipos numéricos (float, enteros) y sin especificar de manera explícita. Las ganancias en tiempos de ejecución que pueden lograrse al cambiar de un tipo a otro son probablemente marginales, y salvo aplicaciones muy específicas, es mejor trabajar con los tipos por defecto sin realizar conversiones de tipos.

2.3.2 Tipado fuerte y débil

Otra clasificación habitual de los sistemas de tipos de los lenguajes de programación los divide en:

  • Tipado fuerte
  • Tipado débil

Los lenguajes con tipado débil son más laxos, ya que permiten usar un tipo diferente al requerido en determinadas ocasiones. El compilador o intérprete se encarga de realizar la conversión necesaria. El lenguaje M tiene tipado débil a la hora de trabajar con valores lógicos. En muchas situaciones donde se requiere un valor de tipo lógico, el lenguaje M admite valores de otros tipos. Por ejemplo, si es un valor numérico, si es igual a 0 asume que es equivalente a un valor lógico falso. Si tiene cualquier otro valor, es verdadero. Si lo que se encuentra es un vector, asume que es falso si el vector está vacío, y verdadero si no lo está. Veamos algunos ejemplos:

El valor numérico cero es siempre falso. Al ejecutar este código, que espera siempre un valor lógico a continuación de if
1 if 0
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

obtenemos el mensaje Es falso.

Si lo cambiamos a 18 (por poner un valor diferente a cero):
1 if 18
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

obtenemos el mensaje Es verdadero.

Lo mismo ocurre con los valores negativos:
1 if -7
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

obtenemos el mensaje Es verdadero.

Si probamos ahora con vectores, cualquier vector no vacío se entiende como verdadero:
1 if [1 2 3]
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

muestra el mensaje Es verdadero.

En cambio, el vector vacío se entiende como falso:
1 if []
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

muestra el mensaje Es falso.

¿Pero qué ocurre por ejemplo con un vector todo de zeros?
1 if [0 0 0]
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

muestra el mensaje Es falso.

Es decir, si el vector no está vacío, pero todos los elementos que contiene se pueden evaluar como falso, el intérprete le asigna el valor falso. Sin embargo, en ocasiones puede ser complicado adivinar qué va a entender el intérprete. Veamos el siguiente ejemplo. ¿Se entenderá como verdadero o como falso?
1 if [0 1 0]
2   disp('Es verdadero');
3 else
4   disp('Es falso');
5 end

El vector no está vacío, y uno de sus elementos no se entendería como falso. Sin embargo, se muestra el mensaje Es falso.

En los lenguajes con tipado fuerte todos los ejemplos de arriba habrían dado error, ya que no se puede usar un tipo diferente donde se requiere un valor lógico.

En el lenguaje M, confiar en la conversión automática del tipado débil puede conducir a errores sutiles, y por tanto difíciles de identificar y depurar. Es mucho mejor convertir de manera explícita cualquier valor a un valor lógico, usando operadores lógicos u otras soluciones.

3 Más información

Wikipedia en español contiene un excelente artículo sobre qué es un lenguaje de programación[3]. Hay también un listado con varias decenas de lenguajes de programación[4].

El primer programa que se suele desarrollar al aprender un lenguaje de programación es el Hola mundo[1], que muestra el mensaje Hola mundo en la pantalla. Suele ser un ejemplo muy sencillo y trivial, pero en algunos lenguajes, puede requerir varias líneas de código. Una buena manera de comparar lenguajes de programación es comprobar cómo se implementa el Hola mundo en ese lenguaje[5].

4 Referencias

  1. 1,0 1,1 Hola mundo (Wikipedia ES)
  2. Extraído de Lenguaje ensamblador (Wikipedia ES)
  3. Lenguaje de programación (Wikipedia ES)
  4. Lista de lenguajes de programación (Wikipedia ES)
  5. Ejemplos de implementación del "Hola mundo" (Wikipedia ES)