Você está na página 1de 47

3URJUDPDFLyQ

'SBODJTDP+BWJFS/PHVFSB0UFSP
%BOJFM3JFSB5FSSÏO
Diseño de la colección: Editorial UOC
Primera edición digital en lengua castellana: noviembre 2013

Este capítulo procede de la obra: Escaneando la informática (Joan Arnedo Moreno, Jordi Cabot Sagrera,
Isabel Guitart Hormigo, Francisco Javier Noguera Otero, Rafael Macau Nadal, Maria Jesús Marco Galindo,
Josep Maria Marco Simó, Joan Antoni Pastor Collado, Josep Prieto Blázquez, Daniel Riera Terrén, Jordi
Tubella Murgadas, José Ramón Rodríguez Bermúdez, M. Elena Rodríguez González, Ramon Segret Sala)
publicada en 2010, con ISBN: 978-84-9788-110-4

© )UDQFLVFR-DYLHU1RJXHUD2WHUR\'DQLHO5LHUD7HUUpQ, del texto.

© Editorial UOC, de esta edición


2EHUWD82&3XEOLVKLQJ6/8
Gran Via de les Corts Catalanes, 872, 3ª. planta, 08018 Barcelona
www.editorialuoc.com

Realización editorial: Oberta UOC Publishing, SL8


Código del capítulo: 978-84-9788-925-4/0

Ninguna parte de esta publicación, incluido el diseño general y la cubierta, puede ser copiada, reproducida,
almacenada o transmitida de ninguna forma, ni por ningún medio, sea éste eléctrico, químico, mecánico, óptico,
grabación, fotocopia, o cualquier otro, sin la previa autorización escrita de los titulares del copyright.
Autores

Francisco Javier Noguera Otero


Ingeniero en informática por la UAB es actualmente Chief SW Engineer en Techideas.com y ha par-
ticipado en múltiples proyectos de investigación financiados por la Unión Europea. Sus ámbitos de
especialización incluyen la comunicación distribuida P2P y los sistemas encastados en Linux.
Colabora con la UOC desde el año 2005, ejerciendo de consultor en las asignaturas Programación
Orientada a Objetos, Taller de Java y dirigiendo proyectos de Comercio Electrónico en Entornos de
software Libre.

Daniel Riera Terrén
Ingeniero y Doctor Ingeniero en Informática por la UAB, es director del Grado de Ingeniería
Informática de la UOC. Trabaja en esta universidad como profesor propio desde septiembre de 2005,
fue consultor de la asignatura de Estructura de la Información entre los años 2002 y 2006 y ha ejer-
cido como Director de Programa de la Ingeniería en Informática desde 2007. Adicionalmente, ha
sido profesor de Algoritmos y Programación, y de Lenguajes de programación en la UAB. Su ámbi-
to de investigación es la Optimización y Simulación con herramientas de Programación con
Restricciones.
© Editorial UOC 9 Índice

Índice

Capítulo IV. Programación,


Francisco Javier Noguera Otero y Daniel Riera Terrén .................................... 101

1. Introducción ........................................................................................ 101


2. ¿Qué es un programa? ¿Quién programa? .......................................... 102
3. Historia .................................................................................................. 103
3.1. La primera programadora ............................................................ 104
3.2. Prehistoria de la programación .................................................... 105
3.3. El primer programa en la memoria ............................................ 106
3.4. Generaciones ................................................................................ 108
4. Algorítmica ............................................................................................ 114
4.1. Las bases ...................................................................................... 115
4.2. Conceptos avanzados .................................................................. 120
5. Traducción de código fuente a código máquina .................................. 122
5.1. La compilación ............................................................................ 122
5.2. La interpretación .......................................................................... 123
5.3. Código intermedio ...................................................................... 123
6. Paradigmas de programación .............................................................. 124
6.1. Programación desestructurada o spaghetti .................................. 124
6.2. Programación procedimental (o imperativa) .............................. 125
6.3. Programación orientada a objetos .............................................. 125
6.4. Programación orientada a aspectos ............................................ 125
6.5. Otros paradigmas ........................................................................ 126
7. Lenguajes de programación .................................................................. 127
7.1. Los lenguajes más utilizados ........................................................ 128
8. Software libre ........................................................................................ 133
9. El mundo laboral .................................................................................. 134
10. Apéndice .............................................................................................. 135
10.1. Premios A. M. Turing .................................................................. 135
10.2. Ejemplo de código de programación ofuscada en C .................... 137
© Editorial UOC 101 Programación

Capítulo IV

Programación
Francisco Javier Noguera Otero y Daniel Riera Terrén

1. Introducción

Como ya se ha dicho anteriormente, un ordenador es una máquina capaz de


ejecutar algoritmos (programas). Esta definición une los conceptos de hardware
y software. Estos son conceptos antagónicos, pero complementarios, ya que no
tienen sentido el uno sin el otro.
La programación es la fase final –entendida como objetivo– del desarrollo del
software, que se concreta en la ingeniería del software (véase el capítulo 6). Para ase-
gurar la buena calidad de éste, antes se tienen que hacer unos buenos análisis y
diseño, y después, una buena depuración de errores y una implantación correcta,
así como un buen mantenimiento posterior. Todas estas fases se complementan y
giran en torno a la realización de un software que resuelva un problema concreto.
Antiguamente, cuando se detectaba un problema, se programaba directa-
mente la solución. Actualmente, debido a la complejidad creciente de los pro-
gramas, las necesidades referentes al funcionamiento correcto de éstos y la exi-
gencia de ciertos niveles de rendimiento, ha ido surgiendo toda una metodolo-
gía para asegurar el buen desarrollo del software.
Este texto se centra en lo que algunos autores se han atrevido a bautizar
como el arte de la programación. Para adentrarnos en este mundo, primeramen-
te, presentaremos la historia, que está directamente relacionada con la apari-
ción y la evolución de los diferentes lenguajes. Así, los lenguajes de programa-
ción que se van proponiendo tienen a menudo una relación directa con las
necesidades históricas del momento. La primera parte termina mostrando los
lenguajes que más se utilizan en la actualidad. Presentamos las razones por las
que han alcanzado su estatus actual y, por otra parte, sus debilidades. Por últi-
mo, se discuten los grandes fracasos del mundo de la programación.
© Editorial UOC 102 Escaneando la Informática

Aunque no se pretende profundizar en la programación en sí misma, sí que


se introducen las bases: componentes de un programa, manera de estructurar-
los y una cata de la manera de medir su calidad. Igualmente, se presentan los
paradigmas existentes y las características propias de cada uno, así como algu-
nos conceptos del software libre y las diferencias con el software propietario.

2. ¿Qué es un programa? ¿Quién programa?

Aunque quizás no tengamos una idea clara de a qué nos referimos cuando
hablamos de programar, sin ser conscientes de ello, a lo largo de nuestras vidas
lo hacemos bastante a menudo y de maneras diversas. Así, programamos el
vídeo para que se active a una hora determinada y grabe, programamos la hora
a la que tiene que sonar el despertador, e incluso, hacemos viajes programados.
¿Pero, realmente, tiene todo eso alguna relación con los programas de ordena-
dor?
Al contratar un viaje programado nos ofrecen la lista de los lugares adonde
iremos, qué día iremos, qué visitaremos y qué actividades realizaremos. Así, el
programa, en este caso, es una especificación detallada previa del acontecimien-
to. Aunque eso ya nos va dando ciertas pistas de lo que se considera un progra-
ma en el mundo de la informática, más cercanos aún son los otros dos ejem-
plos. Cuando programamos el despertador o el vídeo, le damos una orden –o un
conjunto de órdenes– que en un cierto momento del futuro ejecutará automá-
ticamente.
Reuniendo estas ideas, podemos acercarnos a la idea de programa, ahora ya
desde el punto de vista informático. Diremos, hoy por hoy, que un programa es
un conjunto de órdenes ordenadas que la máquina ejecutará para llevar a cabo
una tarea determinada.
Si buscamos la definición en el diccionario, vemos que es bastante parecida
a lo que acabamos de decir: “Conjunto unitario de instrucciones que permite a
un ordenador realizar diversas funciones, como el tratamiento de textos, el dise-
ño de gráficos, la resolución de problemas matemáticos, el tratamiento de ban-
cos de datos, etc.”. Así, las órdenes que damos al ordenador se llaman instruc-
ciones y la ejecución de éstas hace que el ordenador cumpla ciertos objetivos.
© Editorial UOC 103 Programación

Un programador, por tanto, será la persona capacitada para decirle al orde-


nador qué tiene que hacer y especificarle cómo lo tiene que hacer. Así, tiene que
conocer las instrucciones que se pueden utilizar para que el ordenador las
entienda y las pueda ejecutar. Para hacerlo, como veremos más adelante, tiene
que ser capaz, primero, de escribir un algoritmo (conjunto de pasos) para solu-
cionar el problema, y después traducirlo a un programa utilizando un lenguaje
de programación. El hecho de traducir un algoritmo a un programa (que deno-
minaremos código fuente o simplemente código) se llama programar o codifi-
car.
Aunque el trabajo de la programación, hoy día, es bastante cómodo –vistas
las facilidades que ofrecen las herramientas de apoyo–, es interesante ver cómo
ha ido evolucionando hasta llegar al punto donde nos encontramos.

3. Historia

A finales de 1976, los tripulantes de un flamante caza F16 de las fuerzas aére-
as de los Estados Unidos vieron sorprendidos que, en determinados momentos,
el avión giraba 180 grados y quedaba cabeza abajo. Después de analizar cuál
podía ser el problema, detectaron que provenía de un signo menos mal coloca-
do dentro del programa de navegación del aparato. Este error hacía que el apa-
rato girara cada vez que cruzaba el ecuador. Menos mal que estas primeras prue-
bas las hicieron sobre simulador, ya que si no el susto, sin duda, hubiera sido
mucho mayor.
Es curioso que un programa de algunos miles de líneas (y por lo tanto, de
varios millones de caracteres) pueda depender de un solo signo, pero la progra-
mación no es un ejercicio simple y cualquier programa, por sencillo que sea, es
susceptible de tener errores. Precisamente para evitar los errores y facilitar la
programación, los lenguajes y los paradigmas de programación han ido evolu-
cionando continuamente. Veamos, pues, cómo ha sido esta evolución.
Las diferencias entre los primeros programas y lenguajes, llamados de prime-
ra generación, y los que se utilizan actualmente es muy grande, no sólo por el
cambio de tipos de problemas que hay que resolver sino por las estrategias
seguidas para la simplificación, el diseño, la división en subproblemas, etc.
© Editorial UOC 104 Escaneando la Informática

Los lenguajes de programación evolucionan buscando la simplificación de la


tarea de programar y la reducción de la probabilidad de cometer errores.

3.1. La primera programadora

Charles Babbage (1719-1871) es considerado uno de los padres de la infor-


mática por su diseño de la “máquina analítica”. Ada Lovelace (1815--1852), hija
de Anabella y Lord Byron, es reconocida por los historiadores como la primera
persona que hizo un programa para ordenador. Fue su madre, Anabella, quien
introdujo a Ada en las matemáticas, quien, tras conocer a Charles Babbage, tra-
dujo y amplió una descripción de su máquina analítica.

Ada Lovelace (1815-1852): la primera programadora

Los “números de Bernoulli” son una secuencia de números racionales con


unas ciertas propiedades complejas que los relacionan. Babbage nunca pudo
completar la construcción de ninguno de sus diseños, principalmente por la
falta de tecnología y herramientas de precisión de la época. Sin embargo, el tra-
bajo que Ada llevó a cabo con este modelo le ha hecho ganarse el título de pri-
mera programadora de computadores del mundo. El primer programa que hizo
calculaba números de Bernoulli. El nombre del lenguaje de programación “Ada”
se escogió para rendir homenaje a esta programadora.
© Editorial UOC 105 Programación

3.2. Prehistoria de la programación

Después de los intentos y los consiguientes fracasos de Babbage para acabar


su máquina analítica, no hubo mucho interés en la construcción de ordenado-
res hasta la Segunda Guerra Mundial. Las máquinas que se construían entonces
eran gigantescas y tenían decenas de miles de válvulas de vacío. La programa-
ción se hacía construyendo cableados particulares para controlar las funciones
básicas del aparato. De hecho, se desconocían, propiamente dichos, los lengua-
jes de programación y la programación en sí. Hoy día, cuando un programa
tiene un error, se dice que tiene un “bug” (en castellano, bicho). Aunque nor-
malmente se relaciona este término con el hecho de que los antiguos ordena-
dores fallaban algunas veces porque los bichos quedaban atrapados en los
relays, parece que el concepto es anterior. Thomas Edison (1847-1931) ya habla
de bugs en sus diseños, refiriéndose a errores o dificultades.

Personas cargando programas en el ordenador UNIVAC 120

La forma de trabajar era la siguiente: el “programador" pedía tanda para uti-


lizar la máquina durante un cierto intervalo de tiempo. Cuando le tocaba, iba a
la habitación donde estaba el ordenador, insertaba su tablero de conexiones y
empezaba a rezar para que ninguna de las válvulas de vacío se fundiera duran-
te la ejecución. Evidentemente, si había algún error, no se tenía ninguna pista
de dónde se había equivocado.
© Editorial UOC 106 Escaneando la Informática

A finales de los cincuenta aparecieron las primeras tarjetas perforadas, que


significaron una mejora importante a la hora de “leer” los programas. Cada tar-
jeta contenía una instrucción y era importante que el bloque de tarjetas no se
cayera porque después volver a ordenarlas podía significar una odisea. El orde-
nador era capaz de ir leyendo los agujeros mecánicamente y así traducirlos a
instrucciones.

Tarjeta perforada en blanco (Imagen bajo licencia GFDL)

3.3. El primer programa en la memoria

Hasta aquel momento, para ejecutar un programa entero, lo que se hacía era
ir cargando una tarjeta por instrucción. Así, hasta que no se había acabado la
ejecución de la actual, no se retiraba y se introducía la siguiente.
Las siglas ENIAC significan Electronic Numerical Integrator And Computer. Los
primero programas que se utilizaron en el ENIAC estaban relacionados con el
diseño de la bomba de hidrógeno. Los aclamados como primeros ordenadores,
como por ejemplo el ENIAC, no tenían algunas de las características que hoy
día se consideran indispensables en un ordenador. Eran máquinas bastante rígi-
das. En cambio, en la Universidad de Manchester, se crearon una serie de
máquinas que incluían tanto la posibilidad de almacenar programas en la
memoria como de introducir el programa mediante un teclado en vez de recon-
figurar circuitos (tarea que podía durar días).
© Editorial UOC 107 Programación

El tubo Williams fue desarrollado por Freddie Williams (1911-1977) y Tom


Kilburn (1921-2001). El almacenamiento de programas se consiguió gracias a la
introducción de un tipo de memoria llamado “tubo Williams”. Era un tubo de
rayos catódicos, cuyo fósforo guardaba la información y se iba refrescando para
que no se perdiera. En diciembre de 1947 ya se podían guardar 2.048 bits en un
tubo de 6 pulgadas.
Con esta memoria se construyó una máquina llamada SSEM (Small Scale
Experimental Machine), también conocida con el nombre de Baby. La informa-
ción para programar el Baby era la siguiente:
• Constaba de un par de tubos auxiliares para guardar un acumulador A1 y
otro para la instrucción actual PI (Present Instruction) y su dirección (posi-
ción) en la memoria CI (Control Instruction).
• Las instrucciones tenían reservados 12 bits para la dirección de memoria, 3
para la instrucción y había 16 sin usar (véase la imagen de abajo).

Formato de las instrucciones del Baby

• Sólo por curiosidad, a continuación podemos ver las siete instrucciones


(por eso se codifica usando 3 bits) que se podían utilizar para hacer un pro-
grama:
– 000 Hace que la instrucción actual apunte a la dirección almacenada
en S (CI=S).
– 001 Resta en el acumulador el contenido de S (A=A-S).
– 010 Carga el acumulador con el contenido de la dirección S cambian-
do de signo (A=-S).
– 011 Si A es negativo, se salta la instrucción siguiente (si A<0,CI=CI+1).
– 100 Incrementa el contador de instrucción en la cantidad almacena-
da en S (CI=CI+S).

1. Un acumulador es un sitio de la memoria del ordenador que se utiliza de manera auxiliar para
hacer operaciones intermedias aparte de la memoria principal (un tubo Williams de 32x32).
© Editorial UOC 108 Escaneando la Informática

– 101 Se repite la 010 (A=-S).


– 110 Carga en la dirección S el contenido del acumulador (S=A).
– 111 Interrumpe el programa.

El primer programa fue escrito por el propio Tom Kilburn y fue ejecutado el
día 21 de junio de 1948. Este programa calculaba el factor máximo de un núme-
ro. En la imagen siguiente podemos ver el “diseño” a mano del primer progra-
ma.

Anotaciones a mano del primer programa que se hizo para el Baby

3.4. Generaciones

A lo largo de los años, se han ido clasificando los lenguajes en generaciones


según la proximidad al lenguaje natural –utilizado por los humanos– y otras
características que permiten agruparlos.
© Editorial UOC 109 Programación

3.4.1. Primera generación: Código máquina

Los lenguajes de primera generación, también llamados lenguajes máquina,


se caracterizan por utilizar ceros (0) y unos (1) para describir los programas.
Estos, en sus inicios, eran introducidos en el ordenador mediante interruptores
del tablero frontal del aparato.
Ejemplo de línea de código máquina:

10110000 01100001 Esta línea contiene una instrucción que mueve un valor (61 en hexadecimal)
en el registro2 del procesador al. Como se puede ver, no es muy comprensi-
ble desde el punto de vista humano (de hecho, es realmente misteriosa).

La ventaja principal de este tipo de lenguajes es que utilizan instrucciones


que pueden ser ejecutadas directamente por el procesador y, por lo tanto, son
muy rápidos. Por otra parte, son mucho más complejos de aprender, utilizar y,
sobre todo, depurar para buscar errores. Aparte, también encontramos que la
portabilidad de estos lenguajes es prácticamente nula: para reutilizar un progra-
ma de un ordenador en otro hay que rescribirlo completamente ya que es posi-
ble que los procesadores acepten lenguajes máquina diferentes o incluso que
tengan arquitecturas diferentes.
La gran mayoría de los lenguajes que fueron surgiendo posteriormente, al
final y de forma transparente para el programador, acaban traduciendo de una
manera u otra el código a código máquina. Eso se hace para superar las dificul-
tades enunciadas anteriormente.
“Hello World” es un programa que presenta por pantalla el mismo mensaje.
Se utiliza en los manuales introductorios como la mayoría de los lenguajes por
su sencillez. La primera vez que se utilizó fue en el año 1974 en un memorán-
dum interno de Brian Kernighan para Bell Laboratories (“Programming in C: A
Tutorial”). Una aplicación que sólo mostrara por pantalla la frase “Hello World”
en código máquina tendría el código que se muestra a continuación. Este ejem-
plo está escrito para ensamblador de un procesador Intel x86 con DOS y utili-
zando TASM. Hay que advertir que está escrito en hexadecimal, ya que en bina-
rio ocupa cuatro veces más.

2. Un registro es una zona de memoria del procesador donde se guarda información.


© Editorial UOC 110 Escaneando la Informática

4D5A2F00 02000100 20000200 FFFF0300 10000000 00000000


3E000000 0100FB71 6A720000 00000000 00000000 00000000
00000000 00000000 00000000 00000100 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 B802008E D8BA0000 B409CD21 B8004CCD
21000000 00000000 00000000 00000000 48656C6C 6F2C2077
6F726C64 210D24

Como hemos visto, comprender el código máquina es realmente complejo.


Aun así, en la película The Matrix, vemos que Cypher, Morpheus, Neo y algún
otro tripulante del Nebuchadnezzar son capaces de ver la realidad directamente
en el código (de hecho, en este caso, el código de Matrix no es binario, o hexa-
decimal, sino formado por símbolos parecidos a un alfabeto oriental). Queda
claro que son grandes programadores.

3.4.2. Segunda generación: Lenguaje ensamblador

Para evitar tener que utilizar el alfabeto de la máquina (ceros “0” y unos “1”),
que está muy lejos de lo que entendemos nosotros, se crearon los llamados len-
© Editorial UOC 111 Programación

guajes ensamblador. Éstos cartografían directamente cada instrucción en el con-


junto de ceros y unos correspondientes. Eso se hace mediante una herramienta
llamada ensamblador (assembler).
La arquitectura de un ordenador se refiere normalmente a la estructura inter-
na del procesador. Los lenguajes ensamblador son totalmente dependientes de
la familia del procesador y de su arquitectura, igual que el código máquina (por
ejemplo, un programa hecho para Intel no sirve para AMD o Motorola, ya que
son diferentes fabricantes de procesadores).
Ejemplo de línea de código ensamblador:

mov al, 061h Esta línea contiene la instrucción mov, que mueve un byte (61 en hexadecimal) en el
registro al. Esta manera de escribir código es más comprensible que la que hemos
visto anteriormente.

Aunque claramente la introducción de los lenguajes ensamblador implicaba


una mejora importante en la legibilidad del programa, apareció una nueva
necesidad entre los programadores: poder programar pensando más en cómo
solucionar el problema desde el punto de vista humano que en el funciona-
miento interno del ordenador. De ahí surgen los conceptos de lenguajes de pro-
gramación de bajo y de alto nivel. Cuanto más de alto nivel se considera un len-
guaje, más cerca se sitúa del lenguaje natural y de la manera humana de razo-
nar.
Las principales aplicaciones de estos lenguajes son los kernels (los núcleos de
los sistemas operativos), los drivers de los dispositivos (programas que permiten
al sistema operativo comunicarse con los periféricos) y las librerías de sistemas
(introducidas más adelante, en este mismo capítulo).
El “Hello World” para lenguaje ensamblador tendría el código que se presen-
ta a continuación. Este ejemplo está escrito para ensamblador de un procesador
Intel x86 con DOS y utilizando TASM.
MODEL SMALL
IDEAL
STACK 100H
DATASEG
MSG DB ‘Hello, world!’, 13, ‘$’
CODESEG
Start:
MOV AX, @data
© Editorial UOC 112 Escaneando la Informática

MOV DS, AX
MOV DX, OFFSET MSG
MOV AH, 09H ; DOS: output ASCII$ string
INT 21H
MOV AX, 4C00H
INT 21H
END Start

Es normal que, a simple vista, no se entienda, ya que estamos trabajando en


las partes y los mecanismos internos del procesador de un ordenador (por ej. los
registros). Por eso se llaman lenguajes de bajo nivel, porque se baja al nivel de
la máquina.

3.4.3. Tercera generación: Lenguajes de alto nivel

Viendo la complejidad de programar con los lenguajes que había hasta


entonces, empezaron a aparecer los lenguajes de alto nivel. Como ya hemos
comentado, éstos aportan una legibilidad mucho más próxima al lenguaje natu-
ral y ahorran al programador el tener que pensar en cada orden mínima que
tiene que ejecutar el ordenador.
Cualquier instrucción de este tipo de lenguajes equivale a unas cuantas líne-
as de código máquina o ensamblador.
Ejemplo de línea de código C:

if (x>0) factorial(x) Esta línea comprueba si x es mayor que cero (0). En caso de que así sea
llama a una parte del programa que calcula el factorial de x.

Así, a partir del código escrito en un lenguaje de este grupo, se utiliza una
herramienta (de hecho hay diferentes maneras de hacerlo) que “traduce” el
código a código máquina. Normalmente se hace mediante un compilador, aun-
que puede utilizarse un intérprete o incluso una máquina virtual (conceptos
que veremos más adelante).
La mayor parte de los lenguajes que se utilizan hoy día para la programación
de aplicaciones son de tercera generación (por ej. C, C++ y Java).
© Editorial UOC 113 Programación

3.4.4. Cuarta generación (4GL): Lenguajes casi naturales

Son lenguajes de programación que no son de propósito general, como los


que había habido hasta ahora, sino que están diseñados para algún propósito
específico (por ej. Sculptor, Dbase, SAS, PHP, etc.).
Los lenguajes de tercera generación habían conseguido facilitar mucho la
comunicación entre el hombre y la máquina, y habían mejorado claramente el
desarrollo del software desde sus predecesores. Aun así, la tarea de programar se
seguía considerando frustrante, lenta y propensa a errores. Lo cual llevó a la pri-
mera gran crisis del software, ya que la cantidad de trabajo que se esperaba que
hicieran los programadores superaba con creces el tiempo que tenían para lle-
varla a cabo. Para solucionar el problema, se tomaron varias medidas, entre
éstas encontramos la definición de la ingeniería del software y el diseño de len-
guajes de programación según los tipos de problemas que debían resolverse.
Ejemplo de línea de código de SQL:

Esta línea busca en una base de datos todos los registros que tienen
SELECT * FROM USUARIS
como campo de nombre BOB. Se puede ver que es casi como el len-
WHERE NAME=”BOB”
guaje natural (en este caso, el inglés).

Estos lenguajes están diseñados para reducir el esfuerzo de programación, el


tiempo y el coste de desarrollo. Pero no se puede errar en la elección del lengua-
je, ya que si no el proyecto se convierte muy probablemente en un fracaso.
No hay lenguajes mejores ni peores. Lo que puede facilitar o complicar la
programación de una aplicación es la idoneidad del lenguaje elegido para codi-
ficar.
Algunos tipos de lenguajes de cuarta generación son:
• Tratamiento de bases de datos (por ej. SQL y FOCUS)
• Generadores de informes (por ej. PostScript, BuildProfessional y Gauss)
• Manipuladores y analizadores de datos (por ej. Informix-4GL,
Mathematica y SAS)
• Tratamiento de flujo de datos (por ej. APE y AVS)
• Generación y dibujo de pantallas (por ej. Oracle Forms y Unify Accell)
• Generadores de interfaces gráficas de usuario (por ej. Borland Delphi,
Visual Basic’s form editor, Windows Forms y OpenROAD)
• Desarrollo web (por ej. PHP y ASP)
© Editorial UOC 114 Escaneando la Informática

3.4.5. Quinta generación (5GL): No nos ponemos de acuerdo

Diferentes fuentes definen la quinta generación de lenguajes de diversas


maneras. Entre éstas, las dos principales son:

Lenguajes para inteligencia artificial


Son aquellos basados en la resolución de problemas que utilizan restriccio-
nes para modelarlos, sin programar un algoritmo que lo haga. La mayoría son
lenguajes lógicos, basados en restricciones, y/o declarativos (por ej. Prolog,
ILOG o eCLiPse). El programador no se tiene que preocupar por CÓMO resol-
ver sino de QUÉ resolver. Se hicieron bastantes inversiones en estos lenguajes a
lo largo de los años noventa, con la idea de sustituir los lenguajes de alto nivel,
pero fue un fracaso y actualmente se utilizan sólo en círculos académicos.

hermano(X,Y):- madreOpadre(X,Z), Esta línea dice que X e Y son hermanos si comparten padre o
madreOpadre(Y,Z). madre.

En el ejemplo anterior se utiliza una regla. Los lenguajes lógicos utilizan


hechos y reglas para modelar la realidad y a partir de ésta, poder inferir las res-
puestas a posibles preguntas.

Lenguajes con GUI (Graphical User Interface)


Otros presentan los 5GL como los que utilizan una interfaz gráfica o visual
de usuario para crear un código fuente que se compila posteriormente con un
compilador de lenguajes de tercera o cuarta generación. Compañías como
Borland, Microsoft e IBM elaboran este tipo de productos de programación
visual para el desarrollo de aplicaciones en Java, por ejemplo.

4. Algorítmica

Hasta ahora hemos revisado un poco la evolución de los lenguajes hasta lle-
gar a los que utilizamos hoy día. Pero exactamente, ¿cómo son estos lenguajes?
¿Qué tipo de problemas nos permiten resolver y de qué manera? Pronto vere-
mos algunas de las características comunes a todos ellos.
© Editorial UOC 115 Programación

La palabra “algoritmo” proviene del nombre del matemático musulmán


persa del siglo IX, Abu Abdullah Muhammad ibn Musa al-Juarismi. Pero antes
de empezar, volvamos a la definición de programa que habíamos visto. Un pro-
grama era un conjunto de órdenes que el ordenador podía entender y que resol-
vían un problema o hacían una tarea concreta. Un buen programador, antes de
escribir estas órdenes inteligibles para el ordenador (lo que llamaremos “códi-
go”), escribirá lo que se llama “algoritmo”. Los algoritmos son el resultado de
aplicar el razonamiento a un problema: los pasos que hay que seguir para resol-
ver aquella situación en concreto. Estos pasos los vamos generando y aplican-
do, sin ser conscientes, en nuestra vida diaria cada vez que tenemos que resol-
ver una situación de cualquier tipo (por ejemplo, hacer la compra, freír un
huevo, conducir, etc.).
Por lo tanto, la capacidad de pensar algoritmos la tenemos todos los seres
humanos. La cuestión, a la hora de programar, es hacer entender nuestra
solución a un ordenador. Para ser un buen programador, se tiene que ser
capaz, como veremos seguidamente, de formular un algoritmo que resuelva
el problema en un tiempo aceptable y con unas necesidades de recursos
alcanzables; y después, traducir ese algoritmo a un lenguaje que entienda el
ordenador.

4.1. Las bases

Una de las máximas más famosas de la programación es la de Niclaus Wirth:


“Algoritmos + estructuras de datos = programas”. Esta ecuación explica que los
programas se basan en una combinación de los algoritmos que pensamos para
resolver el problema y la manera como guardamos los datos. Profundicemos un
poco en esta idea.
Como ya habíamos dicho, cuando tenemos un problema que queremos
resolver con un programa, lo primero que necesitamos es un algoritmo. Esto es,
el conjunto de pasos necesarios para resolver aquel problema. Un ejemplo clá-
sico de algoritmo (debe quedar claro que no es un programa porque no se puede
resolver con un ordenador) es el de la tortilla de patatas: “Tenemos un invitado
en casa y queremos hacer una tortilla de patatas para dos personas”.
© Editorial UOC 116 Escaneando la Informática

Algoritmo de la tortilla de patatas

1) Batir el contenido de 4 huevos (clara y yema) en un cuenco con la ayuda


del tenedor.
2) Mientras queden patatas:
Pelar las patatas con el cuchillo.
Cortar las patatas con el cuchillo, en trozos pequeños de no más de 2
cm.
3) Sumergir las patatas 10 minutos en aceite caliente pero que no llegue a
humear.
4) Introducir las patatas fritas en el cuenco y mezclarlas con el huevo con la
ayuda del tenedor.
5) Encender el fuego a media potencia y colocar la sartén.
6) Echar dentro de la sartén una cucharada sopera de aceite de oliva.
7) Echar en el cuenco una cucharadita rasa de sal.
8) Verter el contenido del cuenco en la sartén.
9) Esperar sin remover durante 2 minutos.
10) Si se ha pegado o se quema, bajar el fuego y despegarla.
11) Esperar sin remover durante 3 minutos.
12) Darle la vuelta a la tortilla con ayuda de un plato de diámetro mayor que
la sartén.
13) Esperar 5 minutos más.
14) Retirar la tortilla.

Acabamos de definir los pasos que hay que seguir para, a partir de unos
ingredientes y unas herramientas que tenemos en casa, llegar a hacer una torti-
lla de patatas para dos personas. Estos pasos definirán el algoritmo. Cuando
queremos resolver un problema con el ordenador, la mecánica es parecida: tene-
mos un estado inicial y queremos hacer alguna tarea. Tenemos que preparar un
algoritmo, traducirlo a un lenguaje de programación (programar) y ejecutarlo
sobre el ordenador. Así, si es correcto, el ordenador cumplirá la tarea definida
inicialmente.
Para escribir un algoritmo, sin darnos cuenta, utilizamos tres estructuras con
las que se puede resolver cualquier problema. Así, un buen programador, ante
un problema, tiene que ser capaz, inmediatamente, de ver cuáles de estas estruc-
turas combinará para resolverlo. Veamos cuáles son.
© Editorial UOC 117 Programación

4.1.1. Estructuras

La estructura básica de ejecución de un algoritmo es la llamada “estructura


secuencial”, que se define como la aplicación consecutiva de las órdenes.
Posibles algoritmos que siguen esta estructura serían las instrucciones para
montar un mueble o una receta de cocina como la que acabamos de ver.
Pero una vez tenemos la posibilidad de solucionar el problema ejecutando
orden tras orden, nos preguntamos si nos podríamos encontrar con otra situa-
ción. La siguiente necesidad es la toma de decisiones. A veces, la secuencia de
órdenes que hay que ejecutar depende de una determinada condición que se
produce en el sistema. Por ejemplo, en la Fórmula 1, si los mecánicos tienen que
cambiar los neumáticos de un coche, no deben ejecutar los mismos pasos si
llueve o si no llueve. El hecho de que llueva o no es un dato que debe tenerse
en cuenta en este caso. Se aplica lo siguiente:

si llueve entonces preparar neumáticos_de_lluvia


si no preparar neumáticos_de_seco

Así, estamos condicionando la ejecución de una acción u otra según el esta-


do del sistema. En este caso hemos utilizado una “estructura condicional”.
En el algoritmo de la tortilla de patatas, vemos una estructura condicional en
el paso 10, en el que la ejecución de una acción depende de si la tortilla se ha
pegado o se quema.
Por último, nos encontramos con una última estructura, que se aplica cuan-
do una acción o un conjunto de acciones se tienen que ejecutar múltiples veces.
Ésta se llama “estructura iterativa”. Un ejemplo claro es lo que hace un profe-
sor cuando tiene que corregir exámenes:

para cada examen hacer


corregir examen

En este caso, la orden “corregir examen” se repetirá tantas veces como exá-
menes tenga el profesor. Si tiene treinta estudiantes, estas dos líneas equivalen
a escribir treinta veces la orden “corregir examen”.
En el algoritmo de la tortilla de patatas, vemos una estructura iterativa en el
paso 2, ya que se van repitiendo dos acciones, mientras quedan patatas.
© Editorial UOC 118 Escaneando la Informática

Cualquier algoritmo se puede escribir utilizando una combinación de tres


estructuras: secuencial, condicional e iterativa.

4.1.2. Datos

Hasta ahora hemos visto que para resolver un problema se tiene que pensar
un algoritmo y –en el caso de que se tenga que ejecutar en un ordenador–, pos-
teriormente, traducirlo a un lenguaje de programación. Ahora bien, recordando
lo que nos decía Wirth, los programas no son simplemente algoritmos, sino una
combinación de éstos con estructuras de datos. Pero ¿qué son los datos?
Consideramos que el estado está definido por la situación de los elementos
con los que trabaja el algoritmo en un instante concreto. Ya hemos dicho que
un algoritmo (o un programa) nos lleva de un estado inicial a un estado final.
Así, antes de aplicar el algoritmo de la tortilla de patatas, el estado es: estamos
en la cocina de casa, tenemos huevos, aceite, patatas, etc. En cambio, una vez
hemos aplicado el algoritmo, tenemos una tortilla, y algunas cosas que tirar a la
basura. Es fácil ver que cada paso del algoritmo que vamos ejecutando produce
un cambio de estado.
Cuando programamos, estos estados se tienen que guardar de alguna mane-
ra, ya que cada paso del algoritmo tiene sentido si se aplica sobre un/os cierto/s
elemento/s, y en un cierto estado. Un ejemplo claro es, por ejemplo, el segun-
do paso del algoritmo de la tortilla de patatas: pelar las patatas. Esto sólo tiene
sentido si las patatas todavía no están peladas. En este caso, consideramos que
tenemos un dato (patata) que está en un estado (pelada). Otros posibles estados
para el dato “patata” serían “sin pelar”, “cortada”, etc.
Estos datos, cuando hablamos de programas, quedan guardados en la memo-
ria del ordenador y determinan el estado de ejecución del programa en cada ins-
tante.
Recordemos así lo que decía Wirth: “Algoritmos + Estructuras de datos =
Programas”. Efectivamente, cualquier programa contendrá un conjunto de ins-
trucciones ordenadas (un algoritmo) y un conjunto de datos sobre los cuales se
aplican las instrucciones.
© Editorial UOC 119 Programación

4.1.3. Organización de los programas (divide and conquer)

Centrándonos en la programación de ordenadores con las estructuras que


hemos visto (secuencial, condicional e iterativa), como ya hemos subrayado,
puede escribirse cualquier algoritmo. En los ejemplos que hemos escrito, no lo
hemos tenido en cuenta, pero normalmente, un programa hecho por una
empresa, por ejemplo, se extenderá miles o incluso millones de líneas. Eso hace
que haya que poner un poco de orden en los programas. Así, por cuestión de
legibilidad y de reutilización, se tiene que evitar escribir los algoritmos en un
solo bloque. Por eso intentaremos de una manera u otra unir instrucciones que
hagan una subtarea de lo que al final queremos hacer.
Estos trozos de código se llaman funciones, procedimientos, objetos, etc.,
según el tipo de lenguaje con el que se programa y la organización del bloque.
Efectivamente, por una parte, cuando estamos escribiendo un programa, es
posible que haya partes del código que se tengan que repetir a lo largo del algo-
ritmo. En vez de repetirlas, se escriben dando a su conjunto un nombre que se
podrá utilizar desde cualquier parte del programa. Por ejemplo, si tengo el blo-
que siguiente, que llamaré “cambia_neumático”:

cambia_neumático:
quita neumático_gastado
pon neumático_nuevo
comprueba neumático
fin cambia_neumático

ahora puedo utilizar el bloque a lo largo del algoritmo sin volver a escribir
cada orden:

para cada neumático


hacer cambia_neumático

De esta manera, vemos que será más legible el código, ya que queda claro
qué hace sin tener que leer todas las líneas. Si ya sé que uno de estos bloques
funciona correctamente, no hace falta que lo revise si después mi programa
tiene errores. Eso facilita también la fase de búsqueda de errores, dado que es
más fácil ir aislando los errores que se produzcan.
© Editorial UOC 120 Escaneando la Informática

Así, y volviendo al ejemplo de la tortilla, una vez tenemos el algoritmo para


hacerla, si queremos hacer un algoritmo para hacer una cena completa, pode-
mos poner el de la tortilla de patatas en un módulo y llamarlo desde la cena
para no tener que escribirlo todo otra vez.

4.2. Conceptos avanzados

4.2.1. Complejidad algorítmica

Cuando se escribe un algoritmo para resolver un problema, una de las exi-


gencias es que sea capaz de acabar algún día. Considerando, pues, que todos los
algoritmos aceptables son finitos, nos encontramos con un nuevo objetivo un
poco más ambicioso: conseguir diseñar el mejor algoritmo posible para un pro-
blema dado.
Para conseguirlo, lo primero que hace falta es una manera de medir esta
“cualidad”. Así, en el mundo de la programación se consideran típicamente dos
variables que hay que medir para determinarla: el tiempo y el espacio. El prime-
ro da una idea del tiempo necesario para resolver el problema aplicando aquel
algoritmo y el segundo mide los recursos (normalmente, la cantidad de memo-
ria) necesarios para guardar los datos del problema.
Con respecto a la complejidad temporal, la idea es que los problemas se pue-
dan resolver en el mínimo tiempo posible con respecto a los datos de entrada.
Así, por ejemplo, si tenemos que ordenar unas tarjetas de visita siguiendo el
orden alfabético, consideramos que los datos de entrada son las tarjetas. El
número de movimientos que tenemos que hacer para ordenar n tarjetas nos
dará la complejidad del algoritmo.
Fijémonos en el comportamiento (temporal) de algunos algoritmos según la
diferente cantidad de tarjetas que hay que ordenar:

Complejidad n=10 n=100 n=1000 n=10000 n=105 n=106

lg n 3.32 6.64 9.96 13.28 16.6 19.93

n 10 100 1000 10000 105 106

50·n 500 5000 50000 5·105 5·106 50·106

n2 100 10000 106 108 1010 1012

n3 1000 106 109 1012 1015 1018


© Editorial UOC 121 Programación

Cada línea de la tabla anterior presenta los resultados para un algoritmo dife-
rente. Si nos fijamos, por ejemplo, en la última línea, vemos que dado este algo-
ritmo, si se tienen que ordenar 10 tarjetas (n=10), hay que hacer 1.000 movi-
mientos para conseguirlo.
De esta manera, los tres primeros (con complejidades lg n, n y 50·n) se con-
sideran algoritmos aceptables para solucionar el problema. Los otros no son lo
bastante eficientes.
El Clay Mathematics Institute ofrece un millón de dólares a las personas que
resuelvan una serie de problemas matemáticos altamente complejos. Uno de
ellos es ¿P=NP?. El último que se solucionó fue la demostración del último
Teorema de Fermat.
Yendo un poco más allá, también se clasifican los problemas según la exis-
tencia o no (hasta ahora) de algoritmos que los resuelvan en tiempo polinomial
(problemas P) o no (problemas NP). Así, un problema P es el que tiene un algo-
ritmo conocido con complejidad polinómica que lo resuelve. Una de las gran-
des incógnitas a que actualmente se enfrenta la comunidad científica es, como
hemos comentado, si todos los problemas son P. Eso se resume normalmente
con la pregunta ¿P=NP?.

4.2.2. Recursividad

Este es un recurso que se utiliza a veces a la hora de programar para sustituir


una estructura iterativa. La recursividad consiste en la definición de una fun-
ción sobre ella misma. Para verlo más claro ponemos un ejemplo de ello:
El factorial de un número n se define matemáticamente como n!=n·(n-
1)!=n·(n-1)...2·1. Por ejemplo, el factorial de 6 es
6!=6·5!=6·5·4!=...=6·5·4·3·2·1=720. Queremos calcular el factorial de un número
N. Una solución recursiva sería definir la función siguiente:

factorial calcula_factorial (N):


si N és 0 factorial = 1
sinó factorial = N x calcula_factorial (N-1)
retorna factorial
fi calcula_factorial

Esta función recibe un número N y devuelve su factorial. Para hacerlo, defi-


ne el factorial de N como N multiplicado por el factorial de N-1. Eso determina
© Editorial UOC 122 Escaneando la Informática

la recursividad, pero se tiene que tener en cuenta que ésta tiene que acabar (si
no el algoritmo no sería finito). Para hacerlo se añade una condición final (en
este caso, cuando N es 0).

5. Traducción de código fuente a código máquina

Los lenguajes de programación nos facilitan la comunicación con los orde-


nadores, ya que nos ahorran tener que saber escribir código máquina. Éste es
muy complejo y tiene más en cuenta la estructura y la arquitectura del ordena-
dor que el tipo de problema que se tiene que resolver.
Por eso hay herramientas que traducen los lenguajes de programación que
nosotros conocemos a este código máquina. Hay diferentes estrategias para
hacerlo, que describiremos a continuación.

5.1. La compilación

La compilación es el proceso mediante el cual el código fuente, legible por


los humanos, se transforma en código máquina, legible por los ordenadores. El
resultado de la compilación puede ser o bien un programa ejecutable o bien una
biblioteca.
Una biblioteca es un conjunto de bloques de código (como ya hemos visto
antes) que se utiliza para construir software. Las bibliotecas se utilizan normal-
mente para evitar repetir escribir una y otra vez lo mismo. Así si, por ejemplo,
compramos un robot y lo queremos programar, es común que el fabricante del
robot nos provea también de una biblioteca que contenga las funciones para
comunicarnos con él. Igualmente, para programar aplicaciones de comunica-
ción en red, hay bibliotecas de funciones que nos facilitan mucho esta tarea.
Cada tipo de ordenador puede leer y entender un código máquina diferente.
Por ello hay compiladores diferentes para cada arquitectura (por ej. x86, Sparc,
PPC, etc.).
Así, la compilación se puede entender como la traducción automática de un
texto de un idioma a otro, a fin de que alguien lo pueda entender.
© Editorial UOC 123 Programación

En una reunión de los protagonistas de la película Hackers, hablan de sus


biblias de programación, seguridad, etc. Entre éstas, mencionan el “Libro del
dragón” (Dragon Book en inglés), que es un libro de programación de compila-
dores muy utilizado en las facultades de informática de todo el mundo. El
Dragon Book se llama así porque su portada muestra a un caballero y un dragón
en una batalla. Hay dos versiones (A. Aho, 1977 y 1986), una primera con el dra-
gón de color verde (Green Dragon Book o Old Dragon Book) y la segunda (Red
Dragon Book o New Dragon Book). La contraportada muestra de manera humo-
rística la lucha de Don Quijote contra los molinos.
Si bien la versión de 1977 está un poco desfasada, todavía se puede utilizar
para temas prácticos. Incluye el tratamiento de todas las fases de la compilación
con bastante detalle algorítmico para que un estudiante pueda construir un
pequeño compilador en un solo curso.

5.2. La interpretación

Un intérprete es un programa que puede ejecutar directamente el código


fuente sin necesidad de transformaciones. El código fuente no se transforma
sino que se ejecuta línea a línea, por lo que el proceso de interpretación debe
repetirse cada vez que se quiere ejecutar el programa.
La ejecución de un programa interpretado es siempre más lenta que la ejecu-
ción del mismo programa en código máquina, pero nos proporciona otras ven-
tajas, como son la sencillez de programación o la facilidad para detectar errores.
Si la compilación era una especie de traducción automática de un texto, la
interpretación está más próxima a la interpretación que se haría en las Naciones
Unidas de una charla de un jefe de Estado. Se hace en tiempo real. No se gene-
ra ningún documento.

5.3. Código intermedio

Para poder conseguir una independencia de arquitectura, hay lenguajes de


programación que combinan el proceso de compilación y de interpretación. El
código fuente se transforma, mediante la compilación, en un código interme-
dio parecido al código máquina pero más sencillo. Este código intermedio tiene
que ser interpretado por cada arquitectura diferente.
© Editorial UOC 124 Escaneando la Informática

Como todas las arquitecturas interpretan el mismo código intermedio, los


programas pueden escribirse, compilarse y ejecutarse en plataformas diferentes,
sin los problemas de incompatibilidad que normalmente se derivan del tipo de
arquitectura o sistema operativo.
Por otra parte, algunos lenguajes de programación generan, como resultado
de la compilación, código fuente escrito en otro lenguaje. Este código resultan-
te tiene que ser compilado nuevamente con el compilador adecuado.
Esta técnica permite desarrollar nuevos lenguajes de programación sin tener
que programar previamente complicados intérpretes o compiladores, ya que se
utilizan herramientas existentes.
Como se ha dicho, esta técnica combina las anteriores. Buscando otra vez un
símil, se puede entender como una lengua franca intermedia de la que hay
muchos intérpretes en cualquier lengua. Pero hay que realizar un paso anterior:
pasar de la lengua inicial a esta lengua intermedia, lo cual se hace generando
una traducción automática.

6. Paradigmas de programación

Un paradigma de programación es un estilo de programación que provee y


determina la visión que el programador tiene de la ejecución del programa. Hay
lenguajes que soportan un paradigma y otros que soportan múltiples paradig-
mas.
Mientras que el “paradigma de programación” se relaciona con programa-
ción, la “metodología de programación” se relaciona con la aplicación de inge-
niería del software.

6.1. Programación desestructurada o spaghetti

Uno de los lenguajes desestructurados por antonomasia es el BASIC, princi-


palmente por su instrucción GOTO, que da un salto incondicional de una parte
del programa a otra. El código del programa se presenta en una serie de instruc-
ciones, una detrás de otra, en un único bloque, sin estructura. Este tipo de pro-
© Editorial UOC 125 Programación

gramación, utilizada con los primeros lenguajes, hace que el programa sea difí-
cil de entender, corregir o modificar para añadir una nueva funcionalidad. Otro
problema es la dificultad que tiene el programador en reaprovechar el código.
Algunos ejemplos de este tipo de lenguajes son: BASIC, FORTRAN y algunos
lenguajes de scripting.
Los paradigmas que veremos a partir de ahora se consideran estructurados, ya
que la programación desestructurada plantea algunos problemas.

6.2. Programación procedimental (o imperativa)

El código del programa está dividido en diferentes bloques llamados funcio-


nes o procedimientos. Cada uno de estos bloques se encarga de realizar una tarea
específica y puede ser llamado varias veces desde otros puntos del programa.
Este tipo de programación permite ver un programa como la ejecución de
una serie de tareas complicadas, lo cual disminuye la complejidad de la progra-
mación. Está claramente basado en la idea de divide and conquer.
Algunos ejemplos de este tipo de lenguajes son: Ada, C, Delphi y Pascal.

6.3. Programación orientada a objetos

Un programa está formado por un conjunto de objetos independientes que


interactúan entre ellos. Cada objeto contiene su propio código y sus datos, tiene
la capacidad de recibir peticiones (mensajes de otros objetos) para hacer alguna
tarea, procesar datos y enviar peticiones (mensajes) a otros objetos.
El resultado es un programa mucho más sencillo y fácil de modificar. Esta
sencillez y el hecho de que los objetos se puedan reaprovechar para diferentes
aplicaciones han hecho que este sea el paradigma más utilizado en la actualidad
para la programación de aplicaciones.
Algunos ejemplos de este tipo de lenguajes son: Smalltalk, PHP, C++ y Java.

6.4. Programación orientada a aspectos

El programa se divide según la funcionalidad que se quiere proporcionar,


intentando que cada parte sea lo más independiente posible del resto. Se utili-
© Editorial UOC 126 Escaneando la Informática

zan módulos llamados “aspectos” para codificar la funcionalidad compartida


entre módulos y “puntos de unión” para definir cuándo se tiene que ejecutar
un aspecto determinado.
El objetivo es poder cambiar el comportamiento del programa haciendo los
mínimos cambios posibles.
El ejemplo más claro de este tipo de lenguaje es AspectJ.

6.5. Otros paradigmas

Hasta aquí tenemos los paradigmas de programación más utilizados, pero


hay muchos otros que se podrían comentar. Entre ellos, destacamos los siguien-
tes:

6.5.1. Programación funcional

La programación funcional concibe la computación como una evaluación de


funciones matemáticas y evita utilizar datos que vayan cambiando de valor a lo
largo de la ejecución del programa. Por lo tanto, no definirá los cambios de esta-
do a lo largo de la ejecución del programa.
Algunos ejemplos de este tipo de lenguajes son: Erlang, R, APL, Lisp y ML.

6.5.2. Programación lógica

Los programas implementados siguiendo este paradigma suelen definir


hechos, que son considerados ciertos siempre, y reglas, que no explican cómo
se tiene que solucionar un problema, sino qué relaciones hay entre las entida-
des que se modelan.
El ejemplo más claro de este tipo de lenguaje es Prolog.
© Editorial UOC 127 Programación

7. Lenguajes de programación

Como ya hemos visto, en los inicios de la informática, los programas se escri-


bían directamente en código máquina y requerían un conocimiento profundo
del ordenador. Un científico que quisiera resolver un problema matemático ayu-
dándose del ordenador tenía entonces dos problemas más: cómo generar un
algoritmo que resolviera el problema y cómo traducir ese algoritmo a código
máquina.
El hardware es la parte física de un ordenador: el procesador, la memoria, los
dispositivos, etc. Pero ¿se podría crear un lenguaje que permitiera leer y modi-
ficar los programas fácilmente sin preocuparse del hardware? Veamos si eso se
ha conseguido, analizando los lenguajes más importantes históricamente y en
la actualidad.
Uno de los primeros lenguajes conocidos y utilizados en todo el mundo fue
FORTRAN. Posteriormente se crearon COBOL y LISP. Estos tres han sido los
padres de los lenguajes de programación y han servido de base para el desarro-
llo de muchos otros.
Yahoo! Store, la primera versión de la tienda virtual de Yahoo! fue programa-
da en LISP, un lenguaje que normalmente no se utiliza para este tipo de desarro-
llo pero que redujo el tiempo de programación. Lejos de quedar en el olvido,
hoy día se utilizan versiones revisadas de estos primeros lenguajes de programa-
ción. FORTRAN se utiliza en aplicaciones científicas y de cálculo numérico. El
software de bancos y cajas está principalmente escrito en COBOL, ya que des-
pués de años de programación han conseguido unas bibliotecas libres de error
(error free). LISP todavía se utiliza en el ámbito de la inteligencia artificial y en
muchas otras aplicaciones comerciales.
En la imagen siguiente podemos ver una línea temporal que representa la
aparición de los lenguajes de programación más importantes hasta la actuali-
dad.
© Editorial UOC 128 Escaneando la Informática

Línea de tiempo de los lenguajes de programación más importantes

7.1. Los lenguajes más utilizados

Hay un índice, llamado TIBOE-PCI (Programming Community Index), que pre-


senta los lenguajes de programación más utilizados o populares. Esta “popula-
ridad” se basa en los cursos de programación que se ofrecen, en los requisitos
que se piden en las ofertas de trabajo, en los productos que se venden, etc. La
recopilación de estos datos se hace mensualmente utilizando los buscadores de
Internet más conocidos (véase la imagen siguiente).

Índice TIBOE, enero 2010

7.1.1. BASIC

En los primeros años en que se utilizó, las líneas del programa tenían que
estar numeradas. Esta numeración normalmente se hacía de diez en diez (10,
© Editorial UOC 129 Programación

20, 30, etc.) dejando así un espacio entre instrucción e instrucción por si se tení-
an que añadir líneas posteriormente. El lenguaje de programación BASIC
(Beginner’s All-purpose Symbolic Instruction Code) fue diseñado e implementado
en el Dartmouth College (EE.UU.) para facilitar a los estudiantes de letras el
acceso al ordenador. Por extensión, cualquier persona sin conocimientos cien-
tíficos podía utilizar un sencillo juego de instrucciones para escribir un progra-
ma.
El BASIC se hizo popular a partir de la aparición de los ordenadores domés-
ticos que, a diferencia de los grandes computadores que había en las universi-
dades y en las industrias, disponían de muy poca memoria y espacio de disco.
Requería menos recursos para ejecutarse que el resto de los lenguajes de la
época, lo que lo hacía ideal para este tipo de ordenadores. Esta necesidad míni-
ma de recursos y el hecho de que fuera sencillo de aprender hizo que la mayo-
ría de los ordenadores domésticos llevaran incorporado un intérprete de BASIC.
Precisamente fue en esta época y gracias a BASIC, cuando se fundó la com-
pañía Microsoft. Bill Gates y sus socios desarrollaron un intérprete de BASIC
para el primer ordenador doméstico, el Altaif 8800. En 1975 pusieron en venta
Altair BASIC y posteriormente desarrollaron nuevas versiones de Microsoft
Basic para otras plataformas (Apple, IBM, etc.). Treinta años después es la mayor
compañía de software del mundo.
El código del “Hello World” en BASIC es el siguiente:

10 PRINT “Hello World!”

7.1.2. C

Los lenguajes anteriores a la aparición de C eran ampliamente utilizados para


resolver problemas, pero no ofrecían un buen rendimiento ni eran aptos para la
creación de un sistema operativo. Los sistemas operativos estaban programados
en código ensamblador, lo cual implicaba que había que desarrollar uno especí-
fico para cada arquitectura.
Se dice que el lenguaje C fue creado por el equipo de desarrollo de UNIX para
poder jugar al juego Space Travel. Este juego estaba programado en ensamblador
(como el sistema operativo), hecho que obligaba al equipo a volver a codificar-
lo nuevamente cuando querían jugar en otro tipo de ordenador. En vez de eso
crearon un nuevo lenguaje (llamado C) a partir de uno ya existente llamado B.
© Editorial UOC 130 Escaneando la Informática

El nuevo lenguaje era bastante flexible para permitir combinar las programacio-
nes en bajo y alto nivel. Mediante un compilador específico para cada arquitec-
tura (que traducía de C al ensamblador de la máquina) podían obtener progra-
mas ejecutables para diferentes ordenadores sin tener que cambiar el código
fuente. En el año 1973 el código de UNIX se rescribió utilizando C. De esta
manera, fue el primer sistema operativo no escrito en ensamblador. Eso aceleró
y simplificó la programación para este sistema, lo cual se tradujo en un impor-
tante avance en el desarrollo.
El hecho de que, a pesar de ser propiamente un lenguaje de alto nivel, ofrez-
ca la posibilidad de codificar también en bajo nivel para poder acceder a todos
los recursos del ordenador, hace que C sea un lenguaje peligroso para princi-
piantes. Aun así, su potencia lo ha convertido en uno de los más utilizados.
La sintaxis de C se muy permisiva. Tanto es así que incluso hay concursos de
“programación ofuscada”, es decir, concursos en que se valora que el código
fuente no pueda leerse o incluso que forme bonitos dibujos de caracteres (véase
un ejemplo en el anexo 2 de este capítulo).
El código del “Hello World” en C es el siguiente:

#include <stdio.h>
int main (void)
{
printf ("Hello world!\n");
return 0;
}

7.1.3. C++

Como se explica más adelante, la orientación a objetos es un paradigma de


programación en el que el programa se divide en objetos que interactúan entre
sí. Once años después de la aparición de C, Bjarne Stroustrup modificó el len-
guaje más popular de la época para incluir la orientación a objetos, y creó así el
C++ (C más más). En su página personal, Stroustrup explica que el nombre de
C++ se debe a que es una evolución de C, pero sin que los cambios sean tan
importantes como para llamarlo D.
Como ya hemos visto, la programación procedimental es una metodología
de programación en la que el programa se divide en funciones o procedimien-
tos que se encargan de resolver partes de los problemas. Aunque C++ ofrece la
posibilidad de programar utilizando el paradigma orientado a objetos, no supri-
© Editorial UOC 131 Programación

me la posibilidad de utilizar las antiguas características de C, lo cual permite el


reaprovechamiento de recursos escritos en C y la combinación de los paradig-
mas “orientación a objetos” y “programación procedimental”, hecho que ha
generado bastantes críticas.
También ha recibido críticas por ofrecer una excesiva complejidad. Incluso
en Internet hay una entrevista ficticia a Stroustrup en la que dice: “Se suponía
que todo era una broma. Nunca pensé que la gente se tomaría mi libro en serio.
Cualquiera con dos dedos de frente puede ver que la programación orientada a
objetos no es intuitiva, además de ilógica e ineficiente”. Insistimos: es una
entrevista que nunca se produjo, o por lo menos eso es lo que defiende el pre-
sunto autor.
El código del “Hello World” en C++ es el siguiente:

#include <iostream>
using namespace std;
class HelloWorld
{
public: HelloWorld () {}
~HelloWorld () {}
void sayHello { cout << "Hello World!" << endl;}
};
int main (void)
{

HelloWorld hw;
hw.sayHello();
return 0;
}

7.1.4. Java

JAVA fue desarrollado por Sun Microsystems en los años noventa. En argot
norteamericano JAVA quiere decir “café” y la historia cuenta que el equipo que
desarrollaba este lenguaje escogió este nombre en torno a una taza de café. Por
eso, su logotipo es una taza de café y los objetos que utiliza para encapsular
información (agrupar en bloques) se llaman “Java Beans” (granos de café). Aún
podemos encontrar otra referencia al café en su código, ya que si abrimos cual-
quier clase compilada de JAVA con un editor hexadecimal, veremos que todas
las clases empiezan con los cuatro primeros bytes hexadecimales CA FE BA BE.
© Editorial UOC 132 Escaneando la Informática

JAVA es un lenguaje orientado a objetos que ofrece, en la misma base del len-
guaje, apoyo para la utilización de redes y la ejecución remota de código (fue
creado durante el boom de Internet). Sintácticamente, JAVA es muy parecido a
C y C++, pero mucho más simple, ya que tiene reglas menos permisivas. Eso
implica limitar las diferentes maneras de hacer lo mismo (cosa que da lugar a
ambigüedades), no permitir ciertas estructuras que puedan generar errores, etc.
Un programa escrito con JAVA puede ejecutarse en cualquier ordenador y sis-
tema operativo sin tener que modificar ninguna línea de código y sin volver a
compilar. La idea “codifica una vez, ejecuta en cualquier lugar” junto con el
hecho de proporcionar un entorno seguro a la hora de ejecutar código han
hecho que sea uno de los lenguajes más utilizados actualmente.
La Máquina Virtual Java (Java Virtual Machine, JVM) es una capa que se
podrá poner por encima de cualquier máquina para que así pueda ejecutarse el
mismo código, independientemente de ésta. Por eso, al compilar, se genera
código intermedio, interpretable por la JVM. Las mayores críticas a JAVA provie-
nen de su velocidad de ejecución y la cantidad de memoria que utiliza para la
ejecución. Es un lenguaje que se compila y genera un código intermedio que
después tiene que ser interpretado por la Máquina Virtual Java. Por eso, la eje-
cución de los programas de las primeras versiones de JAVA era mucho más lenta
que la de otros similares hechos en C o C++. Actualmente la diferencia ya no es
tan grande.
El código del “Hello World” en JAVA es el siguiente:

public class HelloWorld {


public static void main (String args[])
{
System.out.println ("Hello World");
}
}

7.1.5. PHP

Lenguajes de scripting son lenguajes normalmente creados para facilitar el


proceso de editar, compilar y ejecutar programas. PHP es un lenguaje creado
para ser utilizado en un servidor web. Se utiliza principalmente para escribir
páginas HTML dinámicas (la página se genera personalizada para cada usuario
© Editorial UOC 133 Programación

que se conecta y después se envía) a partir de información de una base de datos,


aunque también puede ser utilizado desde la línea de órdenes principalmente
como lenguaje de scripting. Hay que añadir que también soporta el paradigma
de orientación a objetos.
Uno de sus puntos fuertes es la comunidad OpenSource, que lo utiliza y lo
hace evolucionar. Esta comunidad de usuarios proporciona multitud de bibliote-
cas que los programadores de PHP pueden utilizar libremente en sus programas.
PHP es uno de los miembros principales del LAMP (Linux Apache MySQL y
PHP). LAMP es el nombre con que se conoce un entorno de trabajo para la web
en el que el sistema operativo es GNU/Linux, el servidor de páginas web es
Apache, la base de datos, MySQL, y el lenguaje de programación, PHP.
Actualmente, muchos desarrollos orientados a Internet (blogs, wikis, tiendas en
línea, páginas de noticias, etc.) utilizan este tipo de plataforma.
El código del “Hello World” en PHP es el siguiente:

<?php echo "Hello World!" ?>

8. Software libre

La idea del software libre surgió a partir de un problema que tuvo Richard
Stallman con una impresora. El software que la controlaba no se podía modifi-
car y él quería mejorarlo para evitar unos problemas que se le planteaban. Así
el concepto “software libre” nace en 1984 cuando Stallman inicia el proyecto
GNU y crea la Free software Foundation (FSF). Este proyecto tiene por objetivo
crear un sistema operativo totalmente libre. Antes de esta fecha también había
muchas aplicaciones que se distribuían con el código fuente o de forma gratui-
ta, pero no es hasta ese momento cuando se crean unas normas y emerge la
conciencia identitaria y de pertenencia. GNU es una palabra que se define recur-
sivamente. Quiere decir: “GNU’s Not UNIX”.
Un software se considera libre si garantiza las cuatro libertades siguientes:
• La libertad de ejecutar el programa para cualquier propósito.
• La libertad de estudiar cómo trabaja el programa y de adaptarlo a las nece-
sidades propias. El acceso al código fuente es una condición previa.
© Editorial UOC 134 Escaneando la Informática

• La libertad de redistribuir copias para poder ayudar a vuestros vecinos.


• La libertad de mejorar el programa y de difundir vuestras mejoras al públi-
co, para que toda la comunidad pueda beneficiarse. El acceso al código
fuente es una condición previa.

Así pues, el software libre se puede redistribuir y modificar, bien de forma gra-
tuita o cobrando por esta distribución.
Uno de los proyectos de software libre más conocidos es, sin duda, el núcleo
(kernel) Linux, programado por Linus Torvals en el año 1991. GNU/Linux es la
implementación abierta del sistema operativo Unix para computadores perso-
nales, con Linux de núcleo. El proyecto GNU/Linux lo inició Richard M.
Stallman en el año 1984 con el objetivo de crear un clon del sistema operativo
Unix, pero garantizando las cuatro libertades mencionadas más arriba.
Los proyectos de código abierto y la creación de software libre se caracterizan
por que en su elaboración participan decenas a miles de personas de todo el
mundo. Un grupo reducido de personas toma las decisiones de diseño y de pro-
gramación mientras que un gran número de programadores detectan y corrigen
errores y añaden nueva funcionalidad.

9. El mundo laboral

En el mundo laboral hay diferentes funciones directamente relacionadas con


la programación a las que normalmente pueden acceder los informáticos (inge-
nieros o ingenieros técnicos). Por descontado, hay muchas otras funciones
–relacionadas con otras áreas como la seguridad, las bases de datos, etc.– para
las que las exigencias de conocimientos de programación son elevadas. A con-
tinuación sólo presentaremos las que están directa y exclusivamente relaciona-
das con la programación:
• Programador júnior: Es un programador con menos de tres años de expe-
riencia, normalmente realiza tareas de programación repetitiva o poco ima-
ginativas.
• Programador sénior: Tiene amplios conocimientos de los lenguajes de pro-
gramación, de los paradigmas de programación y de las tecnologías en
© Editorial UOC 135 Programación

general (por ej. protocolos, comunicación, bases de datos, estructuras de


datos, etc.).
• Analista-programador: Es el encargado de analizar un problema para
encontrar una solución automatizada. En otras palabras, es la persona
encargada de organizar el trabajo de programación a partir de los requisi-
tos del cliente.
• Director de proyecto: Es el encargado de planificar y controlar el proyecto.
Es la persona que tiene que dirigir el equipo (analistas, programadores,
diseñadores, etc.) y mantener la relación con el cliente para que el proyec-
to esté acabado y los objetivos se hayan cumplido en la fecha señalada.

10. Apéndice

10.1. Premios A. M. Turing

Son los premios que concede anualmente la ACM (Association for


Computing Machinery) a aquellas personas que han contribuido con excelen-
cia a la comunidad científica informática.
Esta es la lista de algunas de las personas premiadas en el ámbito de la pro-
gramación y la razón por la que se les concedió la mención:
• Alan J. Perlis. Por su influencia en el área de las técnicas avanzadas de pro-
gramación y construcción de compiladores.
• Edsger Dijkstra. Fue el principal desarrollador de ALGOL a finales de los
años cincuenta, un lenguaje de alto nivel que ha sido un modelo de clari-
dad y rigor matemático. Es uno de los principales exponentes de la ciencia
y el arte de los lenguajes de programación en general y ha contribuido a la
comprensión de su estructura y su implementación.
• Donald E. Knuth. Por su aportación al análisis de algoritmos y el diseño
de lenguajes de programación, y en particular por sus contribuciones al
“arte de la programación de ordenadores” a través de su serie de libros que
llevan ese título.
• Niklaus Wirth. Por desarrollar una serie de lenguajes innovadores: EULER,
ALGOL-W, MODULA y PASCAL.
© Editorial UOC 136 Escaneando la Informática

• Richard M. Karp. Por su contribución continuada a la teoría algorítmica


incluido el desarrollo de algoritmos eficientes por flujo de datos a redes y
otros problemas de optimización combinatorios. Por la identificación de
computabilidad polinomial en tiempo con las nociones intuitivas de efi-
ciencia algorítmica, y, muy notablemente, por la teoría de NP-completitud.
• Ole-Johan Dahl y Kristen Nygaard. Por sus ideas fundamentales para la
emergencia de la programación orientada a objetos, a través del diseño de
los lenguajes de programación Simula I y Simula 67.
• Alan Kay. Por ser el pionero de múltiples ideas para las bases de los lengua-
jes orientados a objetos, en calidad de director del equipo que desarrolló
Smalltalk.
• Peter Naur. Por sus contribuciones fundamentales al diseño de lenguajes
de programación y la definición de Algol 60, el diseño de compiladores y
el arte y la práctica de programación de ordenadores.

Otros programadores premiados han sido John Backus (1977), Robert W.


Floyd (1978), Kenneth E. Iverson (1979), C. Antony R. Hoare (1980), Stephen A.
Cook (1982), John Hopcroft y Robert Tarjan (1986), y Jures Hartmanis y Richard
E. Stearns (1993).
© Editorial UOC 137 Programación

10.2. Ejemplo de código de programación ofuscada en C


© Editorial UOC 253 Bibliografía

Bibliografía

Capítulo IV. Programación

Wirth, N. (1976) Algorithms + Data Structures = Programs, Prentice-Hall (Edició


espanyola: Algoritmos + Estructuras de Datos = Programas, Ediciones del Castillo,
1980)

Aho, A.; Ullmann, Jeffrey D. (1977) Compilers. Principles, Techniques and


Tools, Addison-Wesley

Aho, A.; Sethi, Ravi; Ullmann, Jeffrey D. (1986) Compilers. Principles,


Techniques and Tools, Addison-Wesley (Edició espanyola: Compiladores,
Principios, Herramientas y Técnicas, 1990)

Aho, A.; Hopcroft, John E.; Ullmann, Jeffrey D. (1988) Data Structures
and Algorithms, Addison-Wesley (Edició espanyola: Estructuras de Datos y
Algoritmos, 1988)
6LOHKDJXVWDGRHVWHFDStWXORSXHGHDGTXLULUHOUHVWRGHODREUD
HQODVSODWDIRUPDVKDELWXDOHV JRRJOH3OD\&DVDGHO/LEUR&DVDOLQL
/LEHUGUDFH/LEUR'DZVRQHUDHQXQIXWXURSUy[LPR$PD]RQHWF

Você também pode gostar