Sei sulla pagina 1di 128

C a p í t u 1 o

Software
de base

Temas y áreas de conocimiento del capítulo


Área general de conocimientos del capítulo:
5. Software de base
Sección 5.1 Lenguaje de máquina (subárea 3.3.1, AC19)
Sección 5.2 Primer nivel de lenguajes: ensamblador (subárea 5.1.1,
SB1)
Sección 5.3 Macroprocesadores (subárea 5.1.1, SB2-SB3)
Sección 5.4 Cargadores (subárea 5.3.2, SB 17)
Sección 5.5 Compiladores (subárea 5.1.2, SB4-SB5)
Sección 5.6 Sistemas operativos (subárea 5.2, SB8-SB10)
Sección 5.7 Utilerías: editores, hojas de cálculo, bases de datos
(subárea 7.1.2)
Sección 5.8 Inteligencia artificial (subárea 8.2)
176 Capítulo 5 Software de base

5 SOFTWARE DE BASE

Cuando se inicia a estudiar la computación "por dentro", como en este libro, inmediata-
mente salta a la vista una aparente paradoja: el hecho mismo de que un usuario utilice la
computadora para realizar alguna función cualquiera necesariamente implica que se esté
ya ejecutando otro programa "oculto", que le sirve como base operativa. Es decir, grosso
modo, los programas se dividen en dos clases: los utilizados en forma directa por los
usuarios —llamados programas de aplicación , y los requeridos como sustento para

que eso sea posible —los programas del sistema.


Así, el software de base (también conocido como "programación de sistemas" o
"software de sistemas") es el conjunto de programas necesario para que una computadora
pueda desempeñar trabajo útil y actuar sobre la realidad exterior, además de dar una imagen
coherente y monolítica ante sus usuarios.
Además, se ha visto ya que una máquina tan rápida como las descritas sólo es capaz
de hacer un reducido número de operaciones muy elementales, por lo que debemos
encontrar la forma de volver eficiente el trabajo con una computadora, sin tener que comu-
nicarle todo por medio de ceros y unos. La forma de lograrlo constituye, precisamente, el
desarrollo del software de base, que analizamos aquí.

5.1 LENGUAJE DE MÁQUINA

Todo lo descrito en el capítulo 2 ha estado en el lenguaje propio de la computadora, esto


es, en un lenguaje que únicamente hace referencia a los registros de la UCP, al acumulador,
a las celdas de la memoria, etc. Sin embargo, estos elementos de la arquitectura de la
máquina están bastante lejanos del lenguaje cotidiano, por lo que ahora se comenzará a
explorar las posibilidades de acercar la computadora al dominio del ser humano; es decir,
de subir el nivel de la comunicación.

Características de los programas


en lenguaje de máquina
Para que el lector pueda juzgar la claridad de los programas en lenguaje de máquina se
muestra el siguiente ejemplo real, escrito en el código del microprocesador Intel 8086,
que sirve para encontrar un número entre un conjunto de números enteros mediante el
método llamado de búsqueda lineal:

1E B80000 50 B82810 8ED8 SECO BF0000 BB1D00 8BOF BB1F00


8A07 FC F2 AE 7401 CB 4F CB

Cada grupo de números representa el código —en el lenguaje de esa máquina— de


una instrucción del procesador. Vea cómo, de acuerdo con lo dicho en la página 80, a veces
las instrucciones medirán un byte (dos dígitos hexadecimales), dos (cuatro dígitos) o tres.
Un programa escrito en lenguaje de máquina tiene las siguientes limitantes: está es-
crito en ceros y unos (aunque el ejemplo está en hexadecimal, para un mínimo de mayor
claridad); hace referencia a celdas absolutas de la memoria y es inflexible, en el sentido
de que no admite cambios para adaptarlo a nuevos requerimientos. Los programas objeto
exhiben esas desagradables propiedades, que los vuelven totalmente ilegibles e imprácticos
para todo fin humano (por ejemplo, mejorarlos, corregirlos, comentarlos, comunicarlos a
otros, etcétera).
Sección 5.1 Lenguaje de máquina 177

Esto implica que nadie trabaja directamente en lenguaje de máquina, porque es la ma-
nera más inadecuada de comunicarse con una computadora. Debemos encontrar un mejor
medio. La primera idea que surge es usar el diccionario de equivalencias definido en el
capítulo 2 (a partir de la página 80) y decir a la computadora CARGA, en lugar de 20, con las
correspondientes ventajas en cuanto a legibilidad y claridad en el concepto; esto se estu-
diará con más detalle en la siguiente sección, aunque primero es necesario ampliar algu-
nos conceptos ya definidos.

Más sobre modos de direccionamiento


Como se indicó antes, los procesadores tienen varias formas de "direccionar" (llegar a)
las celdas de memoria que contienen los datos requeridos por una instrucción. El más
simple de todos es el direccionamiento inmediato, en donde la operación se aplica al
dato numérico escrito inmediatamente a la derecha de la instrucción (por motivos de cla-
ridad escribiremos las instrucciones en forma simbólica SUMA y no en código —30—).
— —

Por ejemplo,

SUMA i 27
-

añade el número 27 al acumulador.


La forma general de las instrucciones con direccionamiento inmediato es:

Op (valor)

porque la operación (o instrucción) se aplica en forma inmediata al valor (o dato) a su


derecha. En nuestra máquina, las instrucciones de modo inmediato tienen una longitud de
2: una celda (o byte) para el código de la instrucción y otra para el dato.
Por otro lado, en el direccionamiento directo, después del código de la instrucción
se escribe la dirección del valor al que se aplica la operación.
Las instrucciones de este tipo funcionan así:

Op (direcc (valor))

y también tienen una longitud de 2, aunque son más caras en tiempo de ejecución, pues se
requiere efectuar un segundo acceso a la memoria para ir a la dirección especificada y
extraer de allí el dato requerido para la operación, con lo cual aumenta la cantidad de
micropasos necesarios para completarla.
La diferencia entre el direccionamiento inmediato y el directo consiste en que el
primero usa el dato mismo, mientras que el segundo usa la dirección donde se encuentra
el dato, como se ha dicho y visto en varios ejemplos ya.
Es decir,

SUMA 27

añade al acumulador el valor contenido en la celda con dirección 27, no el valor 27.
El direccionamiento indirecto, por otra parte, no toma el número que está a la derecha
de la instrucción como dato inmediato, ni como dirección para extraer de allí un valor,
sino como la dirección de una celda a la que habrá que ir para extraer de allí otra dirección
que, por último, lleve al valor deseado. Esto puede parecer rebuscado, pero es de enorme
utilidad en la programación en general.
La forma general de una instrucción indirecta es

Op (direccl (direcc2 (valor) ) 1


178 Capítulo 5 Software de base

que sigue teniendo una longitud de 2, aunque es mucho más cara que las anteriores en
tiempo de ejecución, porque se requiere efectuar dos accesos adicionales a la memoria: ir
a la primera dirección especificada y extraer de allí la segunda dirección, que finalmente
llevará al valor requerido por la operación. La cantidad de micropasos necesarios para
completar la instrucción aumentó considerablemente, pero se obtiene la capacidad de
"seguirle la pista" a direcciones variables mediante un indicador que no cambia de posi-
ción, como se verá más adelante.
De esta forma, el CARGA indirecto

CARGA-ind <direcc>

tiene longitud 2, y se le asignará el código 22.


Por ejemplo, supóngase que la memoria tiene los siguientes contenidos en las celdas
indicadas:

1001 1002 1003 ••• 2951

Entonces la instrucción

CARGA- ind 2951

irá a la celda 2951, que es interpretada como la direcciónl, y extraerá de allí su conteni-
do: la dirección2 en donde se encuentra el valor a ser cargado en el acumulador. En el
ejemplo, la dirección 2951 lleva a la 1001, en donde está el valor a ser cargado en el acumu-
lador: 23.
La potencia de esto se vuelve evidente si se incrementa en uno el valor de la dirección2
contenida en la celda 2951, porque entonces una nueva ejecución de la misma instrucción
de carga indirecta llevará ahora al valor almacenado en la siguiente celda (1002), y así
sucesivamente.
Por último, el direccionamiento indizado toma la dirección que está a la derecha de
la instrucción y la suma con el contenido de un registro especial de la UCP llamado registro
índice. Al igual que la modalidad anterior, esto permite la variación de direcciones para
realizar un recorrido sobre los elementos de un conjunto de datos:

Op ( (direcc + Registro índice) valor)

Existen muchos otros modos de direccionamiento diferentes, aunque la máquina que


"construimos" en el capítulo 2 sólo emplea los tres primeros. Para más referencias, con-
súltese por ejemplo [MACA95].

5.2 PRIMER NIVEL DE LENGUAJES: ENSAMBLADOR

Acabamos de plantear la idea de usar el diccionario de equivalencias para comunicarnos


con la computadora, y esto nos obliga a resolver el problema de traducir el programa
fuente (escrito con los nombres simbólicos mnemónicos allí definidos) al lenguaje
— —

de máquina (el único que la UCP admite).


Sección 5.2 Primer nivel de lenguajes: Ensamblador 179

Se podría pensar en contratar a un traductor (que llamaremos T i) para que lea cada
programa fuente y lo traduzca a lenguaje de máquina. Con un traductor así ya no sería
necesario trabajar en lenguaje de máquina, pues programaríamos en un lenguaje de más
alto nivel. Para que esto siempre funcionara habría que encontrar la forma de integrar el
traductor a la máquina misma y hacer que la computadora traduzca por sí sola los progra-
mas fuente a programas objeto.
¿Cómo se logra este paso fundamental? Ahora
Si el traductor se convierte en un programa y se deja residente en la memoria de la la comunicación
computadora, el proceso de comunicación con ella tendría dos pasos: primero, convertir con la computadora
el programa fuente a programa objeto y, segundo, llevar a la memoria y ejecutar ese pro- requiere dos pasos
grama objeto, que ya quedó traducido a lenguaje de máquina.

El traductor ensamblador
Analizaremos los pasos necesarios para construir un traductor de esta clase. Se propone
una manera sencilla de atacar problemas complejos, consistente en describir en español, a
grandes rasgos, una solución general. Esa solución será un primer acercamiento al proble-
ma, y seguramente harán falta varios acercamientos progresivos para entender y resolver
una situación compleja, aunque por lo menos ya se ha descrito una forma general de
lograrlo. Más aún, esto constituye una metodología de diseño que debe explorarse en cursos
posteriores. Ahora, volvamos a nuestro asunto.
Se desea hacer un programa T i que reciba como entrada un programa fuente escrito Diseño inicial
en mnemónicos (a partir del diccionario de equivalencias entre los códigos de instrucciones de un primer
y sus nombres mnemónicos), y produzca como salida el mismo programa, convertido ya traductor
a lenguaje de máquina listo para ser ejecutado.
Un primer acercamiento podría ser el siguiente:

! Programa "T 1", primera versión.


! Traductor de lenguaje de mnemónicos a lenguaje de máquina.
! (El símbolo "!" se usa para escribir comentarios.)
Para cada renglón del programa fuente se ejecuta lo siguiente:
Buscar la palabra mnemónica en el diccionario de equivalencias.
Si está, entonces traducirla a lenguaje de máquina
(es decir, reemplazarla por la columna de la derecha de esa entrada en el
diccionario).
En caso contrario mandar un mensaje de error que diga, por ejemplo,
"mnemónico desconocido".
Fin del programa T i .

En este nivel de detalle, el programa T i funciona. Pruebe el lector aplicarlo al ejemplo


de la página 80 y verá cómo pasa del programa fuente

CARGA 20
SUMA 21
GUARDA 22
ALTO

al programa objeto equivalente

20 20 30 21 02 22 70

suponiendo, claro, que el traductor T, tiene acceso al diccionario de equivalencias.


180 Capítulo 5 Software de base

Si por error el segundo renglón dijera, por ejemplo,


SOMA 21

entonces el traductor se quejaría, indicando que el mnemónico "SOMA" no está definido en


el diccionario, y por ende es intraducible y constituye un error en el programa fuente.

El lenguaje ensamblador
Siguiendo por este camino, aparece la posibilidad de no tener ya que preocuparse por
escoger celdas particulares de memoria, sino dejar esta responsabilidad al propio traduc-
tor. Esto es, eximir al programador de la tarea de escoger celdas de memoria y asignársela
al traductor T i . Es en este momento cuando debe introducirse el concepto de variable.
Una variable será un nombre simbólico asociado con una celda cualquiera de la memoria
de la computadora; sólo que el traductor hará esta asociación de manera automática.
Así, en lugar de decidir si se cargará el acumulador con la casilla 21 (o, si fuera el
caso, con la 2951 o la 19689, etc.), se escribirá
CARGA ALFA

donde ALFA es el nombre simbólico de una celda de memoria (y ya no importa cuál será
ésta). Mientras el traductor T i reconozca que ALFA corresponde a una celda en particular
(escogida por el traductor mismo) no habrá problemas. Nosotros, como programadores,
diremos ALFA y el traductor le dirá a la computadora 21 (o, si fuera el caso, 2951 o 19689,
etcétera).
Éste es un paso de fundamental importancia; lograrlo implica estar, efectivamente,
Independencia "por encima" de la memoria, al no tener ya que preocuparnos por direcciones absolutas.
de las direcciones de Es decir, el programador trabajará en un ambiente simbólico, no absoluto. ¿Cuáles son las
la memoria: ventajas de "flotar" por encima de la memoriat? En primer lugar, comenzar el largo camino
el concepto de
de liberarse de la computadora, y dirigirse a ella en un lenguaje más simbólico que antes.
Además, los programas son mucho más flexibles pues, por ejemplo, si la celda 21 está
variable simbólica ocupada por código de otro usuario, el traductor T1 podrá asignar ALFA a cualquier otra
celda libre. Todo esto permite la creación de programas más legibles para un ser humano
y, por tanto, más útiles.
El programa, entonces, dirá:
CARGA ALFA
SUMA BETA
GUARDA GAMA
ALTO

que ya es algo más parecido a lo que realmente se desea hacer:


GAMA = ALFA + BETA

en donde, por ejemplo, ALFA vale 5 y BETA vale 7.

(t) Es obvio que los programadores de sistemas siempre tienen presente este verso del poeta
español Antonio Machado (1875-1939):
f...1
yo amo los mundos sutiles,
ingrávidos y gentiles
como pompas de jabón.
Sección 5.2 Primer nivel de lenguajes: Ensamblador 181

Veamos qué cambios habrá que hacer a T I para que por sí solo pueda escoger y asignar
celdas de memoria a las variables simbólicas. En primer término, deberá guardar en una La tabla de símbolos
tabla las direcciones absolutas que asignó a las variables simbólicas, para poderlas luego
reconocer cuando sean requeridas. Esta tabla, de uso interno del traductor, se conoce como
tabla de símbolos.
Por ejemplo, si se desea elaborar un programa para calcular C = A + B y luego D = E — C,
se escribiría algo como:

CARGA A
SUMA
GUARDA C
CARGA E
RESTA C
GUARDA D
ALTO

Además, el traductor debe reconocer las direcciones de todas las variables simbólicas
ya que, por ejemplo, C es requerida dos veces a lo largo del programa.
Si se supone que T 1 decidió asignar celdas de memoria a partir de la dirección 70
(aunque pudo haber usado cualquier otro número), la tabla de símbolos para este progra-
ma será:

Variable Dirección
simbólica asignada
A 70
B 71
C 72
E 73
D 74

Con lo que la traducción realizada debe ser:

Programa fuente Programa objeto obtenido


1 CARGA A 20 70
2 SUMA B 30 71
3 GUARDA C 02 72
4 CARGA E 20 73
5 RESTA C 33 72
6 GUARDA D 02 74
7 ALTO 70

Obsérvese que, como la variable C aparece dos veces en el programa fuente, su direc-
ción absoluta correspondiente (72) también aparece dos veces. Sin embargo, no hay que
confundir las dos apariciones del número 70. La primera vez que aparece es porque repre-
senta la dirección que el traductor asignó a la variable A, mientras que la segunda representa
la codificación (de acuerdo con nuestro diccionario de equivalencias) de la instrucción
ALTO.
Cuando el traductor T 1 está leyendo el renglón fuente número 3 se encuentra por
primera vez con la variable C, por lo que la introduce en la tabla de símbolos y la reemplaza
en el código objeto por la dirección absoluta 72 recién asignada. Cuando la encuentra de
nuevo, en el renglón 5, no la introduce en la tabla, pues ya está allí; ahora simplemente la
reemplaza en el código objeto por su dirección, 72.
182 Capítulo 5 Software de base

Falta por resolver algunos pequeños problemas técnicos, porque por lo pronto con
ligereza hemos supuesto que el traductor puede por sí mismo darse cuenta de la existencia
de las variables empleadas por el programador, pero en ningún momento le fueron defini-
das. Tampoco se dijo nunca a partir de cuál dirección se desea comenzar a asignar celdas
de memoria; es decir, no se ha sustentado aún la intención de comenzar a "virtualizar"
nuestra forma de comunicación con la computadorat.
Para subsanar la primera omisión, inicialmente el programador debe avisar al traduc-
Comunicación tor TI cuáles variables decidió emplear, aunque —como se dijo— sí le delegará la respon-
con el traductor: las sabilidad de asignarles los números de celda específicos. Para ello es necesario inventar el
pseudoinstrucciones concepto de pseudoinstrucción: una instrucción que el programador escribe para el tra-
ductor, no para la máquina.
Para definir una variable se propone la pseudoinstrucción DATO, a la que debe prece-
der el nombre simbólico que el programador escogió para ella. Además, para propósitos
de generalidad, conviene indicar la cantidad de celdas de memoria que ocupará (normal-
mente será una, pero más adelante se verá un caso con más).
Por ejemplo, la pseudoinstrucción

ALFA: DATO 1

especifica que la variable ALFA ocupa una celda de memoria, pero se sigue suponiendo
que el traductor conoce la dirección a partir de la cual debe asignar variables, lo cual
tampoco es cierto.
Para cubrir la segunda omisión se propone la pseudoinstrucción ORIGEN, seguida de
la dirección física inicial que el programador desea que el traductor emplee.
Con todo esto en mente, el programa anterior debe entonces escribirse así:

ORIGEN 70
A: DATO 1
B: DATO 1
C: DATO 1
D: DATO 1
E: DATO 1
PROGRAMA
CARGA A
SUMA
GUARDA
CARGA E
RESTA
GUARDA
ALTO
FIN

paraque el código objeto generado por el traductor (ahora sí, en forma verdaderamente
automática y sin suposiciones ligeras) sea

70: 00 00 00 00 00
75: 20 70

(t) Recuérdese que ésta es la función última del software de base: virtualizar el modo de comuni-
cación entre la computadora y nosotros, para acercarnos y tratar de aligerar la convivencia. Pero
eso no es fácil, aunque intentarlo bien vale la pena.
Sección 5.2 Primer nivel de lenguajes: Ensamblador 183

77: 30 71
79: 02 72
81: 20 73
83: 33 72
85: 02 74
87: 70

tal como se había dicho. Para propósitos visuales se incluyen las direcciones, aunque debe
quedar claro que éstas no forman parte del código del programa objeto. Las primeras
cinco celdas (a partir de la dirección 70) contendrán datos aún no especificados, y se
presuponen valores iniciales de cero. La primera instrucción ejecutable está en la direc-
ción 75, que aparece subrayada para mayor claridad.
Obsérvese que se emplearon dos nuevas pseudoinstrucciones, PROGRAMA y FIN, sim-
plemente para indicar dónde comienzan y terminan las verdaderas instrucciones del pro-
grama fuente.
Para continuar con el tema del lenguaje simbólico, debemos ocupamos del manejo de
las etiquetas: referencias simbólicas a direcciones del programa, empleadas para dirigir los Manejo adicional
saltos condicionales y modificar el flujo de ejecución dependiendo de los valores que tomen de direcciones
ciertas variables. Como se vio en el capítulo 2, la máquina puede comparar dos números simbólicas: etiquetas
y determinar cuál es mayor; luego, con esta información resulta posible decidir cuál parte
del programa debe ejecutarse a continuación, "brincándose" partes del código.
En los programas escritos en lenguaje de máquina, las direcciones a las que se puede
saltar deben ser especificadas en forma absoluta, es decir, mediante un número que indica
la dirección física de la celda deseada. Por el contrario, en los programas escritos en el
nuevo lenguaje simbólico que estamos desarrollando, esas direcciones pueden especificarse
empleando etiquetas que apuntan a direcciones físicas, pero que en sí mismas son nombres
simbólicos y no números absolutos.

Un primer programa escrito en ensamblador


Por ejemplo, cuando se toma el programa del capítulo 2 para encontrar el máximo de tres
números leídos (pág. 91) y se expresa ahora con pseudoinstrucciones, instrucciones mne-
mónicas, variables y etiquetas, se tendrá algo como:

! Búsqueda del máximo


ORIGEN 3000
ALFA: DATO 1
BETA: DATO 1
GAMA: DATO 1
MAX: DATO 1
PROGRAMA
IN Leer el primer número
GUARDA ALFA Almacenarlo
IN Leer el segundo
GUARDA BETA Almacenarlo
IN Leer el tercero
GUARDA GAMA Almacenarlo
CARGA ALFA Suponer que el primero
GUARDA MAX es el máximo
COMP BETA Comparar lo. vs. 20.
BR> ETIQ1
CARGA BETA Reemplazar cuando el 2o. fue mayor
GUARDA MAX
184 Capítulo 5 Software de base

ETIQ1: COMP GAMA Comparar mayor vs 3o.


BR> ETIQ2
CARGA GAMA 1 Reemplazar cuando el 3o. fue mayor
GUARDA MAX
ETIQ2: OUT Mostrar el máximo
ALTO
FIN

Esta nueva versión del programa es más ligera y mucho más legible, porque emplea
nombres simbólicos tanto para las instrucciones (en forma de mnemónicos), como para
las celdas (en forma de variables), y para las direcciones de los saltos (en forma de etique-
tas); además, no deja nada imprevisto, porque utiliza pseudoinstrucciones y define todo lo
necesario para la posterior traducción.
Es decir, gracias a este nuevo traductor (que aún falta diseñar con mayor detalle) es
posible usar los siguientes recursos adicionales para auxiliar en la larga tarea de virtua-
lización de los programas fuente:

Pseudoinstrucciones para definir condiciones iniciales en el proceso


de la traducción
Mnemónicos en lugar de códigos de instrucciones
Variables en lugar de celdas por número
Etiquetas en lugar de direcciones numéricas como destino
para los saltos

(En realidad las definiciones de variables y las etiquetas para saltos son dos aspectos
de un mismo concepto: el de dirección simbólica, aunque desempeñan un papel ligera-
mente diferente dentro del programa fuente. De hecho, ambas son etiquetas —aparecen
en la parte izquierda del renglón y terminan con dos puntos—, sólo que sus variables no se
refieren a instrucciones dentro del programa ejecutable, sino a las direcciones simbólicas
de celdas que contienen datos.) Con todo lo anterior se dispone ya de mayores facilidades
para escribir programas fuente (es decir, simbólicos) aunque, por supuesto, la computadora
es incapaz de entenderlos directamente sin antes haberlos traducido, por lo que nueva-
mente enfocamos la atención al traductor.
El desarrollo del nuevo lenguaje para comunicación con la computadora ha atravesado
por varias fases, que necesariamente se ven reflejadas en el diseño del traductor: la pri-
mera versión de T 1 sólo era capaz de traducir mnemónicos (utilizando el diccionario de
equivalencias), y pronto se determinó la conveniencia de manejar variables simbólicas,
declaradas inicialmente por el programador mediante etiquetas asociadas con la
pseudoinstrucción DATO (y con otras que luego se verán). Como se dijo, esto último obliga
a que el traductor almacene esas etiquetas en la tabla de símbolos, para de ahí en adelante
sustituir la dirección absoluta en el lugar del nombre simbólico de esa variable.
Continuando con el desarrollo, se definieron direcciones simbólicas para los posibles
saltos dentro de un programa, indicados mediante etiquetas asociadas con la instrucción a
la cual se desea llegar. Esto implica que el traductor también deberá guardarlas en la tabla
de símbolos, para de allí en adelante sustituir las direcciones absolutas en lugar de esas
referencias simbólicas.
Sólo que, a diferencia del caso de las etiquetas que representan variables, sí es posible
(y muy útil, además) hacer referencia a una etiqueta que indica el destino de un salto antes
de haberla declarado. Es decir, las variables simbólicas primero se declaran y luego se
usan, pero las etiquetas de destino para los saltos pueden estar antes o bien después de
donde se emplean.
Sección 5.2 Primer nivel de lenguajes: Ensamblador 185

Por ejemplo, en el siguiente fragmento de programa, la etiqueta ARRIBA se declara


primero y se usa después, pero no sucede así con la etiqueta ABAJO, porque la primera se
empleó para saltar hacia atrás y la segunda para un salto hacia adelante.

ALFA: DATO 1

ARRIBA: IN
GUARDA ALFA
COMP-i 0
BR= ABAJO

BR ARRIBA
ABAJO: OUT

La existencia de las etiquetas obliga al traductor a efectuar dos "pasos" (lecturas)


sobre el texto del programa fuente: el primero para guardar las definiciones de todas las Necesidad
etiquetas en la tabla de símbolos (y además llevar cuenta de la dirección actual para hacer- de dos pasos
la corresponder con las nuevas etiquetas), y el segundo para reemplazar las referencias a
ellas por las direcciones donde se encontraron, y generar el código objeto del programa.
De no ser así, cuando en el programa anterior el traductor se encontrara con la ins-
trucción BR= ABAJO marcaría un mensaje de error ("Referencia a etiqueta desconocida")
prematuro e inválido, pues aún no ha terminado de leer todos los renglones del programa
y por tanto no puede juzgar si realmente existe o no la definición de esa etiqueta.
Si sólo existieran saltos hacia arriba, únicamente sería necesario un paso, porque en
el programa fuente siempre aparecerían primero las declaraciones de las etiquetas y luego
se usarían. Pero, por supuesto, dentro de un programa pueden emplearse saltos hacia arriba y
hacia abajo, según sea conveniente, porque tan importantes y valiosos son unos como otros:
los saltos hacia abajo sirven para rodear partes del programa y no ejecutarlas, y los saltos
hacia arriba sirven para repetir la ejecución de grupos de instrucciones y crear ciclos.
Ahora se verá el panorama completo, porque nuestro lenguaje consta de mnemónicos,
direcciones simbólicas (en forma tanto de variables como de etiquetas para saltos) y pseudo-
instrucciones. El traductor debe entonces ser capaz de manejar todo esto para producir
finalmente lenguaje de máquina.
El nuevo acercamiento será:

! Programa T 1 , segunda versión


! —Primer paso—
Para cada renglón del programa fuente ejecutar lo siguiente:
Si existe una pseudoinstrucción ORIGEN y un número,
considerarlo como la dirección actual e inicial.
Si existe una definición de etiqueta, buscarla en la tabla de símbolos,
esperando que no esté;
si ya está en la tabla, marcar error: etiqueta repetida; en otro caso, darla de
alta en la tabla de símbolos.
Si la etiqueta acompañaba a una pseudoinstrucción DATO seguida de un
número, asignar la dirección actual a esa variable en la tabla de símbolos y
sumar el número a la dirección actual.
186 Capítulo 5 Software de base

Si luego de la etiqueta aparece un mnemónico, añadir su longitud a la


dirección actual.
Si existe la pseudoinstrucción FIN, termina el primer paso
! —Segundo paso—
Regresar al primer renglón después de la pseudoinstrucción PROGRAMA.
Para cada renglón del programa fuente ejecutar lo siguiente:
Ignorar la posible definición de etiqueta que contenga.
Buscar la instrucción mnemónica en el diccionario de
equivalencias.
Si está, escribir su código en el programa objeto;
en otro caso, marcar error: mnemónico desconocido.
Si a continuación el renglón contiene una variable simbólica, buscar su
dirección en la tabla de símbolos;
si está, escribir esa dirección en el programa objeto;
en otro caso, marcar error: variable (o etiqueta)
desconocida.
Si existe la pseudoinstrucción FIN, termina el proceso.
Fin del programa T I .

En este momento ya es posible llamar al traductor T 1 por su nombre: ensamblador.


El ensamblador Un ensamblador es, entonces, un traductor que además asigna direcciones absolutas a las
variables simbólicas escogidas por el programador, liberándolo de esa tarea. Como se
sugirió por los títulos empleados, el lenguaje simbólico con el que se escriben los progra-
mas también se llama ensamblador, y mediante el contexto de la frase se distinguirá cuando
se está hablando del traductor y cuando se trata del lenguaje.
Aplicando el procedimiento de traducción al programa de búsqueda del máximo
expuesto unas páginas antes, el ensamblador genera en forma interna la siguiente tabla de
símbolos
Nombre simbólico Dirección
ALFA 3000
BETA 3001
GAMA 3002
MAX 3003
ETIO1 3025
ETIQ2 3033

Además, se produjo el siguiente código objeto, que el lector debe comprobar efectuan-
do la traducción manualmente:

3000: 00 00 00 00
3004: 10
3005: 02 3000
3007: 10
3008: 02 3001
3010: 10
3011: 02 3002
3013: 20 3000
3015: 02 3003
3017: 40 3001
3019: 45 3025
3021: 20 3001
Sección 5.2 Primer nivel de lenguajes: Ensamblador 187

3023: 02 3003
3025: 40 3002
3027: 45 3033
3029: 20 3002
3031: 02 3003
3033: 11
3034: 70

Como las primeras cuatro celdas a partir de la dirección 3000 contendrán datos aún
no especificados, se presuponen valores iniciales de cero. La dirección de la primera
instrucción ejecutable es 3004. Obsérvese cómo las instrucciones BR> ETIQ1 y BR> ETIQ2
se traducen como 45 3025 y 45 3033, respectivamente, porque ETIQ1 es el nombre simbólico
de una dirección que resultó ser la 3025, y ETIQ2 corresponde a la dirección 3033. El
lector no debe sentirse mal si no entiende el programa objeto, ...porque confiamos que es
una persona y no una máquina.
Con este diseño terminan nuestros esfuerzos iniciales por elevar el nivel de la comuni-
cación con la computadora. Como aún nos faltan técnicas de estructuración de programas
debemos conformarnos con esa rebuscada receta, aunque en realidad no es tan complicada.

Características adicionales del lenguaje ensamblador


Gracias a las pseudoinstrucciones es posible establecer comunicación con el ensamblador
antes de la traducción, por lo cual en principio cualquier directiva razonable puede ser válida. Más
Otra pseudoinstrucción muy común y útil establece equivalencias entre nombres simbó- pseudoinstrucciones
licos y valores, lo cual permite al programador definir constantes simbólicas y emplearlas
luego a su conveniencia. Para ello proponemos la nueva pseudoinstrucción EQU:

DIEZ: EQU 10
NUMERO: EQU 915

Con estas definiciones, por ejemplo, el ensamblador deberá reemplazar la palabra


DIEZ por el valor 10 en cualquier lugar del programa fuente donde aparezca, y lo mismo
sucede con la otra.
Otra directiva común sirve para separar una celda de memoria y asignarle un valor,
con lo cual se pasa al ensamblador la tarea de definir valores iniciales para las variables
que así lo requieran. Para ello se propone la pseudoinstrucción VALOR. Por ejemplo
ALFA: VALOR 30

asigna la dirección actual a la celda llamada ALFA y además le da el valor 30.


Como antes, durante el paso 1 de la traducción, el ensamblador deberá reconocer las
etiquetas, guardarlas en la tabla de símbolos y actuar sobre ellas de acuerdo con lo indicado
por la pseudoinstrucción. Como las nuevas pseudoinstrucciones especifican valores inicia-
les, éstos también deberán ser almacenados en la tabla de símbolos para su posterior uso
durante la traducción.
Así, la traducción del siguiente fragmento de programa fuente
INICIO: EQU 7000
LÍMITE: EQU 8
ORIGEN INICIO
ALFA: DATO 1
BETA: VALOR LÍMITE + 2
188 Capítulo 5 Software de base

produce la tabla de símbolos ampliada (pues ahora tiene una tercera columna para los
posibles valores durante la traducción) que se muestra a continuación:

Nombre simbólico Dirección Valor


INICIO 7000
LÍMITE 8
ALFA 7000 ---
BETA 7001 10

Es muy importante observar que, a diferencia de la pseudoinstrucción DATO, la direc-


tiva EQU no separa celdas de memoria, sino que únicamente le sirve al ensamblador para
posibles posteriores reemplazos textuales (es decir, para efectuar la sustitución del 7000
en lugar del texto "INICIO", por ejemplo).
Nótese también que el traductor ensamblador debe poder efectuar operaciones
aritméticas sencillas al momento de las sustituciones textuales, como cuando asignó el
valor 10 a la celda BETA, porque realizó la suma especificada como "LÍMITE + 2", donde
LÍMITE previamente había sido reemplazado por el valor 8.
Hay que ser cuidadosos y darse cuenta de que aún no se está ejecutando el programa
objeto y apenas se está traduciendo el fuente; téngase en mente que la traducción y la ejecu-
ción son dos procesos independientes, aunque el segundo sólo es posible a partir del primerot.
Por último, se propone la nueva pseudoinstrucción LISTA, que asigna a su etiqueta el
inicio del conjunto de valores especificado a continuación, como en

FACTOR: EQU 6
ORIGEN 1000
NÚMEROS: LISTA 7,21,14,6,99
ZETA: DATO 3
ALFA: VALOR FACTOR * 5

En este caso, primero se está indicando al ensamblador un sinónimo para el valor 6;


luego se le pide separar cinco celdas de memoria a partir de la dirección 1000, asignarles
los valores especificados y conocer la primera de ellas con el nombre NÚMEROS; después,
que asigne a la variable ZETA la primera de tres celdas (sin valor inicial). Con la última
pseudoinstrucción debe separar una celda para la variable AL FA y asignarle el valor inicial
de 6 * 5 (o sea, 30). La tabla de símbolos en este nuevo y elaborado caso será:

Nombre simbólico Dirección Valor


FACTOR 6
NÚMEROS 1000 7
1001 21
1002 14
1003 6
1004 99
ZETA 1005
1006
1007
ALFA 1008 30

(f) Como todo esto pudiera resultar confuso inicialmente, convendría no perder de vista la gran dife-
rencia existente entre, por ejemplo, traducir del francés al español una receta para cocinar crépes, y el
hecho de prepararlas y comérselas. Para traducir el texto de la receta se requiere ...un traductor, mientras
que para cocinarlas se necesita un agente procesador, en estos casos mejor conocido como chef
Sección 5.2 Primer nivel de lenguajes: Ensamblador 189

No se debe quedar satisfecho si no se ha entendido bien porqué la tabla de símbolos


es precisamente como se muestra. Y si ya se comprendieron esas razones, entonces resulta
fácil saber que el ensamblador no tendrá problema alguno para traducir instrucciones con
referencias simbólicas complejas, porque todos los casos se resuelven simplemente
siguiendo el proceso de ensamblado ya discutido, consistente en generar el código objeto
—durante el segundo paso— consultando la tabla de símbolos y reemplazando las referen-
cias simbólicas por sus equivalentes direcciones absolutas.
Así, por ejemplo, la instrucción

COMP NÚMEROS + 2

produce el código

40 1002

porque la dirección de la variable NÚMEROS es 1000. Cuando la instrucción se ejecute,


se comparará el contenido del acumulador contra el valor 14 almacenado en la celda
1002.
Similarmente, con

GUARDA ZETA + 1

se genera el código ejecutable

02 1006

para depositar lo que en ese momento contenga el acumulador en la dirección ZETA + 1, es


decir, en la celda 1006.
Con todo lo anterior, procederemos ahora a escribir más programas en ensamblador
para resolver otros casos de estudio.

Almacenamiento de conjuntos de números


Aunque todavía no estudiamos programación (asunto que se verá en el aún lejano capí-
tulo 7), sí ha sido necesario involucramos en algunos de sus aspectos mínimos, y ahora
llegó el momento de definir una primera estructura de datos (es decir, una forma de repre-
sentar datos en la memoria de la computadora), útil para almacenar conjuntos de núme-
ros, llamada vector o arreglo, y que ya fuera mencionada brevemente en la página 78. La
principal virtud del concepto de vector es su capacidad de referirse a un conjunto de
valores con un solo nombre, y diferenciar entre sus elementos mediante sus posiciones
relativas al inicio. Por ejemplo, con el siguiente fragmento de programa se define un arre-
glo de cinco celdas de memoria (sin especificar sus valores):

LONG: EQU 5
ORIGEN 1000
ARR1 : DATO LONG

La primera se llama ARR1, la segunda ARR1+1 , , y la quinta ARR1+4 .


También se pueden definir valores iniciales, mediante este nuevo fragmento, cuya
posterior representación de memoria aparece en la figura:

ORIGEN 1000
ARR2 : LISTA 700,710,720,730,740
190 Capítulo 5 Software de base

1000 1001 1002 1003 1004

Aquí, por ejemplo, la celda ARR2 + 3 contiene el valor 730, por lo que con las
instrucciones

CARGA ARR2 + 3
OUT

se puede mostrar su contenido por la unidad de salida. Si ahora se deseara escribir un


programa para desplegar los contenidos de todas las celdas se podría simplemente escribir

CARGA ARR2
OUT
CARGA ARR2 + 1
OUT

CARGA ARR2 + 4
OUT

pero, además de largo y poco elegante, el programa sería totalmente específico para cinco
celdas. El caso de, por ejemplo, quince celdas no se podría tratar con el mismo programa,
no obstante ser casi idéntico.
La principal desventaja de este acercamiento es que el tamaño de la solución depende
Consideraciones al linealmente del tamaño particular de cada aparición del problema, por lo cual no se puede
margen sobre ciencia realmente decir que sea una solución completa. Para que sea general debe considerar los
aspectos estructurales, desde una perspectiva que haga abstracción de los casos particu-
lares; es decir, debe ser un modelot, que se preocupe por determinar la estructura de lo
que se estudia, y no por un caso en particular. La ciencia (y la técnica) se guían por
consideraciones e inferencias de tipo universal, matematizable, no particular; es decir
tratan siempre de establecer las premisas que describan el conjunto universal de ejemplos
del fenómeno que se analiza.
Para nuestro ejemplo, deberá definirse la mecánica para la detección del valor de
Direccionamiento una celda cualquiera de la memoria, sin preocuparnos por cuál sea, y para ello se utili-
i ndirecto zará una instrucción CARGA con modo de direccionamiento indirecto, porque en éste se
emplea una celda fija de referencia cuyo contenido puede ser variable. Y si ese conteni-
do, a su vez, es la dirección de la celda actual, entonces tendremos una verdadera solución
general.
Es decir, inicialmente se llevará la primera dirección a una celda fija de referencia;
luego se hará el acceso indirecto y después se incrementará en uno esa dirección, para
entonces repetir el ciclo de acceso e incremento de la dirección tantas veces como sea
necesario (para lo cual se emplea un contador especial).

(t) El concepto de modelo es de vital importancia en la ciencia. En el capítulo 6 se trata un poco


más el tema.
Sección 5.2 Primer nivel de lenguajes: Ensamblador 191

! Programa general para mostrar contenidos de celdas


CELDAS: EQU 5
INICIO: EQU 1000
ORIGEN INICIO
ARR2: LISTA 700,710,720,730,740
BASE: DATO 1
CUENTA: VALOR 1
PROGRAMA
CARGA-i INICIO
GUARDA BASE
! Se hace la lectura indirecta a la celda actual
CICLO: CARGA-ind BASE
OUT
! Se incrementa la dirección de referencia
! para apuntar a la siguiente celda
CARGA BASE
INCR
GUARDA BASE
! Se incrementa la cuenta
CARGA CUENTA
INCR
GUARDA CUENTA
! Averiguar si hay más celdas
COMP-i CELDAS
BR<= CICLO
ALTO
FIN

Esta es la tabla de símbolos producida por el traductor ensamblador cuando procesó


el programa fuente:

Nombre simbólico Dirección Valor


CELDAS 5
INICIO 1000
ARR2 1000 700
1001 710
1002 720
1003 730
1004 740
BASE: 1005
CUENTA 1006 1
CICLO 1011

Y este es el código objeto generado, que inicia en la dirección 1000 con siete celdas
de datos (el sexto de los cuales no está especificado y se supone como cero). La primera
instrucción ejecutable está en la dirección 1007:

1000: 700 710 720 730 740 00 01


1007: 21 1000
1009: 02 1005
1011: 22 1005
1013: 11
192 Capítulo 5 Software de base

1014: 20 1005
1016: 32
1017: 02 1005
1019: 20 1006
1021: 32
1022: 02 1006
1024: 41 05
1026: 48 1011
1028: 70

Solicitamos al estudiante hacer la simulación detallada de la ejecución del programa:


apunte en una hoja los sucesivos contenidos de las celdas de memoria que cambian y del
acumulador. Para propósitos de verificación, éstos son los diferentes contenidos del acu-
mulador a lo largo de toda la ejecución (se subraya lo que aparece por la unidad de salida
como resultado de las cinco operaciones OUT):

1000 700 1000 1001 1 2 710 1001 1002 2 3 720 1002


1003 3 4 730 1003 1004 4 5 740 1004 1005 5 6

Haremos un último ejemplo antes de escribir el diseño final del traductor ensamblador
y dar por concluida esta larga sección.

Búsqueda del máximo entre una lista de números


En páginas anteriores se planteó el problema de encontrar el mayor entre tres números, y
ahora escribiremos un programa para el caso general: cuando existe un conjunto arbitrario
de números almacenados en un arreglo en la memoria.
La lógica empleada en la búsqueda será la misma ya utilizada: suponer al primer
número como el máximo, e ir comparando secuencialmente contra todos los demás de la
lista; cuando alguno de ellos sea mayor que el supuesto máximo, lo reemplazará. Al final
del arreglo simplemente se mostrará el máximo, cualquiera que éste haya sido.
Las estructuras de datos a emplear son una lista de números en forma de arreglo, y dos
variables auxiliares: una para apuntar a la celda actual (que además será usada en forma in-
directa) y otra para contener el máximo. Luego de cada comparación se averigua si ya se agotó
la lista, comparando la dirección de la celda actual contra la dirección de la última celda del
arreglo. Curiosamente, lo menos importante aquí son los datos particulares que contiene el
arreglo, porque —como debe ser— el programa es por completo independiente de ellos.

! Programa de búsqueda en un arreglo


TAMAÑO: EQU 10
INICIO: EQU 8000
FINAL: EQU INICIO + TAMAÑO - 1
ORIGEN INICIO
ARREGLO: LISTA 43,56,7,8,3,4,12,98,71,93
APUNT: DATO 1
MÁXIMO: DATO 1
PROGRAMA
! Se supone al primero como el máximo
CARGA ARREGLO
GUARDA MÁXIMO
1 Se apunta a (la dirección de) la segunda celda
CARGA-i INICIO + 1
GUARDA APUNT
Sección 5.2 Primer nivel de lenguajes: Ensamblador 193

! Comienza el ciclo de búsqueda ; la siguiente


1 instrucción indirecta es el "motor" del
programa
CICLO: CARGA-ind APUNT
COMP MÁXIMO
BR<= NOCAMBIA
1 El nuevo fue mayor, apuntar al nuevo máximo
GUARDA MÁXIMO
1 ¿Hay más?
NOCAMBIA: CARGA APUNT
COMP-i FINAL
BR>= SALIDA
! Si aún hay más, avanzar el apuntador
INCR
GUARDA APUNT
BR CICLO
1 Mostrar el máximo y terminar el ciclo
SALIDA: CARGA MAXIMO
OUT
ALTO
FIN

El proceso de ensamblado genera y maneja la siguiente tabla interna de símbolos:

Nombre simbólico Dirección Valor


TAMAÑO 10
INICIO 8000
FINAL 8009
ARREGLO 8000 43
8001 56
8002 7
8003 8
8004 3
8005 4
8006 12
8007 98
8008 71
8009 93
APUNT 8010
MÁXIMO 8011
CICLO 8020
NOCAMBIA 8028
SALIDA 8039

El resultado final de la traducción es, por supuesto, el programa objeto, que se muestra
a continuación. Como antes, para ayuda visual a la izquierda aparecen las direcciones de
los datos y del código. Los últimos dos datos no tienen valor inicial, por lo que se suponen
como cero; aparece subrayada la dirección inicial de ejecución.

8000: 43 56 07 08 03 04 12 98 71 93 00 00
8012: 20 8000
8014: 02 8011
8016: 21 8001
194 Capítulo 5 Software de base

8018: 02 8010
8020: 22 8010
8022: 40 8011
8024: 48 8028
8026: 02 8011
8028: 20 8010
8030: 41 8009
8032: 47 8039
8034: 32
8035: 02 8010
8037: 43 8020
8039: 20 8011
8041: 11
8042: 70

Nuevamente pedimos al estudiante hacer la simulación detallada de la ejecución del


programa. Considere que con ello podrá realmente decir que ya entiende con precisión
el funcionamiento del lenguaje de máquina y del modelo de von Neumann t. Debe tenerse
cuidado con la instrucción de carga indirecta, porque ésa es la clave de funcionamiento de
todo el esquema.

Descripción del programa ensamblador


Como conclusión se escribirá la receta que se ha venido siguiendo para el proceso de traduc-
ción entre programas fuente escritos en lenguaje ensamblador y lenguaje de máquina.

! Programa T I , tercera y última versión


! —Primer paso—
Para cada renglón del programa fuente ejecutar lo siguiente:
Si existe la definición de una etiqueta, buscarla en la tabla de símbolos,
esperando que no esté;
si ya está en la tabla, marcar error: etiqueta repetida; en otro caso, darla de alta
en la tabla de símbolos, y efectuar luego una de las siguientes cinco acciones,
según sea el caso:
(1) Si la etiqueta acompañaba a una pseudoinstrucción EQU seguida de un
número, meter ese número como valor de la constante en la tabla de
símbolos.
(2) Si la etiqueta acompañaba a una pseudoinstrucción DATO seguida de un
número, asignar la dirección actual a esa variable en la tabla de
símbolos y sumar el número a la dirección actual.
(3) Si la etiqueta acompañaba a una pseudoinstrucción VALOR seguida de un
número, meter la dirección actual en la tabla y guardar el número como
el valor de esa variable en la tabla de símbolos. Luego, añadir 1 a la
dirección actual.
(4) Si la etiqueta acompañaba a una pseudoinstrucción LISTA seguida de un
conjunto de números, para cada uno guardar la dirección actual y ese
número como su valor en la tabla de símbolos, añadiendo además 1 a
la dirección actual por cada uno de ellos.

(t) Si se entiende bien el modelo de von Neumann, se entiende bien la forma en la que en principio
operan todas las computadoras, sin importar marcas, modas, modelos, tamaños, velocidades, cos-
tos o colores.
Sección 5.3 Macroprocesadores 195

(5) Si luego de la etiqueta aparece una instrucción


mnemónica, añadir su longitud a la dirección actual.
Si existe una pseudoinstrucción ORIGEN seguida de un número o un nombre
simbólico, considerar ese número como la dirección actual e inicial, o bien buscar
el nombre en la tabla de símbolos y considerar su valor como la dirección inicial.
Si existe la pseudoinstrucción FIN, termina el primer paso
! —Segundo paso—
Regresar al primer renglón después de la pseudoinstrucción
PROGRAMA.
Para cada renglón del programa fuente ejecutar lo siguiente:
Ignorar la posible etiqueta que contenga.
Buscar la instrucción mnemónica en el diccionario de
equivalencias.
Si está, escribir su código en el programa objeto;
en otro caso, marcar error: mnemónico desconocido..
Si a continuación el renglón contiene una variable
simbólica, buscar su dirección en la tabla de símbolos.
Si está, escribir esa dirección en el programa objeto;
en otro caso, marcar error: variable (o etiqueta)
desconocida.
Si existe la pseudoinstrucción FIN, termina el proceso.
Fin del programa T I .

Como aún nos faltan técnicas de estructuración de programas debemos conformar-


nos con esta rebuscada receta, aunque en realidad no es tan complicada.
Con este elaborado diseño terminan nuestros esfuerzos iniciales por elevar el nivel de
la comunicación con la computadora. Ojalá para el lector sea evidente que es mucho más
conveniente escribir programas en ensamblador que en lenguaje de máquina, aunque tam-
bién lo será el hecho de que las cosas parecen comenzar a complicarse un tanto. Como
mínima compensación, puede consultar las referencias [BECL88], [DONJ72], [MACA95]
y [ULLJ76] para información adicional sobre el diseño de los traductores ensambladores.

5.3 MACROPROCESADORES

En la ya imparable carrera por reducir la distancia que nos separa de la computadora (que
además es lo que le da sentido a los estudios sobre computación), se podría también pen-
sar en dar al ensamblador la capacidad de repetir, por medio de una orden, grupos comple-
jos de instrucciones que deban aparecer en múltiples ocasiones. Esto es, compactar de
alguna forma renglones repetitivos en uno solo que funja como su abreviatura, y pedir al
traductor que lo expanda a la hora de la traducción.
Si los renglones

CARGA A
SUMA B
GUARDA C

aparecen con frecuencia en un programa en ensamblador, se podrían agrupar en uno solo


que se llamara, por ejemplo, ADICIÓN. Cada vez que el traductor observara la abreviatura
ADICIÓN como parte del programa fuente, la expandiría para producir los tres renglones
anteriores.
196 Capítulo 5 Software de base

Este nuevo esquema recibe el nombre de macroprocesamiento, y es de importancia


Las macrodefiniciones capital dentro de las ciencias de la computación porque permite —en su expresión más
ocupan un lugar general— la sustitución textual de símbolos de un tipo con símbolos de otro. Esto, que
central dentro parece tan simple e inocente forma parte de la idea central de las matemáticas y la compu-
de las ciencias tación, como se verá en la sección 6.3 .
Un macroprocesadort trabaja con definiciones globales de renglones o símbolos,
computacionales
llamadas macrodefiniciones (o macros), que serán expandidas cuando se las llame para
producir nuevos renglones de texto. Una macrollamada, por tanto, será la invocación de
una macrodefinición por su nombre para lograr una sustitución textual.
Para convertir los tres renglones anteriores en una macrodefinición deberán ence-
rrarse entre las palabras especiales MACRO y FIN_MACRO, con lo cual se constituyen en su
"cuerpo". Además, la macro debe tener un nombre, y esto se logra mediante una etiqueta,
a la manera usual. Así, una macrodefinición consta de los siguientes elementos:

<nombre>: MACRO •
renglón 1 del cuerpo

renglón n del cuerpo


FIN_MACRO

En el caso anterior, la macrodefinición será entonces:

ADICIÓN: MACRO
CARGA A
SUMA B
GUARDA C
FIN_MACRO

Donde se requiera llamarla sólo habrá que escribir su nombre (posiblemente precedi-
do de un símbolo especial de identificación) para que entonces, cuando el macroprocesador
lo detecte, produzca los tres renglones especificados como cuerpo de la macro.
Escogemos el símbolo % como indicador de una macrollamada. Entonces, cuando
dentro del programa fuente el macroprocesador encuentre el renglón

%ADICIÓN

realizará la macroexpansión para obtener en su lugar los renglones

CARGA A
SUMA B
GUARDA C

que constituyen el cuerpo de la macro ADICIÓN, y quedan formando parte de un programa


intermedio (porque no es el programa fuente original, pero tampoco es un programa objeto;
simplemente es un programa que ya no tiene macros).

(DEI término hace referencia a un programa (o conjunto de programas), y no debe confundirse con
un dispositivo físico como el microprocesador.
Sección 5.3 Macroprocesadores 197

Es decir, el macroprocesador recibe un programa fuente con macrodefiniciones, macro-


llamadas y texto en general, y produce como resultado un programa intermedio que con-
tiene las macroexpansiones y (ese mismo) texto en general.

Diseño del macroprocesador


Visto así, se está hablando de otro traductor, al que sólo le interesan las macrodefiniciones
y las macrollamadas: cuando detecta una macrollamada dentro del programa fuente, la Otro traductor
expande y la reemplaza por su cuerpo dentro del programa intermedio; pero si le llega un
texto que no sea ni definición ni llamada de macro, despreocupadamente lo copia sin
alteración al programa intermedio.
Por supuesto que para efectuar una macroexpansión (es decir, la expansión de una
macro), el traductor debió haber previamente almacenado las macrodefiniciones en algún
lugar interno, que llamaremos tabla de macros.
Con estas ideas ya podemos proponer la primera versión del macroprocesador, como
sigue:

! Programa macroprocesador, primera versión


Para cada renglón del programa fuente ejecutar lo siguiente:
Si contiene la palabra MACRO, buscar su nombre en la tabla de macros,
esperando que no esté;
si esa etiqueta ya está en la tabla, marcar error: nombre de macro repetido;
en otro caso, darla de alta en la tabla de macros y comenzar a copiar allí los
renglones que constituyen su cuerpo, hasta llegar al que contiene la palabra
FIN_MACRO.
Si el renglón contiene una macrollamada, buscar ese nombre en la tabla de
macros, esperando que sí esté; si no se encuentra, marcar error: referencia a
una macro desconocida;
en otro caso, realizar la macroexpansión, copiando al programa intermedio
los renglones almacenados en la tabla como cuerpo de esa macrodefinición.
Si el renglón contiene texto que ni es macrodefinición ni macrollamada,
copiarlo al programa intermedio.
Fin del programa macroprocesador.

A modo de ejemplo se aplicará el procedimiento anterior a un texto fuente genérico,


en el cual simplemente se numeraron los renglones para poderlos distinguir en forma
individual tanto en el programa fuente como en el programa intermedio resultante.
Si este fuera el "programa" fuente:

1
2
3
4
ALFA: MACRO
Al
A2
A3
A4
FIN_MACRO
5
6
198 Capítulo 5 Software de base

BETA: MACRO
B1
B2
B3
FIN_MACRO
7
%BETA
8
9
%BETA
%ALFA
10
11
12

Entonces el macroprocesador produciría el siguiente programa intermedio:

1
2
3
4
5
6
7
B1
B2
B3
8
9
B1
B2
B3
Al
A2
A3
A4
10
11
12

Antes de continuar, el lector debe asegurarse de comprender cómo se obtuvo este


programa intermedio mediante el macroprocesador. Observe que en el programa intermedio
ya no hay vestigios de que antes existieron macros.

Macros con parámetros


En nuestro ejemplo, la macrollamada %ADICIÓN produce tres renglones, que sirven para
efectuar la operación C = A + B.
Las macrodefiniciones Si ahora fuera necesario hacer la operación similar K = L + W, por ejemplo, existen
con parámetros son dos posibilidades: escribir nuevamente esos tres renglones, cambiando las letras (variables),
mucho más generales
o bien usar la misma macrodefinición, pero permitiendo dentro del cuerpo la existencia
de ciertas partes variables llamadas parámetros.
Sección 5.3 Macroprocesadores 199

La macro dirá ahora

ADICIÓN: MACRO(3)
CARGA ?1
SUMA ?2
GUARDA ?3
FIN_MACRO

Si se invoca como %ADICIÓN (A, B, C) se obtendrá exactamente el mismo resultado


que antes, pero si se llama como %ADICIÓN (L, W, K), se obtendrá la nueva operación.
Los cambios requeridos fueron mínimos: al inicio se especifica el número de parámetros
a emplear, y luego es posible referirse a ellos en los lugares requeridos mediante un mar-
cador especial (por ejemplo ?n, donde n indica a cuál de los parámetros se está haciendo
referencia dentro del cuerpo de la macro).
Por ejemplo, esta nueva macro sirve para obtener el promedio de tres valores ya
existentes; una vez definida, y gracias al uso del macroprocesador, permite considerar que
el lenguaje ensamblador ha sido ampliado, porque se puede entonces emplear como si
formara parte de las capacidades originales de la máquina:

PROM3: MACRO(4)
CARGA 71
SUMA ?2
SUMA ?3
DI V - i 3
GUARDA ?4
FIN_MACRO

Es decir, dentro del programa fuente pueden aparecer llamadas a (macro)instrucciones


del tipo %PROM3 (A ,B,C,R) o bien 96PROM3 ( ZETA, AL FA+1 8 , GAMA, NÚMERO) , y el macropro-
cesador las expandirá para producir los renglones

CARGA A
SUMA B
SUMA C
DIV-i 3
GUARDA R

en el primer caso, y

CARGA ZETA
SUMA ALFA+18
SUMA GAMA
DIV-i 3
GUARDA NÚMERO

en el segundo. Obsérvese que se trata de una mera sustitución textual: el macroprocesador


no ejecuta ninguna instrucción del programa fuente, simplemente reemplaza un texto por
otro. Es al ensamblador al que tocará traducir el texto resultante para su posterior ejecución.
Al igual que antes, en estos ejemplos, como en todos los escritos en ensamblador,
debe tenerse cuidado y notar que no se está ejecutando el programa objeto sino apenas
traduciendo el fuente. Considere a la traducción y la ejecución como dos procesos inde-
pendientes, aunque el segundo sólo sea posible a partir del primero.
200 Capítulo 5 Software de base

Para que un macroprocesador pueda manejar parámetros requiere un espacio en don-


de guardarlos temporalmente y efectuar la correspondencia entre los parámetros ficticios
(también llamados argumentos) y los parámetros reales. Los argumentos están represen-
tados por 71, 72, 73 y ?4 en el ejemplo, mientras que los parámetros reales en la primera
llamada son A, B, C, R o bien ZETA, ALFA+1 8, GAMA y NÚMERO en la segunda.
Los cambios requeridos para permitir al macroprocesador manejar parámetros son
sencillos: al momento de la macrollamada debe simplemente colocar los parámetros reales
en sus correspondientes lugares marcados con ?n dentro del cuerpo de la macro.
El macroprocesador con manejo de parámetros enriquece la calidad de la comunicación
con la computadora, porque ahora se pueden escribir programas fuente en macroensam-
blador: lenguaje ensamblador con macros. Como antes, se llama así tanto al lenguaje como
al traductor. Es decir, a un macroprocesador integrado con un ensamblador se le conoce
como macroensamblador.

Macroensamblador
11111111111111111111111~
.u.uuuuuuuIuI-
U111111111111111111~
111111MMIIII~

Figura 34 Función de un UCP


macroensamblador Programa fuente Memoria

Como debe estar ya claro, primeramente el macroprocesador efectúa una lectura so-
bre el programa fuente para registrar las macrodefiniciones y expandir las macrollamadas.
Se produce así un programa intermedio escrito en ensamblador "puro", que a continuación
es alimentado al traductor ensamblador para que éste efectúe los dos pasos descritos en el
apartado anterior y obtenga el programa objeto equivalente, que posteriormente será lle-
vado a la memoria para su ejecución.
Como último ejemplo, a continuación se reescribe con macros el programa de la pág.
183 que encuentra el máximo entre tres números:

1 Programa para buscar máximo de tres, versión con macros


ORIGEN 3000
ALFA: DATO 1
BETA: DATO 1
GAMA: DATO 1
MÁX: DATO 1
LEER: MACRO(1)
IN
GUARDA 71
FIN_MACRO
IGUAL: MACRO(2)
CARGA 71
GUARDA ?2
FIN_MACRO
PROGRAMA
%LEER(ALFA) Leer y almacenar el primer número
%LEER(BETA) Leer y almacenar el segundo
Sección 5.3 Macroprocesadores 201

%LEER(GAMA) Leer y almacenar el tercero


%IGUAL(ALFA,MÁX) Suponer al primero como máximo
COMP BETA Comparar 10. vs. 20.
BR> SIGUE
%IGUAL ( BETA , MÁX ) Cuando el 20. fue mayor
SIGUE: COMP GAMA Comparar mayor vs. 30.
BR> FUERA
%IGUAL (GAMA,MÁX) Cuando el 30. fue mayor
FUERA: OUT Mostrar el máximo
ALTO
FIN

La tabla de símbolos y el código objeto producidos por el ensamblador son los mis-
mos que antes, porque el programa intermedio generado por el macroprocesador es igual
al del programa fuente de la página ya citada.
El poder de las macros es muy grande y existen muchas variantes, aunque ya no se
analizarán aquí con detalle. Por ejemplo, se pueden establecer directivas para el macro-
procesador dentro de las macrodefiniciones, y lograr así expansiones condicionales. La
siguiente macrodefinición contiene directivas especiales (%sI y %F I N_Si), empleadas para
controlar el resultado de la expansión:

ALFA: MACRO(2)
CARGA 71
%SI ?2 < 10
SUMA-i ?2
%FIN_SI
PSI ?2 >= 10
MULT ?1
%FIN_SI
OUT
FIN_MACRO

Y producirá el texto

CARGA X
SUMA-i 3
OUT

si se llamó a la macro como %ALFA ( X, 3 ), por ejemplo, pero en cambio se expandirá como

CARGA ZETA
MULT ZETA
OUT

si la llamada fue %ALFA ( ZETA, 1 7 ) . Es decir, la macroexpansión produce resultados dife-


rentes según sean los valores de sus parámetros. Obsérvese que no se está ejecutando el
programa fuente: sólo se dan órdenes al macroprocesador para que se comporte de tal o
cual forma. Estas directivas de expansión condicional son similares a las pseudoinstruc-
ciones del lenguaje ensamblador, en el sentido de que no se traducen a código objeto sino
que resultan en acciones por parte del traductor.
Con esos medios, y otros similares, el lenguaje macroensamblador se vuelve bastante
poderoso, y es mucho más conveniente programar en él que en el lenguaje de máquina,
202 Capítulo 5 Software de base

aunque en realidad la comunicación con la computadora sigue siendo bastante limitada


aún. El resto del capítulo se dedicará a explorar formas de seguirla ampliando.
Para mayor información sobre las macroexpresiones se pueden consultar las referencias
[KERB76] y [BECL88]. Todos los ensambladores comerciales son en realidad macro-
ensambladores, y en sus manuales se pueden encontrar los inacabables detalles necesarios
para emplearlos.
Después de todo esto, disponemos ya de un primer lenguaje simbólico (macroen-
samblador) para establecer comunicación con la computadora. Sabemos también que la
máquina sólo "entiende" lenguaje de máquina, y por ello resulta imprescindible la exis-
tencia de un traductor, diseñado a lo largo de las páginas anteriores. Pero aún falta lograr
que la propia computadora efectúe la traducción, porque por lo pronto la hemos realizado
nosotros, siguiendo la receta del traductor T I .
Para que la computadora realice la traducción será necesario algo que parece raro:
traducir el traductort, y por ello se procederá a averiguar con mayor precisión los requeri-
mientos para que la computadora pueda traducir los programas fuente a programas objeto
por sí sola.

5.4 CARGADORES

Para que la computadora pueda hacer la traducción de los programas escritos en lenguaje
(macro)ensamblador hacia lenguaje de máquina, es necesario que el programa traductor
T, resida, ya traducido, en la memoriaff.
Supóngase que en lenguaje (macro)ensamblador se escribió un programa, P 1 , para
jugar ajedrezttt. Se estudiará ahora la serie completa de pasos a realizar para lograr que
la computadora traduzca y ejecute P I . (Se iniciará desde que la máquina está apagada y se
analiza lo que debe hacerse para llegar al final.)
Cuando se enciende por vez primera la computadora, la memoria está completamente
El problema vacía; es decir, el procesador está detenido, esperando alguna instrucción en memoria para
de la carga leer, decodificar y ejecutar. ¿Cómo se saca a la computadora de este letargo? Está claro
que aún no es posible ejecutar programas, por la sencilla razón de que no existe ninguno
residente en la memoria, y todo programa debe estar cargado para poderse ejecutar. Aquí
encontramos el primer problema: ¿cómo se lleva un programa a la memoria? Existen dos
posibles respuestas; la primera consiste en cargar manualmente celdas de memoria con
los valores numéricos que representan la codificación en lenguaje de máquina de algún
programa, y la segunda en ejecutar un programa que haga esto de manera automática.

(t) "Traducir el traductor" en este contexto es equivalente a "ensamblar el ensamblador", porque el


traductor (macro)ensamblador estará escrito en (macro)ensamblador, pero el bosquejo inicial de su
diseño está en español, en forma de las recetas ya mostradas. Como se comenzará a ver ahora, el
software de base está lleno de estos aparentes trabalenguas: traducir el traductor, ensamblar el en-
samblador, cargar el cargador, compilar el compilador.
(tt) Pero, ¿quién tradujo el traductor? ¿Podrá ser (otro) programa traductor? Si así fuera, ¿quién
tradujo a ése? Después de analizar el problema se llega a la triste conclusión de que el primer
traductor tuvo que haber sido traducido a mano por el diseñador: lo escribió en español —como en
las recetas aquí mostradas— y lo fue paulatinamente traduciendo a lenguaje de máquina. Es una
penosa labor, pero sólo debe efectuarse una primera vez. Ya teniendo el traductor T, traducido a
lenguaje de máquina, bastará con ejecutarlo para que de allí en adelante parezca como si la compu-
tadora "entiende" directamente lenguaje macroensamblador. Claro que no es cierto, pero el soft-
ware de base así lo hace parecer; es el concepto de herramienta llevado a un extraordinario nivel.
(t t t) ¿Qué le parece: será fácil o difícil escribir un programa así? ¿Se podrá hacer? Por ahora sólo
supóngalo y no se angustie tratando de pensar en los millones de detalles requeridos.
Sección 5.4 Cargadores 203

Exploremos la primera posibilidad. Supóngase que el programa fuente P 1 tiene cien


renglones de longitud de la misma forma general que el programa de la página 194. Esto
significa un enorme esfuerzo, ya que habría que colocar manualmente varios miles de
ceros y unos en varios cientos de celdas de la memoria. Una vez realizado este penoso
paso todavía quedaría la gigantesca molestia de tener que cargar también manualmente el
traductor T i completo.
La segunda posibilidad es mucho más prometedora, y consiste en escribir un progra-
ma para que haga estos pasos por nosotros: lo llamaremos cargador. Las funciones de un Necesidad
cargador son relativamente sencillas, y consisten en extraer información objeto de algún del cargador
medio externo a la memoria (disco o cinta magnética, por ejemplo) y colocarla en celdas
sucesivas de la memoria, a partir de una celda preespecificada.
He aquí el diseño básico de un cargador:

! Programa cargador, primer acercamiento.


Localizar el dispositivo de memoria secundaria con los datos a cargar.
Averiguar la dirección de memoria a partir de la cual va a quedar cargado el
programa objeto, y considerarla como la celda actual.
Para cada "renglón" del programa objeto ejecutar lo siguiente:
Determinar cuántas celdas de memoria se requieren para almacenar esos
datos binarios.
Depositar los datos en celdas contiguas de la memoria, a partir de la celda
actual, e ir incrementando las direcciones para mantenerlas actualizadas.

Este programa debe traducirse a lenguaje de máquina para que entonces quede en la
memoria de la computadora y se pueda ejecutar. El diseño inicial está en español pero,
como antes, habrá que convertirlo en un programa escrito en ensamblador y luego ensam-
blarlo, para tener así una herramienta muy poderosa con la cual se podrá cargar en memo-
ria cualquier programa objeto, siempre y cuando se le indique en cuál disco o cinta está
almacenado, y a partir de cuál celda de memoria se desea que lo deposite.
Esto, sin embargo, da lugar a otro problema: ¿cómo se carga el cargador?
La respuesta ya no puede ser "por medio del cargador", porque está claro que éste no El dilema
puede cargarse a sí mismo; para ello debería estar residente en memoria (para que la UCP de la carga inicial de
lo pudiera ejecutar), ¡pero ese es precisamente el problema que se desea resolver! un programa
Nos encontramos ante una situación que no puede solucionarse en términos de un pro-
grama. El problema inicial —romper el círculo vicioso recién descrito— recibe el curioso
nombre de bootstrap, que en inglés significa algo así como "el problema de tratar de levan-
tarse del suelo tirando de las cintas de nuestras propias botas". Éste es, evidentemente, un
problema que tiene "truco" y requiere de medios externos para poderse resolver.
El truco consiste en cargar a mano el cargador para evitar los obstáculos lógicos mencio-
nados. Sólo que esta operación se tendría que repetir cada vez que se encienda la computadora,
porque cuando se retira la corriente eléctrica la memoria pierde todos sus contenidos. La solu-
ción a este nuevo problema técnico (ya no lógico) es, a grandes rasgos, la siguiente.
Se escribe otro pequeño programa (que llamaremos "minicargador"), cuya única fun-
ción consiste en cargar el cargador.Este miniprograma no será de uso general, y solamente
servirá para extraer al cargador objeto de un lugar preestablecido (de una sección fija de un
disco magnético predeterminado, por ejemplo) y depositarlo en una zona también preesta-
blecida de la memoria central. Luego de hacer esto, el minicargador cede el control al
cargador. Como este programa es de uso particular y cumple una sola función muy espe-
cífica, será pequeño (unos pocos centenares de renglones fuente), por lo que será factible
traducirlo a mano al lenguaje de máquina.
La situación aparece ahora así: cuando se enciende la computadora, se carga manual-
mente el minicargador objeto y entonces se ejecuta. Éste, a su vez, inmediatamente cargará
204 Capítulo 5 Software de base

al cargador —que debe estar permanentemente en una posición fija y predeterminada del
disco rígido—, y a partir de allí se podrá seguir con el proceso.
Por supuesto que en una computadora real el minicargador no se carga manualmente,
sino utilizando una memoria especial tipo ROM, que al encender la máquina descarga su
contenido en la memoria central. Como se dijo, al proceso de cargar el minicargador y con
ello dar "vida" a la computadora se le conoce como bootstrap, o también como IPL (Initial
Program Load, carga del programa inicial). Esto sucede cada vez que se enciende la compu-
tadora y es el requisito previo para su operaciónt.
Los pasos para traducir y ejecutar el programa P 1 son, entonces:

0. Dar IPL, para cargar el minicargador (no es necesario si la computadora ya está


operable).
1. Ejecutar el minicargador para que cargue al cargador, residente en una sección
predeterminada del disco magnético.
2. Ejecutar el cargador para que cargue el ensamblador, residente en algún disco
especificado.
3. Ejecutar el ensamblador para que lea el programa fuente (también almacenado en
algún disco especificado) y lo traduzca a lenguaje de máquina. El archivo objeto
resultante quedará en otra sección del disco magnético.
4. Ejecutar nuevamente el cargador, para que cargue el programa objeto recién pro-
ducido por el ensamblador.
5. Ejecutar el programa objeto.

El paso 4 puede eliminarse si en el 3 se pide al ensamblador dejar el resultado de la


traducción directamente en la memoria, aunque entonces no quedaría registro permanente
del programa objeto porque se perderá al apagar la computadora.
Para configurar un ejemplo más completo supondremos los siguientes datos: el mini-
cargador (originalmente contenido en ROM) residirá en memoria a partir de la celda cero, y
mide 500 bytes. El cargador, ya traducido, mide 5,000 bytes y quedará colocado a continua-
ción. El ensamblador objeto T 1 mide 20,000 bytes, y el programa objeto P 1 medirá, una
vez ensamblado, 8,400 bytes.
Si se efectúan todas las operaciones de carga secuencialmente y utilizando celdas
contiguas de memoria, ésta se verá así justo antes de ejecutar el paso 5 anterior:

(0) (500)
Minicargador Cargador
5,500
Traductor T 1 (macroensamblador)
25,500 33,900
Programa objeto P 1 libre

espacio libre ...

(t) El minicargador forma parte del BIOS ya mencionado en el capítulo 3. Además, siendo éste un
pequeño programa de propósito especial, carece de "inteligencia" suficiente como para algo más
que simplemente quejarse cuando por error el usuario enciende la computadora habiendo dejado
un diskette dentro al apagarla, con lo que hace creer al minicargador que allí reside el cargador
(y no en la pista 0 del disco rígido). Es el típico mensaje Non - system disk or disk error.
Sección 5.4 Cargadores 205

En este diagrama del llamado espacio de direcciones (que no está a escala) se indica
cuáles programas objeto residen en la memoria, y a partir de cuál celda.
Veamos el proceso con más detalle. Como se dijo, el cargador debe estar en una sección
fija y preestablecida del disco magnético (que llamaremos "D o"), pues allí irá ciegamente Pasos
a buscarlo el minicargador; supondremos que el ensamblador T 1 está en el disco "D-T 1" y para la ejecución
que el programa fuente F i quedó en el disco "D-F i"t. Además, cuando el traductor termi- de un programa
na de ensamblar el programa fuente, deja el resultado objeto en el disco "D-0 1 ". en una computadora
Asignaremos al contador de programa (CP) las direcciones ya descritas, mediante la
notación

CP <— x {F: Y, D: Z}

que se lee "ejecutar el programa que inicia en la celda x de la memoria, que lee sus datos
a partir de la Fuente Y, y los escribe en el Destino Z (la fuente y el destino pueden ser
posiciones en la memoria o el disco, según sea el caso)".
Aunque esta notación pudiera parecer complicada, nos deberá servir de consuelo
pensar que si no la empleáramos entonces los detalles sobre las localizaciones de los
archivos en disco y las posiciones finales en la memoria deberían ser generadas a partir de
la nada ...y eso sí sería complicado.
Con todo esto, los pasos anteriores serán entonces:

0. Dar boot (si es necesario). Con ello se lleva el minicargador del ROM a la memo-
ria, a partir de la celda 0. Obsérvese que no se ha ejecutado nada todavía porque
éste es un paso efectuado por medios eléctricos externos.
1. CP <— O {F: Do , D: 500}
(Ejecutar el minicargador para que vaya al disco D 0, extraiga de allí el cargador
objeto y lo deposite en la memoria, a partir de la celda 500)
2. CP <— 500 {F: D-T 1 , D: 5,500}
(Ejecutar el cargador, para que lea lo que contiene el disco D-T 1 (el traductor) y lo
deposite en la memoria a partir de la celda 5,500)
3. CP <— 5,500 {F: D-P 1 ;D: D-0 1 }
(Ejecutar el ensamblador para que lea el programa fuente del disco D-P 1 , lo tra-
duzca, y deje el programa objeto resultante en el disco D-0 1 )
4. CP <— 500 {F: D-0 1 , D: 25,500}
(Ejecutar el cargador nuevamente, para que tome el contenido del disco D-0 1 y lo
deposite en la memoria, a partir de la celda 25,500)
5. CP <-- 25,500 {F: ?, D: ?}
(Finalmente, ejecutar el programa objeto, que inicia en la celda 25,500. Como no
se dijo nada muy específico sobre el programa P i (ahora ya 0 1 ), no sabemos —ni
nos preocupa en este ejemplo— de dónde va a tomar sus datos y cómo y hacia
dónde va a enviar sus resultados.

Será responsabilidad del programa P i/0 1 ejecutar la instrucción ALTO como último
paso; de no hacerlo así, la computadora tratará luego de ejecutar lo que contenga la celda
33,900 (¿por qué?), con resultados impredecibles.

(t) Las notaciones "D-T 1 " y similares se refieren a algunas secciones del disco o discos, para no
preocuparnos ahora por las particularidades específicas y necesariamente precisas sobre cómo y
dónde están almacenados esos archivos.
206 Capítulo 5 Software de base

Programa objeto
Figuro 35 Función de un en disco
cargador Memoria

Más adelante se verá cómo se pueden convertir los pasos 1 a 5 en un programa para
que, una vez traducido a lenguaje de máquina, gobierne la operación de la computadora y
nos libere de esa tarea. Un programa de este tipo recibe el nombre de monitor.

Esquemas de carga
Por lo pronto se ha supuesto que el programa objeto a ser cargado en la memoria forma un
todo estático, único y contiguo, pero no siempre será así. Los sistemas complejos suelen
consistir en un conjunto de programas diseñados, escritos y traducidos en forma indepen-
diente, por lo que no constituyen en forma natural una sola unidad, aunque sí deberán
estar cargados en la memoria para su posterior ejecución.
Esto implica la existencia de varios esquemas de carga, desde el más simple —como
el que aquí explicaremos— hasta verdaderos subsistemas de enlace de programas objeto,
conocidos como editores de ligado (link editor o linker), o como enlazadores dinámicos
(binders): en el sistema operativo Windows ®, por ejemplo, se habla de esquemas OLE
(Object Linking and Embedding: incrustación y ligado de módulos objeto), y DLL (Dynamic
Linking Library: bibliotecas ligadas dinámicamente).
Para nuestro caso, como el programa objeto es único, bastará con indicar al cargador
su localización en disco y la dirección de carga. Por ejemplo, el renglón 4 antes expuesto,

CP 500 {F: D-0 1 , D: 25,500)

solicita tomar el archivo del disco D-0, para colocarlo en la memoria a partir de la celda
25,500 .
Además, sería conveniente que tanto la dirección de carga como los posibles datos
fijos requeridos por el programa formaran parte del archivo objeto, porque entonces así se
tendría una especie de "cápsula" ejecutable autocontenidat. Para este fin, el ensamblador
deberá preparar lo que se conoce como un módulo absoluto de carga: un "paquete" con
la dirección de carga, la dirección inicial de ejecución, los datos fijos y, por supuesto, el
programa objeto.
Si se toma nuevamente como ejemplo el programa objeto de la página 194, que busca
un número dentro de un arreglo, el módulo absoluto de carga será entonces este:

8000 8012 43 56 07 08 03 04 12 98 71 93 00 00
20 8000 02 8011 21 8001 02 8010 22 8010 40 8011
48 8028 02 8011 20 8010 41 8009 47 8039 32 02 8010
43 8020 20 8011 11 70

(t) Tal es el caso de los archivos con terminación ".COM" y ".EXE" de las computadoras per-
sonales.
Sección 5.5 Compiladores 207

Se trata de un archivo magnético dividido en dos partes lógicast: un encabezado y el


programa objeto propiamente. El encabezado (header, en inglés) contiene —en ese preciso
orden— la dirección de carga, la dirección inicial de ejecución, y un número de datos
(bytes para nosotros) determinado por la diferencia entre las dos cifras anteriores.
Si se recuerda, la dirección de carga estuvo especificada en el programa fuente por la
pseudoinstrucción ORIGEN y, en la misma forma, la dirección inicial de ejecución está
marcada por la pseudoinstrucción PROGRAMA. Con ello, el ensamblador dispone de todos
los elementos para preparar el módulo absoluto de carga y dejarlo grabado en el disco
magnético antes de terminar su función de traducción y despedirse.
De esta forma, cuando el cargador toma el módulo absoluto del disco especificado
(aún sigue siendo necesario, por supuesto, indicarle cuál es) puede por sí solo cargar todos
sus contenidos, con excepción de los dos primeros bytes que le sirven de guía, porque el
primero es la dirección de carga y el segundo es la dirección de la primera instrucción
ejecutable, después de los posibles datos.
Por supuesto que los módulos absolutos de carga empleados en las computadoras
reales tienen un formato más elaborado que éste, pero —como siempre— nos interesan más
los porqués que los detalles. Por otro lado, confiamos en que el lector esté convencido de
que todo esto debe necesariamente funcionar así (o en una forma similar), porque siempre
nos guía el principio ya enunciado de necesidad lógica: las cosas son como son por alguna
razón, que debe entonces servirnos de guía para averiguar cómo funcionan o, mejor aún,
para re-inventarlas.
Con todo esto, el escenario queda listo para dar otro paso hacia arriba en la calidad de
la comunicación con la computadora.

5.5 COMPILADORES

Los apartados anteriores han dado la posibilidad de escribir programas fuente para aplica-
ciones particulares en lenguaje (macro)ensamblador, pues se dispone de un traductor T i
queloscnvrtiáagjedmqun.Sptabiédejrsopgmai-
dentes en el disco y llevarlos a la memoria por medio del cargador, también ya diseñado.
Estamos, pues, en una situación bastante buena, con un cierto nivel de comunicación con
la computadora. No obstante, deseamos aún más capacidad y una mayor flexibilidad para
transmitir los requerimientos a la máquina; es decir, establecer comunicación con la compu-
tadora en un lenguaje más parecido al nuestro (y, por tanto, menos parecido al lenguaje de
unos y ceros).

¿Qué se puede hacer?


Exploremos las posibilidades de intentar comunicarnos con la computadora usando un
lenguaje de más alto nivel expresivo. Cuando se dice "lenguaje de alto nivel" se piensa en
uno que permita, con una sola orden, expresar cosas complejas; esto es, un lenguaje dota-
do de una estructura capaz de sostener esa posibilidad.
Necesariamente debemos analizar entonces el problema de la traducción de lengua-
jes de alto nivel expresivo, porque intentamos establecer comunicación con la máquina en
un lenguaje de este tipo y esperar que reciba nuestros mensajes mediante un traductor
(T2) que los convierta a su lenguaje de máquina.

(t) Es decir, la división no es física —es un solo archivo— sino que sigue dos criterios diferentes.
Recuérdese cómo en este contexto la palabra "lógico" se refiere a algo simbólico o que no está en
forma física.
208 Capítulo 5 Software de base

, El compilador (que es el nombre de este nuevo traductor T 2) trabajará sobre las


cadenas de entrada (esto es, sobre los renglones que componen el programa fuente), para
traducirlos al lenguaje ensamblador. Luego de esto, el traductor ensamblador (T 1 ) obten-
drá finalmente el mismo programa en lenguaje objeto.
Como la teoría matemática requerida para el diseño de un compilador rebasa los límites
de este curso introductorio (pues se trata de la subárea 2.4 de los Modelos Curriculares),
aquí sólo se estudiará el proceso de compilación desde un punto de vista muy general.
Para comenzar a entender el nivel de complejidad del problema analizaremos el caso
de traducir frases del español al inglés; por ejemplo: "la casa es azul".
Si usamos un diccionario, encontraremos que "la" se traduce por the, "casa" por house,
El problema "es" por is y "azul" por blue, por lo que la frase ya traducida será the house is blue.
de la traducción Ojalá esté claro, sin embargo, que esto no fue sino una afortunada casualidad. Inténtese
un ejemplo más complejo y de inmediato se encontrarán las dificultades inherentes a todo
proceso de traducción de lenguajes de alto nivel expresivo; es decir, un simple diccionario
no basta para lograr una buena traducción y, a veces, ni siquiera para lograr algo que me-
dianamente se asemeje a la frase original. Esto se debe, por supuesto, a la estructura del
lenguaje, definida por su gramática, y a un mundo de significados que no es reducible a
un diccionario.
Así, aunque se haya traducido la frase "sufragio efectivo, no reelección" por, medio de
un diccionario, nos veremos en dificultades casi insalvables cuando se intente traducir la
frase "sufragio efectivo no, reelección"; aunque tiene exactamente los mismos componentes
(las mismas cuatro palabras y una coma) significa nada menos que lo contrario que la frase
original. Ningún diccionario será suficiente para dar cuenta de la diferencia, ya que éstos
trabajan únicamente con palabras aisladas, sin tomar en cuenta la estructura gramatical.
Noam Chomsky, lingüista del Instituto Tecnológico de Massachusetts (MIT)t, que
en 1956 publicó un estudio ya clásico sobre gramáticas formales (esto es, estudiadas des-
de un punto de vista matemático), proponía otro ejemplo, (en inglés), para poner en eviden-
cia que en toda frase de un lenguaje existe como respaldo una estructura que le da forma
y sentido. ¿Qué significa la frase Half baked chicken? Puede significar tanto "medio pollo
Noam Chomsky cocido" como "pollo medio cocido", que de ninguna manera dice lo mismo. Esta es una
clásica frase ambigua. La ambigüedad está determinada por la estructura que respalda la
frase, como a continuación se explica.
Para el caso de "pollo medio cocido", la estructura de la frase es como sigue:
Diagramas de
estructura gramatical

half baked chicken

(t) Quien además es uno de los más honestos intelectuales estadounidenses, y mordaz crítico de la
política exterior de su país desde una perspectiva más que inteligente y liberal.
Sección 5.5 Compiladores 209

Mientras que para "medio pollo cocido" es:

half baked chicken

Sin preocuparnos todavía por el significado de los diagramas, sí diremos que existen
varias maneras de agrupar las palabras (por medio de lo que en lingüística se conoce El proceso
como la "estructura profunda"), y que estas formas le confieren significados distintos a la de la traducción
misma frase.
Como estamos ante un problema no trivial, a continuación se comenzará a explorar
lo que se requiere para poder traducir frases dotadas de estructura interna.

Análisis lexicográfico
El paso inicial consiste en reconocer todos y cada uno de los símbolos aislados constitu-
yentes de la frase; lo cual, a su vez, implica reconocer las letras (y signos de puntuación)
y reconocer las palabras. Obsérvese que reconocer no necesariamente significa entender;
para reconocer un símbolo sólo se requiere buscarlo (y encontrarlo) en un diccionario
previamente especificado.
Esta primera etapa se conoce como análisis lexicográfico. Su tarea central consiste
en separar los componentes léxicos (o tokens) de entre el conjunto de símbolos fuente.
Esto es, en un renglón normal coexisten símbolos de diversas clases (letras, dígitos,
símbolos de puntuación, blancos y caracteres especiales) aunque sean invisibles, y es nece-
sario aislar los componentes sintácticos de este conglomerado de caracteres. Para nosotros
es obvio que la frase "uno, dos, tres" consta de tres palabras, pero en realidad contiene catorce
símbolos diferentes que es necesario agrupar de alguna manera. Todos aprendimos a ha-
cer análisis léxicos de manera intuitiva durante el largo proceso de enseñarnos a leer, pero
dentro de un compilador se requiere definir esta tarea con toda precisión en forma de un
programa ejecutable, para lo cual se emplea un modelo matemático ya mencionado en el
capítulo 1, el autómata finito. Un autómata así es una función matemática con capacidad
de reconocer los grupos de caracteres que constituyen un componente léxico, o bien de
marcar un error si no están bien construidos. Más adelante en el texto hay otras referen-
cias a este tipo de construcciones formales.

Análisis sintáctico
Una vez concluido exitosamente ese análisis se llega a una parte muy interesante, llamada
análisis sintáctico, cuya finalidad es encontrar la estructura gramatical de la frasefor-
mada por los elementos aislados ya reconocidos.
No obstante que los humanos podemos realizar este tipo de análisis igualmente en
forma intuitiva e inmediata (aunque no sepamos gramática), dentro de un compilador se
210 Capítulo 5 Software de base

requieren métodos matemáticos para especificar cómo lograrlo. La idea general consiste
en diseñar un programa que trate de acomodar estructuras gramaticales predefinidas para
las frases objeto del análisis, guiándose por medio de las palabras que la componen.
El análisis sintáctico sólo fue entendido formalmente hasta hace algunos años. Los
analizadores sintácticos del tipo de los empleados por un compilador (llamados parsers
en inglés) se dividen en dos grandes familias: los que funcionan en forma "ascendente" y
sus contrarios, en forma "descendente". Para poder discutir estos puntos, aunque sea míni-
mamente, será necesario mencionar antes algunos elementos sobre la teoría de las gramá-
ticas y los lenguajes formalest. Como se dijo, esta teoría nace en la década de 1950 y trata
sobre las propiedades de ciertas construcciones formales llamadas gramáticas, que no son
sino especificaciones matemáticas de la estructura de los lenguajes formales, similares a
la gramática que se supone todos aprendimos en la escuela elemental para describir la
estructura del lenguaje ordinario empleado para comunicarnos.
Para caracterizar el problema de la comunicación se puede pensar en una gramática
El problema como un generador de palabras (o frases), que luego llegarán a un reconocedor que se encar-
de la comunicación gará de decidir si una frase es "hija legítima" de cierta gramática o no; esto es, el reconoce-
dor deberá efectuar análisis sobre las frases recibidas para proceder luego a interpretarlas.
A continuación hay un ejemplo muy elemental, sobre un subconjunto de la gramática
del español.
Todo hablante de nuestro idioma reconocerá que la frase "la casa es azul" es correcta
desde cualquier punto de vista. Para analizar el porqué de ello se partirá del conocimiento
de que una oración (o frase) está compuesta de palabras (que a su vez constan de letras).
Las palabras, sin embargo, se clasifican en ciertos tipos gramaticales agrupadas luego en
construcciones más complejas. En suma:

—la es un ARTÍCULO.
—casa es un SUSTANTIVO.
—es es un VERBO.
—azul es un ADJETIVO.

y además:

—Un ARTÍCULO seguido de un SUSTANTIVO es una FRASE NOMINAL.


—Un VERBO seguido de un ADJETIVO es una FRASE VERBAL.
—Una FRASE NOMINAL seguida de una FRASE VERBAL es una ORACIÓN.

O más escuetamente,

1) <ORACIÓN> <FRASE NOMINAL> <FRASE VERBAL>


2) <FRASE NOMINAL> <ARTÍCULO> <SUSTANTIVO>
3) <FRASE VERBAL> ---> <VERBO> <ADJETIVO>
4) <ARTÍCULO> —> "la"
5) <SUSTANTIVO> —> "casa"
6) <VERBO> ---> "es"
7) <ADJETIVO> —> "azul"

Estas siete reglas configuran una primera gramática formal, con la que trabajaremos
un poco. Obsérvese el uso de algunos símbolos nuevos: con las llaves triangulares se

(t) En el capítulo 6 se dedica una sección a este importante tema, y se proponen referencias biblio-
gráficas sobre la teoría matemática requerida por los compiladores.
Sección 5.5 Compiladores 211

encierran palabras de la gramática que se llamarán no terminales, mientras que con las
comillas se distinguen las palabras terminales. Estas últimas son las que forman las fra-
ses terminales, o sea, los elementos finales de una construcción gramatical, que además
son los únicos que se muestran al mundo exterior. Una frase u oración tiene una estructura
interna, y los elementos empleados para definir esta estructura profunda de la frase son
precisamente los no terminales.
El otro elemento nuevo es la flecha, que liga miembros izquierdos (no terminales),
con miembros derechos (que pueden ser terminales o no).
Por ejemplo, la regla 6)

<VERBO> -9 "es"

se lee: "el no terminal VERBO produce el terminal "es".


El lector podrá comprobar que la frase "la casa es azul" tiene la siguiente estructura:

<ORACIÓN>
Un primer análisis
sintáctico

<FRASE NOMINAL> <FRASE VERBAL>

<ARTÍCULO> <SUSTANTIVO> <VERBO> <ADJETIVO>

1 1 1 1
"la" "casa" "es" "azul"

Ahora se pueden entender un poco mejor los conceptos del análisis sintáctico. Si
iniciamos en la parte superior de este diagrama (conocido como árbol sintáctico) y se
intenta aplicar una a una las siete reglas de producción se obtendrá lo siguiente:

<ORACIÓN> <FRASE NOMINAL> <FRASE VERBAL>


<ARTÍCULO> <SUSTANTIVO> <FRASE VERBAL>
"la" <SUSTANTIVO> <FRASE VERBAL>
"la" "casa" <FRASE VERBAL>
"la" "casa" <VERBO> <ADJETIVO>
"la" "casa" "es" <ADJETIVO>
"la" "casa" "es" "azul"

(La doble flecha se lee "genera mediante la aplicación de una regla de producción", o
simplemente "genera".)
Obsérvese que se partió del tope (o raíz) del árbol y se llegó a las hojas terminales.
Éste fue el primer análisis sintáctico descendente; aunque trivial, es representativo.
212 Capítulo 5 Software de base

¿Cómo será el análisis sintáctico ascendente? Hagamos lo siguiente:

"la" G <ARTÍCULO>
"casa" G <SUSTANTIVO>
<ARTÍCULO> <SUSTANTIVO> G <FRASE NOMINAL>
"es" <VERBO>
"azul" <ADJETIVO>
<VERBO> <ADJETIVO> G <FRASE VERBAL>
<FRASE NOMINAL> <FRASE VERBAL> <ORACIÓN>

Ahora se procedió exactamente a la inversa: partiendo de los elementos terminales se


encontró un camino hasta la raíz del árbol. Aunque en apariencia el análisis ascendente
sólo es el inverso del descendente, en realidad resulta mucho más complejo. La razón de lo
anterior reside, intuitivamente, en que el problema central del análisis descendente con-
siste en escoger alguna regla y aplicarla, partiendo de su miembro izquierdo (que consta
de un solo elemento no terminal), mientras que en el caso contrario se debe escoger algu-
na regla y "desaplicarla"; pero ahora ya no existe un solo elemento del lado derecho, sino
varios y, por tanto, aumenta la gama de combinaciones posibles.
De hecho, hace apenas pocos años que se encontraron métodos eficientes para reali-
zar análisis sintácticos ascendentes, mientras que los descendentes fueron inventados hace
más de cuarentat.
Luego de esta somera revisión del análisis sintáctico podemos pasar a la siguiente
etapa en el proceso de la traducción.

Análisis semántico
Una vez terminada exitosamente la fase sintáctica o gramatical se está ya en posición de
entender el significado de la frase, por medio del análisis semántico.
Aquí lo importante es determinar la coherencia entre lo dicho por medio del lenguaje,
Disquisiciones sobre y los elementos del mundo a los que se está haciendo referencia. Nosotros efectuamos el
el lenguaje análisis semántico también en forma intuitiva e inmediata, lo cual nos permite darle sen-
tido a las frases y manejar ese maravilloso vehículo llamado lenguaje.
Las funciones del lenguaje son múltiples, y cubren desde la elemental necesidad de
describir el mundo para propósitos de supervivencia cotidiana hasta los juegos de pala-
bras y la significación poética.
En todos esos casos se sigue manteniendo, aunque con diversos matices, la razón
semántica: dotar a las palabras con la cualidad de mundo; dar contenido real a los conti-
nentes huecos representados por los meros símbolos lingüísticos.
Por ejemplo, el sentido de la frase "la casa es azul" es directo, porque en el mundo sí
existen casas con el atributo denotado por esas palabras; sin embargo la frase "la casa es
triste" maneja contenidos que ya no tienen un referente tan inmediato, porque las casas
que la experiencia nos muestra en el mundo son azules, rojas, grandes o pequeñas, pero no
son directamente tristes. Sin embargo, exhibiríamos una sensibilidad muy pobre si nos
negamos a entender que una casa pueda efectivamente (aunque no en forma directa) ser
triste.

(t) Las ideas originales sobre análisis sintáctico se deben a Donald Knuth, en el artículo "On the
Translation of Languages from Left to Right", aparecido en la revista Information and Control,
octubre, 1965; pero no fue sino hasta la publicación del artículo "An Efficient Context-Free Parsing
Algorithm", que es un resumen de la tesis doctoral de Jay Earley, publicado en Communications of
the Association for Computing Machinery de febrero de 1970, cuando se contó con un método
práctico, que luego se refinó más.
Sección 5.5 Compiladores 213

Pero hay más, mucho más. El poema "Piedra negra sobre una piedra blanca" del
enorme escritor peruano César Vallejo (1892-1938) inicia con la extraordinaria frase:

Me moriré en París con aguacero,


un día del cual tengo ya el recuerdo.t

O bien, ¿qué efecto nos causan estos elaborados pasajes, tomados casi al azar del libro El
ser y la nada, obra filosófica cumbre del escritor y literato Jean Paul Sartre (1905-1980)?t t:

La conciencia es un ser para el cual en su ser es cuestión de su ser en tanto que


este ser implica un ser otro que él mismo. (pág. 31)
Así encontramos nuevamente, en otro plano, una necesidad ontológica que ha-
bíamos señalado con motivo de la existencia de mi cuerpo para mí: la contin-
gencia del para-sí, decíamos, es la recuperación perpetuamente trascendida y
perpetuamente renaciente del para-sí por el en-sí sobre fondo de nihilización
primera. (pág. 431)

Por otro lado, del poeta Raúl Bañuelos (Editorial Universidad de Guadalajara, 1989) leemos:

La lluvia ama del río lo que tiene de agua


y por eso llueve.

Pero basta. Para el caso de un compilador, claro, "el mundo" es la computadora, sus
registros, sus celdas de memoria, etc. El análisis semántico efectuado por un compilador
averigua, por ejemplo, si una expresión dentro de un programa significa algo válido, y no
pide hacer una operación aritmética sobre una letra, cosa que no tendría sentido.
Una vez analizada a fondo una frase, y cuando ya se ha determinado su validez lexico-
gráfica, sintáctica y semántica, llega por fin el momento de traducirla. Es importante
observar que la traducción es lógicamente posterior a las operaciones de análisis. En algu-
nas referencias sobre compiladores se llama fase semántica a la parte encargada de lo
recién descrito, pero además suelen incluirse también allí las funciones de generación de
código que ahora se explican.

Generación y optimización de código


La traducción —o generación de código— busca representar la frase fuente original en
términos de elementos de un lenguaje mucho más sencillo, que ya no está dotado de
estructura. O sea, precisamente, llegar a traducir la frase fuente al lenguaje de máquina (o
por lo menos al lenguaje ensamblador).
En este nuevo ejemplo se muestra el concepto. Supóngase que se desea efectuar la
operación C = A + B donde las letras representan variables de tipo numérico. Un renglón
del programa fuente será, entonces,

C=A+B

(t) Si el lector no encuentra sentido en esta frase, respetuosamente le sugerimos alejarse algunos
años de la televisión e Internet y sus tristes influencias.
(tt) Este complejo libro de 1943, maravillosamente denso y extenso (casi 800 páginas), subtitu-
lado "Ensayo de ontología fenomenológica", Editorial Losada, Buenos Aires, 1972, contiene la
fundamentación teórica de la filosofía del existencialismo. Sartre ganó, y rechazó, el Premio Nobel
de Literatura de 1964.
214 Capítulo 5 Software de base

El compilador efectuará el análisis lexicográfico y luego el sintáctico, para determi-


nar que la estructura gramatical de la frase es

Hecho esto, procederá a determinar si las tres letras son variables numéricas defini-
das dentro del programa fuente. El siguiente paso es aplicar ciertas reglas predefinidas
para la generación de código. Estas reglas indicarán las operaciones a efectuar para tradu-
cir las expresiones primitivas que el análisis sintáctico determinó como componentes de
la frase fuente original. Para el ejemplo, la estructura del árbol sintáctico pide efectuar
—en ese orden— las siguientes reglas de generación de código intermedio:

1. +(X1, A, B)
2. =(C, X1, -)

La estructura de cada regla es como sigue: se especifica la operación que se aplicará


al triplete de elementos (en realidad, localidades simbólicas de memoria) dentro del pa-
réntesis. El primer elemento recibirá el resultado de la operación efectuada sobre los dos
siguientes. Para el ejemplo, la regla número uno especifica que se haga la suma de A y B
y se deje el resultado en un elemento temporal X1. La segunda regla hace la transferencia
de este elemento temporal a la variable c, y deja indefinido el tercer elemento, porque no
se requiere.
Aunque en el ejemplo está claro que este elemento temporal sobra, obsérvese que
esto no puede estar previsto en las reglas predefinidas que, por ser de carácter general, no
pueden saber que se desea dejar el resultado de la suma en la variable C.
Como se dijo, los elementos a los que nos estamos refiriendo no pueden ser otros que
celdas de la memoria de la computadora. Toca ahora seleccionar las celdas a utilizar, y se
usarán las que estén disponibles. Por lo pronto no importa mucho cuáles serán, sino cuán-
tas y en qué orden.
A la generación de código recién descrita se le conoce como generación de código
intermedio, porque no se trata de lenguaje de máquina, sino de una especificación relati-
vamente informal, en términos de elementos (temporales o no) producidos "a ciegas" por
medio de reglas predefinidas.
Una operación importante que se puede hacer sobre el código intermedio generado es
la optimización. Una vez producidas las líneas de código intermedio es posible analizadas
y tratar de eliminar redundancias y repeticiones. Nótese que esto no se puede hacer al
momento de generación de código intermedio, porque equivaldría a quitar a las reglas pre-
definidas su carácter genérico, y porque además hace falta el contexto y la interrelación
entre las diferentes instrucciones generadas.
Sección 5.5 Compiladores 215

En el siguiente ejemplo se ve la conveniencia de mantener la generalidad de las re-


glas de traducción y su manejo de elementos temporales. Del lado izquierdo aparecen las
frases fuente, y del derecho sus correspondientes traducciones a código intermedio.

C=A+B 1. +(X1, A, B )
2. .(C, X1, - )

C=0 3. =(C, 0, - )
D = E - (A + B) 4. -(X2, E, X1)
5. =(D, X2, - )

Independientemente del objetivo de ese programa, conviene manejar los elementos


temporales libremente, y sólo decidir si se eliminan o no en una segunda fase. En todo caso,
luego debe abordarse el problema de convertir el código intermedio (quizá ya optimizado)
en código objeto propiamente dicho; esto es, en lenguaje de máquina (o tal vez en
ensamblador). A esta nueva fase se le conoce como generación de código objeto.
Se describirá ahora la generación de código objeto a partir del código intermedio:

Programa Código Código


fuente intermedio objeto
C = A + El +(X1 , A, B ) CARGA A
=(C, X1, - ) SUMA B
GUARDA X1
CARGA X1
GUARDA C

C=0 =(C, 0, - ) CARGA-i 0


GUARDA C
D = E - (A + B) -(X2, E, X1) CARGA E
RESTA X1
GUARDA X2
=(D, X2, - ) CARGA X2
GUARDA D

Obsérvese cómo el código objeto (comúnmente llamado simplemente "código") es


de mayor extensión que el programa fuente; esto resulta natural, en virtud de la corres-
pondencia de uno a varios existente entre una expresión escrita en un lenguaje de alto
nivel y la equivalente escrita en lenguaje de máquina. Nótese también que aún hay redun-
dancias en el código generado. Esto resulta primero aparente en el cuarto renglón, donde
se hace un CARGA X1 redundante, puesto que en el renglón anterior se había hecho la
operación contraria y, por tanto, resulta innecesario cargar el acumulador con el valor que
ya contiene.
Todo compilador, pues, requiere de una última etapa: la optimización de código obje-
to. Cabe advertir al lector que estos ejemplos mínimos sólo permiten tener una idea vaga
del alcance, diversidad y magnitud de los problemas de la generación y optimización de
código intermedio y objeto.
La estructura funcional de un compilador aparece resumida en el siguiente diagrama,
donde se interrelacionan sus diversas fases y se conectan mediante estructuras de datos
comunes, conocidas como diccionarios de símbolos. Estas áreas de memoria guardan
216 Capítulo 5 Software de base

información relativa a los componentes sintácticos encontrados por el compilador a lo


largo de sus recorridos lexicográfico y sintáctico del programa fuente, junto con sus carac-
terísticas, importantes para la posterior generación de código (por ejemplo, el tamaño de
cada variable, su tipo, sus requerimientos de memoria, etcétera).

1.17,1•77,

ANALIZADOR LEXICOGRÁFICO
, DICCIONARIO
L DE
ANALIZADOR SINTÁCTICO SÍMBOLOS

ANALIZADOR SEMÁNTICO
• El

GENERACIÓN Y OPTIMIZACIÓN OTRAS


DE CÓDIGO INTERMEDIO ESTRUCTURAS
DE DATOS
GENERAU N Y OPTIMIZACIÓN COMUNES
DE CÓDIGO OBJETO

Figura 36 Estructura
funcional de un compilador

No debe perderse de vista que un compilador es un programa encargado de la traduc-


Cómo se escribe ción de otros programas, y por ende aparece de nuevo la conocida referencia circular
un compilador inherente al software de base. Como el objetivo es que la misma computadora se encargue
de traducir los programas fuente a objeto, entonces forzosamente el compilador debe
residir en la memoria, traducido ya a lenguaje de máquina. Pero ¿quién tradujo al traduc-
tor? Puesto que se está hablando de traductores de nivel 2 (es decir, "por encima" del
traductor ensamblador, T 1 ), la respuesta es que el primer compilador se diseñó y final-
mente se escribió en lenguaje ensamblador, y luego fue traducido a lenguaje de máquina
por el ensamblador ya existente.
Escribir un compilador no es tarea fácil, y resulta prácticamente imposible escribir
uno en un lenguaje tan poco expresivo como el ensamblador. Pero no hay salidas mági-
cas: históricamente, la primera vez que se escribió un compilador para una cierta máquina
tuvo que hacerse en ensamblador; a partir de allí ya resulta posible utilizarlo para escribir
otras versiones, empleando para ello el nuevo lenguaje recién obtenido. Desde hace mu-
chos años, los compiladores se escriben en lenguajes de alto nivel traducidos por otros
compiladores ya existentes (principalmente en C/C++), pero la primera vez esto no fue
posible: ¿en cuál horno se cuecen los ladrillos con los que se construye un horno?
Independientemente del lenguaje en el que esté escrito, en algunos casos el traductor
ejecuta inmediatamente el código obtenido, y recibe entonces el nombre de intérprete.
Un intérprete no almacena el código generado, sino lo ejecuta tan pronto lo obtiene. Hay
una gran diferencia de velocidad entre la ejecución de un programa (objeto) compilado y
un programa (fuente) interpretado. Lo segundo siempre es más lento, pues el intérprete
debe analizar, traducir y ejecutar cada instrucción, aun cuando ésta forme parte de un
ciclo de repeticiones.
En la actualidad, prácticamente toda la programación de computadoras se hace en
lenguajes de alto nivel, por medio de compiladores e intérpretes. La programación en en-
samblador se reserva para aplicaciones especiales, que requieren de una optimización
Sección 5.5 Compiladores 217

especial, hecha a mano, y para aplicaciones relacionadas con equipo y hardware especia-
les. También se emplea la programación en ensamblador para propósitos didácticos, pues
permite al afortunado estudiante establecer un contacto mucho más directo e "íntimo"
con la computadora y el modelo de von Neumann.
Existen, además, herramientas de programación especializadas en la construcción de
compiladores. Un generador de analizadores lexicográficos, por ejemplo, es un progra-
ma que recibe como entrada la especificación de la lexicografía de un lenguaje (cómo
deben arreglarse sus componentes léxicos o tokens), y produce como salida un programa
que realiza el análisis.
Un compilador de compiladores (compiler compiler), por otro lado, es un programa
que recibe como entrada la especificación de la gramática de un lenguaje de programación
y produce como salida el analizador sintáctico para esa gramática. Como se comprenderá,
estos son programas altamente complejos y elaborados.

IE!InEl!1!!!!!~§ 1111111111111M
ElE1!1=~El!I
1111E111!1~1M• uuumuuI-
uuuuuuuIfi
lelE~El~ uuounm-
1!■521MIEEIEI Compilador -3>

1==1E1=EIENI
EMIERIEME=Z
IMEI!!!!!1=1=11
1=1211=!1=1.
1=IE1!1 1
1:21!=~1:01
15211~1=ffil
EMnE21=12221111 Figura 37 Función de un
compilador

Ventajas de los compiladores


Gracias a los compiladores se obtiene algo que antes no era posible (además de la mucho
mayor claridad y poder expresivo de los programas fuente): la posibilidad de que un
mismo programa (fuente) pueda ser "entendido" por dos computadoras diferentes. Como
debe ya estar claro, en el nivel del lenguaje de máquina, las computadoras diferentes son
por completo incompatibles entre sí, porque si cada modelo de procesador es único, tam-
bién lo serán su diccionario electrónico y su unidad de control y, en general, todas sus
características funcionales. Lo mismo sucede para el caso del lenguaje (macro)en-
samblador, porque sus instrucciones no son sino "disfraces" mnemónicos del código de
máquina.
Eso implica una total falta de entendimiento entre computadoras de marcas y mode-
los diferentes, y también significa que un programa en ensamblador debe reescribirse si Compatibilidades e
se desea ejecutarlo en una máquina diferente de aquella para la cual fue originalmente incompatibilidades
diseñado.
Pero en el nivel de los lenguajes dotados de estructura sintáctica y semántica (y sus
correspondientes traductores) la situación aparece muy diferente, pues ahora los progra-
mas fuente ya no dependen de las características de la máquina, sino sólo de las especifi-
caciones formales del lenguaje. Es tarea del compilador, como se ha visto, traducir esos
programas al lenguaje ensamblador específico de la computadora en donde funciona.
Es decir, si se tiene un programa P escrito en el lenguaje L, por ejemplo, entonces
deberá existir un compilador de L para cada máquina en la que se desee ejecutar P. Con
ello, efectivamente el mismo programa puede operar en computadoras diferentes, siem-
pre y cuando cada una cuente con su propia versión de ese compilador para traducirlo a su
propio lenguaje de máquina. A esta característica de poder compartir programas fuente
entre computadoras diversas se le conoce como portabilidad, y la clave para ello reside
en los compiladores y en la independencia del hardware que ofrecen.
218 Capítulo 5 Software de base

ot
Programa P
escrito en
lenguaje X

17-
i5
'o Compilador de X 11 'o Compilador de X 11
I
para computadora 1 I
Í ;

;
para computadora 2

Figura 38 La portabIlIdad
de los programas fuente
COMPUTADORA 1 COMPUTADORA 2

En términos generales, las fases de análisis lexicográfico, sintáctico y de generación


I
1

de código intermedio de un compilador del lenguaje L para la máquina A serán idénticas


a las del compilador de ese mismo lenguaje para la máquina B. Lo que seguramente sí
cambiará será la fase final de generación y optimización de código objeto de los dos
compiladores, porque cada versión está por completo enfocada a las particularidades de la
máquina A y de la máquina B, en forma independiente y específica para cada caso.
Puesto que un lenguaje de programación es en principio independiente del hardware,
para una misma máquina podrá haber tantos lenguajes diferentes como compiladores exis-
tan. El desarrollo de los lenguajes de programación (y sus correspondientes compiladores)
ocurre a finales de la década de 1950, durante la segunda generación de computadoras, y
no se ha detenido. En la actualidad existen decenas y decenas de lenguajes diferentes,
aunque no todos gozan de la misma popularidad, ni existen compiladores para todos ellos
en todas las clases de computadoras -t.
Para el extendido caso de las computadoras personales, se puede encontrar en el mer-
cado una enorme variedad de lenguajes y compiladores, para cualquier gusto y afición.
En el anexo al final de este largo capítulo hay una somera historia de los lenguajes de
programación más importantes.
Muchas carreras universitarias de computación incluyen al menos un curso sobre
compiladores (subárea 5.1.2 de los Modelos Curriculares), y el tema también se estudia en
los niveles de posgrado. Se recomiendan las referencias [AHOS90], [LEMK96] y [TEUT95].
Logramos ya el objetivo especificado al inicio: hacer una herramienta para comunicar-
Ahora se con la computadora usando un lenguaje más cercano al nuestro, y menos dependiente de
la comunicación los detalles particulares de la arquitectura de la máquina.
con la computadora Pero esto tiene un precio; comunicarse con la máquina mediante un compilador añade
un paso más a los descritos al final del apartado 5.4, porque ahora es necesario compilar
requiere tres pasos
un programa, ensamblarlo, y cargarlo para ejecutarlo, en ese orden.
antes de la ejecución.
A continuación se muestra el detalle de los pasos requeridos para llegar a ejecutar un
programa fuente escrito en, por ejemplo, el lenguaje de programación Pascal.
Es necesario suponer la existencia de los siguientes programas del sistema (como se
había mencionado, son los programas objeto especializados que hacen posible la poste-
rior ejecución de los otros programas: las aplicaciones escritas por los programadores):

(t) ¿Hará falta recordar al lector que no todas las computadoras son personales?
Sección 5.5 Compiladores 219

Minicargador (en ROM)


Cargador: en el disco D o Dos clases
Ensambldor:en isco DE de programas
Compilador de Pascal: en el disco D p

Y, claro, existe un programa fuente escrito en Pascal por el programador, o tal vez por
algún feliz estudiante en época de exámenes. Supondremos que ese programa fuente de
aplicación está en el disco DAP.
Estos son, entonces, los pasos requeridos para la ejecución:

0. Dar boot (si es necesario), para cargar el minicargador.


1. CP <— O {F: Do , D: 500}
(Cargar el cargador: tomarlo del disco D o y depositarlo a partir de la celda 500 de
la memoria.)
2. CP F 500 {F: DE , D: 5,500}
(Cargar el ensamblador: tomarlo del disco DE y depositarlo en la memoria a partir
de la celda 5,500.)
3. CP 500 {F: D p , D: 25,500}
(Cargar el compilador de Pascal: tomarlo del disco D p y depositarlo en la memo-
ria a partir de la celda 25,500.)
4. CP <— 25,500 {F: D A, , D: D i }
(Compilar el programa fuente de aplicación: tomarlo del disco D Ap y dejar el
programa intermedio resultante —ya traducido a ensamblador— en el disco D,.)
5. CP <— 5,500 {F: D I , D: D2 }
(Ensamblar el programa intermedio: tomarlo del disco D I y dejar el programa
objeto resultante en el disco D 2.)
6. CP 500 {F: D2 D: 230,000}
(Cargar el programa objeto: tomarlo del disco D2 y depositarlo en la memoria, a
partir de la celda 230,000.)
7. CP <— 230,000 {F: ? , D: ?}
(Finalmente, ejecutar el programa objeto, que inicia en la celda 230,000. Como
no se dijo nada específico sobre el programa fuente en Pascal (ahora ya programa
objeto), no sabemos —ni nos preocupa en este ejemplo— de dónde va a tomar
sus datos y hacia dónde va a escribir sus resultados, si es que lo hace.)

Estos siete pasos detallados (el paso O es manual) configuran lo que arriba se agrupó
en tres: compilar, ensamblar, cargar y ejecutar.
El lector debe comprender que el calificativo "detallados" recién empleado no es
sino una muy gruesa aproximación, porque en realidad para nada nos hemos preocupado
de la enorme cantidad de detalles involucrados en, por ejemplo, leer un archivo del disco,
pues harían falta hojas y hojas de instrucciones para precisar la cantidad de bytes que lo
componen, sus posiciones en los sectores de las pistas de las caras de los platos del disco,
así como una enorme lista de las particularidades y minucias específicas necesarias para
el funcionamiento de todo el esquema en una computadora real.
Para nuestros fines será suficiente con observar atentamente los siete pasos descritos
y darnos cuenta que son una descripción de cómo lograr algo (en este ejemplo, la ejecu- Los pasos anteriores
ción de un programa fuente). ¿Y acaso esa descripción no puede constituir a su vez un forman
programa? Finalmente, un programa no es mas que la descripción de un cierto proceso, una descripción.
escrita en algún lenguaje, y por ello ahora trataremos de expresar a la computadora, me-
diante un lenguaje, los pasos a efectuar, para que la máquina tome el control de las opera-
ciones de ahí en adelante. Y esto nos lleva a la siguiente sección.
220 Capítulo 5 Software de base

5.6 SISTEMAS OPERATIVOS

Cuando en la página 206 se hablaba de un monitor, la referencia era a un programa residente


en el sistema de cómputo, cuya función es controlar los procesos que en él suceden. Ahora
inicia el tema de los sistemas operativos y se estudiará la idea con más detenimiento.
Como ya se ha visto, el simple hecho de intentar la ejecución de un programa escrito
en un lenguaje de alto nivel implica ejecutar varios programas más —no diseñados por cada
programador, sino parte del software de base— como cargadores, ensambladores, compi-
ladores, editores, etc. Diremos rápidamente que todos estos programas son coordinados
por otro (mucho más grande y complejo): el sistema operativo. En este sentido parece no
haber gran diferencia entre un monitor y un sistema operativo y, en efecto, ambos parten
de la misma idea: que sea la propia computadora la que lleve el control de los procesos,
quitando al usuario esa responsabilidad.
La diferencia comienza a ser notoria si nos detenemos a pensar en que un procesador
común es capaz de ejecutar millones de instrucciones por segundo, y que resulta absurdo
dedicar esos recursos a una sola persona. ¿Qué pasa, por ejemplo, cuando el programa de
aplicación pide un dato por la pantalla? Sucede que el procesador se detiene a esperar a
que el usuario teclee el valor esperado y, mientras tanto, pueden pasar varios segundos (¡o
minutos!). En todo ese tiempo el procesador estará desperdiciando la oportunidad de eje-
cutar millones y millones de instrucciones, dedicado sólo a esperar. Claramente, esto no
Funciones de un es correcto. Surge entonces la idea de aprovechar el intervalo en que el programa no está
sistema operativo utilizando el procesador para asignarlo a alguno otro. He aquí una tarea importante para el
recién introducido concepto de sistema operativo.
Otra función que se le puede dar, dependiente de la anterior, es compartir los recursos
de la máquina entre varios procesos (locales o remotos), lo cual incluye al procesador
mismo si se le considera como un recurso del sistema de cómputo (al igual que la memo-
ria y los discos magnéticos). Todo esto se origina en la gran velocidad de funcionamiento
del procesador central, que le permite atender a varios procesos.
Del hecho de compartir el procesador sigue un amplio conjunto de tareas afines, tales
como compartir la memoria central, el espacio en disco, la impresora, etc. Por tanto, es
necesario administrar eficientemente el sistema de cómputo como un todo armónico. Y no
sólo eso: también debería permitirse a los diferentes usuarios comunicarse entre sí y prote-
gerlos unos de otros; se requiere que puedan almacenar información durante plazos me-
dianos o largos, y darles además la facilidad de utilizar de manera sencilla todos los recursos,
facilidades y lenguajes de la computadora. Por si todo lo anterior fuera poco, también se
desea poder comunicar esa computadora con otras dentro de una o varias redes.
Podría resumirse la tarea de un sistema operativo diciendo que su función central es
administrar y organizar los recursos de la computadora para su mejor utilización, en bene-
ficio del mayor número posible de usuarios.

Modelo de estudio para los sistemas operativos


El análisis de los sistemas operativos suele dividirse en funciones jerárquicas, desde nive-
les muy cercanos a la máquina misma hasta niveles más virtuales, en el sentido de que ya
no tratan a la computadora como una máquina (dotada de un procesador y de memoria,
etc.), sino como un esquema diseñado para manejo de información, sin preocuparse de-
masiado por detalles como registros, direcciones, etcétera.
Esta es una idea fundamental; un sistema operativo convierte a una máquina real en
una computadora virtual, capaz de hacer cosas cualitativamente superiores a las de su
contraparte física. Para entender esto, piénsese en el concepto usual de una computadora
y se apreciará cómo, en general, uno se refiere a una computadora en términos virtuales,
Sección 5.6 Sistemas operativos 221

y no reales ("una computadora es un cerebro electrónico capaz de almacenar muchísima


información y manejarla y procesarla a enorme velocidad", o "es la encargada de cobrar-
me el teléfono")t.
Si se considera a la máquina por un lado y al ser humano por el otro, inmediatamente
saltará a la vista un abismo en sus capacidades de comunicación; el lenguaje manejado
por la computadora no es más que una burda imitación del lenguaje de los humanos y, por
tanto, la comunicación que se puede establecer entre máquina y hombre es rudimentaria.
La función general del software de base, como hemos dicho, consiste hacer más fácil el
camino que nos separa de las computadoras, y la tarea de los sistemas operativos en par-
ticular consiste en lograr una comunicación donde aparezca una imagen virtual de la compu-
tadora y no necesariamente se note que enfrente de nosotros hay un aparato dotado de un
acumulador y cien millones de celdas de memoria.

Figura. 39 Un modelo
de estudio para los sistemas
operativos

Esta no es una tarea fácil, pues requiere la automatización de cientos (o miles) de


pequeñas tareas, que van de lo trivial a lo enormemente complejo. La jerarquización men-
cionada parte del hecho de que un sistema de cómputo está configurado (como se vio en
los capítulos anteriores) alrededor del procesador y de la memoria, y a partir de estos
recursos se van formando herramientas de software para utilizar la máquina como sistema
y no como una agrupación de elementos aislados.
El esquema que suele seguirse para el estudio de los sistemas operativos está formado
por "capas" concéntricas alrededor de un núcleo. La parte interna del conjunto jerárquico Sistemas operativos
de programas que forman un sistema operativo recibe precisamente el nombre de núcleo modulares
(kernel, en inglés). Las otras capas o subsistemas se encargan del manejo de la memoria,
el procesador, los dispositivos de entrada/salida, los archivos, etcétera. En un inicio (du-
rante la primera parte de la tercera generación de computadoras), los sistemas operativos

(t) ¿Quién no ha oído aquello de que "las computadoras no se equivocan"?


222 Capítulo 5 Software de base

eran grandes piezas de software, construidos en forma que bien podría llamarse monolítica,
pues en realidad era un gran programa, formado por partes fuertemente interrelacionadas.
A partir del sistema operativo Unix, en la década de 1970, las cosas comenzaron a cam-
biar, porque se inauguró una tendencia (que continúa hasta la fecha) a escribir sistemas
formados por un núcleo (preferentemente pequeño) y un amplio conjunto de módulos
encargados de las demás funciones de control de la computadora. Cuando una de estas
capas modulares requiere de algún servicio del núcleo, lo invoca mediante una llamada
al sistema (system call, en inglés). De esta forma, en los sistemas operativos actuales, el
control básico de la computadora reside en el núcleo (que siempre está cargado en la memo-
ria) y para las demás funciones se hace uso de una gran cantidad de módulos adicionales.
El caso extremo de esta idea lo constituyen los llamados sistemas operativos distribui-
dos, en los cuales ni siquiera es necesario que todos los módulos estén presentes en la
misma computadora, pudiendo encontrarse en alguna otra que esté conectada en red.
Para el siguiente análisis es necesario definir algunos términos. Se llama programa a
Definiciones iniciales un conjunto de instrucciones escritas en algún lenguaje de computación (no importa si
para el estudio inició en un lenguaje de alto nivel, pero ahora es una cadena de unos y ceros del lenguaje
de los sistemas de máquina). La característica principal de un programa, en este sentido, es que es la especi-
operativos
ficación de un conjunto de instrucciones estáticas, porque están escritas y aún no se han
ejecutado. Son sólo un adelanto de lo que va a suceder cuando se les dé vida. Al ejecutar
un programa no se observa ya un conjunto de instrucciones, sino de acciones: las instruccio-
nes en estado activo. Se llama proceso a un conjunto de acciones —dinámicas— resul-
tado de la ejecución de un programa. Es decir, el concepto de programa es anterior
necesariamente al de proceso. El agente que da vida a una instrucción (para convertirla en
acción) se llama, por supuesto, procesador.

Proceso
en ejecución
(dinámico)

Figura. 40 Programa
vs. proceso

Se dice que dos procesos son simultáneos cuando se ejecutan en el mismo instante, y
se les llama concurrentes cuando se ejecutan (acción por acción) en el mismo intervalo de
tiempo. Es decir, para que exista simultaneidad entre n procesos se debe forzosamente contar
con n procesadores, mientras que para ejecutarlos concurrentemente tan sólo se requiere
"repartir" el procesador entre ellos a una velocidad tal que, por unidad de tiempo (o interva-
lo), todos reciban atención (aunque sea parcial). Este último concepto —base de la multipro-
gramación y el tiempo compartido— recibe el nombre de multiplexación en tiempo.

El núcleo del sistema operativo


Ojalá sea claro que el problema de la concurrencia entre procesos tiene que resolverse en
el nivel más cercano posible al núcleo del sistema operativo, porque la multiplexación del
procesador es una operación primitiva; es decir, combinándola con otras sirve para for-
mar funciones más complejas dentro del sistema.
Sección 5.6 Sistemas operativos 223

Las funciones básicas de un núcleo consisten en tomar el control del procesador y


determinar cuándo y cómo se va a repartir entre los diversos procesos activos.
En términos generales sucede que el procesador abandona el proceso en ejecución y
dedica su atención a ejecutar otro cuando el primero entra en algún estado de espera.
Como se dijo, un proceso pasa a estado de espera cuando pide efectuar alguna operación
muy lenta en comparación con las velocidades normales de procesamiento. Las operacio-
nes lentas casi siempre son las de entrada/salida de datos de la computadora desde/hacia
el mundo exterior. Surge así una primera pregunta: ¿cómo detecta el núcleo del sistema
operativo cuando se trata de una operación lenta?
El problema se resuelve clasificando el conjunto de operaciones que un procesador
puede efectuar en "normales" o "privilegiadas". Por definición, una operación privilegia- Manejo
da es aquella que al ser ejecutada causa que el procesador entre en un estado especial de interrupciones
llamado interrupción (véase la pág. 102).
Durante una interrupción, el procesador se detiene momentáneamente y determina si
puede (o debe) seguir ejecutando. Para ello, automáticamente ejecuta un pequeño progra-
ma de atención a la interrupción que averigua la causa de ella y determina los pasos a
seguir. Ese programa de software de base, claro, fue previamente escrito por un progra-
mador de sistemas, y reside en un área preespecificada de la memoria central.
Un ejemplo aclarará esto. Supóngase que un proceso pide la ejecución de una opera-
ción (privilegiada) de entrada/salida. El hardware del procesador está diseñado de tal
forma que en ese momento se produce una interrupción; inmediatamente ocurre un desvío
en la secuencia de la ejecución de instrucciones y, en vez de continuar ejecutando el progra-
ma, comienza a ejecutar una rutina prefijada de atención a la interrupción que averigua la
causa del desvío y determina, para este caso, que se intentó ejecutar una operación privile-
giada de entrada/salida. Además, indica al procesador lo que se debe hacer a continuación. ¿Qué hacer
Toca entonces determinar cómo aprovechar el procesador mientras el proceso original en los tiempos
es atendido por algún dispositivo de entrada o salida. Esto depende, naturalmente, de si hay muertos?
o no otros procesos participando en la multiplexación de la unidad central de procesamiento
(es decir, compitiendo por el procesador).
Si no hay otro proceso en espera del procesador, tan sólo se devuelve el control al
proceso original, y el procesador espera pacientemente —desperdiciando miles de millo-
nes de ciclos de máquina— a que se complete la operación de entrada/salida deseada.
Esto recibe en inglés el nombre de idle-wait, (espera ociosa); durante estas esperas el
procesador puede ejecutar un programita para, por ejemplo, dibujar un pequeño reloj en la
pantalla, pidiendo calma al impaciente usuario.
Pero si existen más procesos en estado de espera, entonces sucede algo muy interesante;
el proceso original se "congela" —luego veremos cómo— y se deja en estado de espera,
mientras otro proceso se "descongela" y recibe el control sobre el procesador. Ahora la opera-
ción del sistema de cómputo sigue como antes, ¡pero ejecutando un proceso diferente!
Por supuesto, en una computadora real todos estos pasos no toman más de algunas
pocas milésimas de segundo.
Surgen muchas preguntas. ¿Cómo se representa un proceso dentro de una compu-
tadora? ¿Cómo se activa y desactiva un proceso? ¿Cómo se escoge el proceso que se acti-
vará ahora? ¿Quién y cómo decide el conjunto de operaciones necesarias para lograr todo
esto? Se intentará responderlas conforme se analice el núcleo del sistema operativo.
El kernel está formado, en términos generales, por tres subsistemas. El primero se
Composición
encarga de manejar las interrupciones del procesador central, de la manera ya descrita.
El segundo tiene como función escoger (y activar) un nuevo proceso para ser ejecutado, del núcleo: manejo de
y la operación inversa ("congelar" el que fue interrumpido). El tercer programa cumple interrupciones,
la muy importante función de coordinar los diversos procesos (del sistema operativo y despachador, control
de los usuarios) que interactúan en el núcleo del sistema operativo para que no interfie- de concurrencia
ran entre sí.
224 Capítulo 5 Software de base

Todo esto es tema de un estudio más detallado, fuera del alcance de nuestro curso. Por
lo pronto sólo diremos que las tres funciones son desempeñadas por otros tantos procesos
del núcleo, conocidos como manejador de interrupciones de bajo nivel, despachador y
semáforos (o control de concurrencia). En las referencias [MADS74], [SILG99] y [STAW97]
se pueden encontrar capítulos completos con descripciones mucho más detalladas.
El despachador "congela" un proceso almacenando —en registros especiales de la
Cómo se desactiva UCP— los datos volátiles resultantes de su ejecución hasta antes de ser desactivado. Por
un proceso ejemplo, si se almacenan los contenidos de los diferentes registros de trabajo, del acumula-
dor, y de otros elementos del procesador central que intervienen en un cálculo, el proceso
puede ser desconectado del procesador sin perder el avance de lo obtenido hasta ese mo-
mento. Para esto sirven operaciones como las descritas en la pág. 130 dentro de la clase de
instrucciones privilegiadas del lenguaje de máquina.
Cuando proceda, el despachador reactivará ese proceso simplemente copiando los
contenidos de esos registros especiales de regreso en el acumulador y demás registros de
trabajo de la UCP, de modo que el proceso recién despertado pueda continuar su ejecución,
como si nada hubiera sucedido.
Los registros especiales donde el despachador almacena la información volátil de un
proceso que se desactivará reciben el nombre (establecido por IBM desde la segunda
generación) de Program Status Word (PSW) o vector de estado.
No debe confundirse, sin embargo, el almacenamiento de la información volátil de
un programa con el hecho —que no sucede— de guardar el contenido de las celdas de me-
moria utilizadas por el proceso.
Claramente esto último no tendría sentido, pues para almacenar la información conte-
nida en, digamos, diez mil celdas de memoria, se requieren, precisamente, diez mil celdas
adicionales, lo cual resulta absurdo. Es obvio que deben mantenerse separadas esas diez
mil celdas (pertenecientes a un proceso en particular) y no permitir su utilización por
ningún otro, so pena de perder la información original.
Esto llevará naturalmente a la segunda función del sistema operativo: administrar el
uso de la memoria, aunque todavía falta indagar un poco más sobre los procesos. Con-
sidérense los problemas que enfrenta el sistema de cómputo para dar atención concu-
rrentemente a seis usuarios (locales o en red, para esta parte del sistema es prácticamente
indiferente). Por un lado, debe multiplexar el procesador entre sus seis clientes y, a la vez,
dar el control a las rutinas del sistema encargadas de muchas tareas auxiliares (desacti-
var/activar procesos, mandar mensajes, mantener las pantallas activas, controlar las opera-
ciones globales, etc.). También le toca determinar un orden óptimo de multiplexación
(analizado más adelante) y subdividir la memoria en al menos seis porciones, una para
cada proceso activo. En todo momento, además, debe mantener el control sobre cada uno
de los procesos.
Los procesos pueden estar en uno de varios estados: activos, en ejecución, o bloquea-
Estados dos (residentes en disco magnético). Un proceso se llama activo cuando está "congelado"
de un proceso pero residente en memoria central y disponible para ejecutar en cuanto haya oportunidad.
Al ser "descongelado" por el despachador del núcleo, el proceso elegido (sólo puede
haber uno a la vez) pasa al estado de ejecución, y gozará de ese privilegio durante un corto
período. Como se dijo, el núcleo bloqueó un proceso y lo sacó de memoria central cuando
intentó ejecutar una instrucción de entrada/salida, porque este tipo de operaciones toman
mucho tiempo (en comparación con la velocidad del procesador) en ser atendidas. Más
adelante se explica cómo manejar los diferentes procesos para lograr la atención concu-
rrente de varios usuarios.

Manejo de memoria
La función del manejador de la memoria consiste en mantener un espacio en ésta para
todos los procesos activos dentro del sistema.
Sección 5.6 Sistemas operativos 225

Existen varias maneras de manejar la memoria en un sistema de cómputo. La más sen-


cilla es asignar toda la memoria disponible a un solo usuario, pero esto no permite más Esquema básico:
que un solo cliente en operación. Este es el esquema más simple, el que más recursos des- memoria única
perdicia, el que surgió durante la primera generación de computadoras ...y el que más se asignada
usa en la actualidad en las computadoras personales (aunque algunos insisten en que ya
estamos más allá de la quinta generación), porque dedican todos sus considerables recur-
sos a un solo individuo, aunque el usuario se tarde varios segundos en encontrar la tecla
adecuada para la función deseada o se levante a prepararse un café; por supuesto, se puede
aducir correctamente que para eso son computadoras personales, y que además tienen la
capacidad de mantener varias "ventanas" abiertas a la vez, como se verá más adelante.
Una primera forma de permitir la multiprogramación (o sea, la activación de varios
procesos (y usuarios) simultáneamente, aunque sólo se pueda -éjecutar uno a la vez) recibió Ejecución de varios
el nombre de manejo de memoria por particiones, y consistía en subdividir la memoria en procesos en forma
varias secciones fijas, asignando cada una de ellas a un usuario o proceso activo. El principal concurrente
problema por resolver fue cómo asegurar que ningún usuario interviniera en el área de
memoria de otro. Desde este punto de vista, el manejo de memoria consiste en controlar
cuáles particiones están asignadas a cuáles procesos, para poder liberar particiones cuando
los procesos residentes en ellas terminen o cambien, y ofrecer particiones libres a procesos
que soliciten atención por parte del sistema de cómputo.
La ventaja fundamental de este modelo (empleado durante la segunda generación) es
que permitía la multiprogramación, y su principal desventaja consiste en que dejaba espa-
cios libres en la memoria; como éstos son de tamaño fijo, únicamente podrán ser utiliza-
dos por procesos de longitud menor o igual a la partición en cuestión. Sucedía todo el
tiempo que, por ejemplo, aun cuando hubiera dos particiones libres de diez mil celdas
cada una, no se podía dar atención a un proceso (que se encontraba a la espera en el disco
magnético) que mide doce mil celdas de longitud. La razón técnica es por falta de una
partición adecuada, aunque sí existe área suficiente en la memoria, sólo que está particio-
nada. Este problema, llamado fragmentación externa, se podría evitar permitiendo a las
particiones fusionarse, para lograr otras nuevas de más capacidad.
Esto dio lugar a otro esquema de manejo de memoria: particiones relocalizables. La
idea consistía en mover celdas de memoria de un lugar a otro para reunir las áreas libres
en un mismo lugar. Las celdas no se mueven, sino que sus contenidos se copian de un lugar
a otro, y aunque con esto se crea un nuevo problema —el de la relocalización—, permite
mayor flexibilidad que el anterior aunque resulta más costoso, puesto que los procesos
deben compactarse (moverse) durante la ejecución y además se requieren algunos cam-
bios de diseño en el procesador central, para evitar que este desplazamiento cause proble-
mas con respecto a las direcciones.
Sucede que si un proceso estaba cargado, por ejemplo, a partir de la celda 1000, las
direcciones absolutas de su espacio de direcciones tienen esta celda como origen. Pero si El problema
ahora se relocaliza y se coloca a partir de la celda 3478, entonces las referencias a todas de la relocalización
las direcciones deben alterarse —sumándoles el desplazamiento de 2478 celdas—, a fin
de que el proceso pueda continuar su ejecución como antes. El procesador central se
encarga de este ajuste durante la ejecución por medio de un componente electrónico adi-
cional conocido como registro de relocalización.
Para evitar complicaciones, en algunos sistemas se recurre al sencillo expediente de
quitar por completo de la memoria los procesos bloqueados, copiándolos al disco magné-
tico y liberando así un área significativa en la memoria central. Cuando llegue el momen-
to de volver a ejecutar el proceso, se cargará nuevamente trayéndolo del disco magnético
en el que reside. Este esquema recibe el nombre de swapping (intercambio), y fue el em-
pleado inicialmente por el sistema operativo Unix.
Debido a los costos que representaba la compactación (pues es necesario detener la
ejecución del proceso para efectuarla) o el swapping (porque el traslado hacia/desde el
226 Capítulo 5 Software de base

disco magnético toma tiempo), durante la tercera generación se inventó otro esquema,
Manejo de memoria más ágil y eficiente, llamado paginación. Consistía en dividir los procesos en fragmentos
por fragmentos: de longitud fija llamados páginas, que se almacenan en áreas de igual tamaño en me-
paginación moria, llamadas bloques. Esto es, cada página de cada proceso se guarda en un bloque en
memoria. Un proceso normal puede constar de cuarenta páginas, residentes en memoria
en otros tantos bloques. La ventaja del esquema radica en que no es necesario que las
páginas de un proceso estén contiguas en la memoria, quedando automáticamente elimi-
nado el problema de la fragmentación externa. La flexibilidad ofrecida por el manejo de
memoria por paginación es muy grande, porque un programa puede entrar a la memoria
"por pedazos", con lo que se elimina también la necesidad de compactar espacios libres.
Pero esto tiene un costo, porque entonces —en virtud de que las páginas no están acomo-
dadas en bloques contiguos de memoria física— ya no se puede asegurar lo que antes era
obvio: del byte de programa con dirección n sigue el byte con dirección n+ 1, debiéndose
necesariamente averiguar la localidad de memoria física en donde se encuentra cada ins-
trucción (es decir, en cuál bloque está cargada la página que la contiene); para ello, cada
proceso en ejecución tiene una tabla de mapeo (asociación) de páginas, en donde se
indica cuáles páginas residen en cuáles bloques de memoria. El costo de este requeri-
miento es alto: debe efectuarse un ciclo de fetch (a la tabla de mapeo de páginas) por cada
ciclo de fetch de las instrucciones del programa.
Lo anterior es grave, porque implica que la velocidad de procesamiento se reduce
casi a la mitad, pues el gasto operativo para ejecutar una instrucción aumentó considera-
blemente. Para salvar la situación y mantenerla en un nivel aceptable de eficiencia se hace
imprescindible utilizar hardware especializado para auxiliar en la compleja tarea de la
ubicación de las instrucciones y su posterior relocalización. Uno de esos medios es la ya
mencionada memoria caché o memoria auxiliar rápida (cf. pág. 106), que guarda los conte-
nidos activos de la sección de la tabla de páginas en uso, reduciendo grandemente el tiempo
extra requerido por cada consulta. Los microprocesadores recientes trabajan en colabora-
ción con complejos subsistemas electrónicos para el manejo de las tareas de paginación,
conocidos como unidades de manejo de memoria (MMU: Memory Management Unit).
De acuerdo con lo dicho, si ya no es necesario que todas las páginas de un proceso
estén cargadas de forma contigua en la memoria (gracias a las tablas de páginas y al MMU),
entonces tampoco se requiere que todas las páginas de un proceso determinado estén
residentes (contiguas o no) en memoria. Es decir, se podría comenzar a ejecutar un proce-
Paginación por so cuando tan sólo una parte del mismo esté cargada en memoria, e ir cargando a tiempo
demanda y memoria de ejecución las páginas requeridas.
virtual Esta importante (y audaz) idea recibe el nombre de memoria virtual (cf. pág. 121), y
es la base sobre la cual descansa el enorme poderío de las computadoras actuales y la
razón por la que una máquina (multiusuario o servidor) puede atender a muchos procesos
al mismo tiempot aunque disponga de una memoria limitada. Como se dijo, los micropro-
cesadores actuales ofrecen facilidades integradas de hardware para auxiliar en los mane-
jos de la memoria virtual.
Cuando un proceso pide una página no residente en la memoria, el sistema operativo
lo detecta por medio de una interrupción, atendida por el manejador de interrupciones del
núcleo. Éste determina la causa (interrupción por página) y copia la información solicitada
—residente en el disco magnético— hacia algún bloque libre de la memoria. Si no hubie-
ra espacio disponible, entonces el manejador de memoria se encarga de seleccionar alguna
página poco utilizada de otro proceso (apropiadamente conocida como "víctima") y co-
piarla al disco de donde originalmente vino —si es que tuvo cambios—, para en su lugar
insertar la página solicitada por el proceso que recién acaba de interrumpir; de esta elabo-

(t) Realmente no es "al mismo tiempo": conocemos la diferencia entre simultaneidad y concurrencia.
Sección 5.6 Sistemas operativos 227

rada forma el proceso podrá entonces seguir ejecutando como si hubiera estado por com-
pleto residente en la memoria.
Este nuevo esquema de manejo de memoria virtual se llama paginación por demanda.
Sus ventajas son obvias, pues permite una tremenda flexibilidad en el uso de los recursos
del sistema: ahora se puede dar cabida a un gran número de procesos en la memoria
aunque no haya espacio suficiente para todos a la vez. Es decir, la memoria de la compu-
tadora parece aumentar en forma ilimitada (pues le cabe "todo"), y de ahí su nombre. Su
desventaja es, fundamentalmente, su enorme complejidad. En efecto, los sistemas opera-
tivos de este tipo constan de millones de instrucciones; son escritos por grupos enteros de
programadores durante largos meses y tienen errores como el tan frustrantemente común
"Ha ocurrido una operación inválida y el sistema se apagará". Además, se requiere del ya
mencionado y muy considerable auxilio por parte de los circuitos electrónicos del proce-
sador, para no disminuir radicalmente la velocidad de procesamiento por la enorme canti-
dad de operaciones adicionales que el sistema debe ejecutar.

ucp1 .< 1>

Proceso Página

Memoria Disco

Tabla de mapeo
de páginas Figura. 41 Memoria virtual

Existe otra posibilidad de manejo de memoria que también permite memoria virtual,
conocida como manejo de memoria por segmentación. Aquí, los procesos se dividen en La segmentación
fracciones llamadas segmentos. Un segmento es una unidad lógica autocontenida (un progra- como otro esquema
ma completo, un módulo o un área grande de datos) cargada en forma independiente en la de manejo de
memoria. La diferencia con respecto a las páginas es que aquéllas son de longitud fija, mien-
memoria
tras que los segmentos son variables, dependiendo de la cantidad de código contenida en el
programa o módulo que representan. El manejo de segmentos es parecido al de páginas,
aunque tiene ciertas ventajas sobre éste, que ya no se estudian aquí por tratarse de un tema
especializado. Basta con saber que una máquina con sistema operativo de segmentación es por
lo menos tan poderosa y compleja como otra que maneje memoria virtual por paginación.
Otro método —el más complejo de todos— combina las ventajas de la paginación por
demanda con las de la segmentación; recibe el nombre de segmentación-paginación, y
reúne las ventajas de ambos esquemas, porque los segmentos se paginan por demanda,
con lo cual en principio se obtiene una mayor flexibilidad. Como antes, el auxilio del hard-
ware es imprescindible para operar con eficiencia. En [TANW98] y [STAW97] se estu-
dian estas técnicas de memoria virtual con mucho detenimiento.
Como se ha visto, el problema del manejo de memoria es extenso y complicado, pero
es fundamental para comprender los sistemas operativos. Sin embargo, apenas se han
comentado dos capas del modelo: el núcleo y el manejador de memoria.
228 Capítulo 5 Software de base

Lo que sigue es determinar el orden y los criterios para dar atención a los diversos
usuarios de un sistema de cómputo o, en otras palabras, definir cuáles procesos estarán
activos y cuándo. Esta es función del siguiente nivel.

Manejo del procesador


Esta sección del sistema operativo (a veces conocida como despachador de alto nivel o
scheduler) se encarga de determinar el orden óptimo de atención a los diversos procesos
que están compitiendo por ganar la atención del procesador central. Su principal caracterís-
tica es la capacidad de afrontar la indeterminación, es decir, el desconocimiento del orden
de aparición de los diversos procesos, su número y composición. Para emplear un tér-
mino de la filosofía existencialista, una computadora simplemente "está ahí" y no se pue-
de predecir el uso que darán al procesador los numerosos procesos existentes, por la sencilla
razón de que no hay un plan de acción determinado, pues los procesos son independientes
unos de otros y pertenecen a usuarios que no se conocen entre sí o incluso están en dife-
rentes continentes.
El factor de indeterminación hace que los programas de manejo del procesador sean
complicados y deban considerar criterios estadísticos y suposiciones acerca del compor-
tamiento de los posibles usuarios. Esto obliga, por otro lado, a que el sistema controle un
conjunto de colas de espera para los procesos que no puede (o no debe) atender en un cierto
momento. Estas colas son áreas del disco magnético donde se almacenan los programas
que desean ingresar al sistema de cómputo, y desde donde los tomará el scheduler.
Una de las importantes funciones de esta sección del sistema operativo es convertir
Convertir los programas de los usuarios en procesos. Es decir, tomar los programas originales y
un programa asignarles una representación interna que permita determinar los recursos que los procesos
en un proceso requerirán de la computadora. Por ejemplo, es necesario tratar de averiguar la cantidad de
memoria solicitada por un proceso cuando entre en ejecución, así como el área en disco y
el tiempo de procesador central que espera consumir, etc., con la finalidad de dar al sistema
operativo elementos para una planeación eficiente de la distribución de los recursos entre
los diversos (y aún no conocidos) usuarios o procesos.
Además, de entre la cola de procesos activos ya descrita y mantenida por el kernel, el
despachador de alto nivel selecciona los adecuados para su próxima atención por parte
del procesador.
Como similitud, supóngase que en un supermercado dos clientas se dirigen hacia la
caja de salida, una con muchas mercancías por pagar y otra con sólo un artículo, y que no
es posible determinar inicialmente a cuál se atenderá primero porque esto depende de su
orden —indeterminado— de llegada a la caja. Si ambas llegaran simultáneamente a la caja,
y para atender a una fueran necesarios diez minutos —porque lleva muchas mercancías— y
la otra fuera atendida en un minuto, claramente convendría atender primero a esta última,
porque no es grave extender a once minutos una espera prevista de diez; pero, por supuesto,
volver una espera de un minuto en una de once sí constituye una carga para ese usuario.
Naturalmente, el cliente menor quedará más satisfecho con el sistema si es atendido
con prontitud, lo cual depende de la programación de atención que se pueda hacer cuando
ambos estén listos para llegar a la caja de salida.
Siguiendo con la analogía, el núcleo del sistema operativo es el que —mediante las
interrupciones— mueve a los clientes dentro de la tienda (aunque no puede predecir cuán-
do van a salir), y al scheduler le corresponde definir el orden de atención por parte del
procesador.
Esto implica también la necesidad de averiguar cuáles procesos ya terminaron de eje-
cutarse, cuáles están listos para comenzar a hacerlo, cuáles van a imprimir sus resultados,
etc., para formar y mantener las colas de espera de procesos.
Como se vio antes, el manejador del procesador de bajo nivel (residente en el núcleo)
está guiado por diversos criterios para determinar la operación de la computadora
Sección 5.6 Sistemas operativos 229

multiusuario. Las interrupciones pueden deberse a que los procesos piden la ejecución de
operaciones de entrada/salida, pero también es posible atender a cada proceso durante un
tiempo fijo (una décima de segundo, por ejemplo). En el artículo [SCIA71] hay una descrip-
ción del origen de estas ideas, que siguen siendo la base funcional de los actuales sistemas
multiusuario y los servidores de redes.
La figura muestra las ligas entre los despachadores de bajo y de alto nivel y su rela-
ción con los procesos.

1 1%
Se terminó su
"fracción de tiempo"

Bloqueado
temporalmente Figura. 42 Estados de
(en espera de E/S) un proceso

En las computadoras personales actuales, fuertemente afectadas por las presentacio-


nes gráficas y el uso del inefable ratón, lo más común es que el usuario tenga abiertas Procesos light
varias "ventanas" a la vez en la pantalla, y para ello el sistema operativo tendría que
mantener un proceso por cada una. Esto resulta caro e innecesario por dos razones: 1) hay
un solo usuario, y 2) las ventanas emplean los mismos subsistemas gráficos y bien po-
drían entonces compartirlos, en lugar de que cada una tenga sus propias copias en la
memoria, como sucedería si fueran procesos independientes. Para ello, se inventó el con-
cepto de "proceso ligero" (en inglés llamado thread, o "hilo"): activaciones de un mismo
proceso básico que comparten el mismo código, pero con variaciones particulares que se
activan para cada ventana.
De esta forma, se mantienen varias ventanas simultáneamente mediante los hilos, y
por ello el sistema operativo es multitarea (multitasking), aunque en realidad siga sien-
do personal y ejecute un solo proceso de usuario a la vez. Por otra parte, como ya se dijo,
si además el sistema operativo debe atender a varios usuarios (y procesos) concurren-
temente, entonces se habla de multiprogramación, aunque este término ya casi no se
usa, y se prefiere decir sistema multiusuario. Por supuesto que si un sistema operativo
es multiusuario, puede con toda tranquilidad atender a una sola persona (si los demás
usuarios no están presentes), pero a una computadora con sistema monousuario no se le
pueden conectar otras terminales. Conclusión: el sistema operativo del servidor que atien-
de los pedidos de las varias computadoras conectadas a una red, es multiusuario (ade-
más de multitarea).
230 Capítulo 5 Software de base

Una vez estudiadas las funciones mínimas de atención a los procesos, queda por
resolver el problema de la comunicación entre los que están en ejecución y el mundo
exterior. Para un proceso, el mundo exterior se refiere a las unidades de entrada/salida de
la computadora y, de manera adicional, a las unidades de memoria secundaria. La siguiente
capa del sistema operativo maneja las funciones de entrada/salida; o sea, la comunicación
de los procesos con su entorno.

Manejo de entradas y salidas


El manejador de entrada/salida tiene como propósito principal atender los pedidos de
atención que los procesos en ejecución hacen sobre las unidades periféricas. La mayoría
de las veces esto requiere realizar traducciones lógicas y físicas entre las diversas unida-
des involucradas. La parte física permite comunicar aparatos diferentes entre sí aunque
manejen códigos internos distintos, y la traducción lógica —más interesante para noso-
tros en este momento— tiene como función virtualizar los pedidos de entrada/salida y
postergar su ejecución física lo más posible. En el capítulo 4 se describió el modelo gene-
ral ISO/OSI para la intercomunicación entre computadoras diversas, encargado entre otras
cosas de la resolución de diferencias de códigos entre equipos conectados en red.
El manejador de entrada/salida hace que esos pedidos se virtualicen y sean desviados
del/al disco magnético, para no depender de las limitaciones de los dispositivos físicos de
lectura o escritura. Cuando un proceso en ejecución manda un carácter a la impresora, el
sistema operativo lo envía al disco magnético, a un área especial destinada a ser la impre-
sora virtual para ese proceso. Y como en general se está hablando de multiprogramación,
entonces habrá tantas impresoras virtuales en disco magnético como procesos activos
haya en el sistema.
Lo mismo cabe señalar para el caso de las lecturas. En muchas grandes computadoras
para proceso de datos primero se capturan los datos y se guardan en una lectora virtual en
disco magnético para que, cuando el proceso pida un dato, éste le llegue del disco y no de
la unidad física de lectura.
Este concepto recibe el nombre de SPOOLing (Simultaneous Peripheral Operations
On Line, operación simultánea de periféricos en línea) o, más simplemente sistema
de colas (queues). Sus ventajas son claras; al lograr una virtualización de las unidades de
entrada/salida de la computadora, ésta se comporta como si tuviera varias de cada una y
los procesos no tienen que esperar a que la impresora esté libre para seguir ejecutando.
Además, permite redirigir los archivos de impresión hacia la primera impresora desocupada
si la computadora dispone de varias; asimismo, posibilita la reimpresión de múltiples copias
del mismo resultado de un programa, grabado previamente en el disco. En los enormes
centros de cómputo en donde se procesa, por ejemplo, la nómina de los empleados fede-
rales o la lista de abonados telefónicos, no deja de resultar impresionante ver cómo una
gran computadora imprime miles y miles de cheques u otros documentos en varias impre-
soras láser de gran velocidad en forma aparentemente simultánea.
Esta capa se encarga también de las transferencias físicas de información entre las
unidades de entrada/salida y los procesos en ejecución. Como ya se dijo, realiza en forma
automática las traducciones necesarias entre códigos diversos y velocidades de acceso
diferentes. Una sección muy importante es el subsistema de manejo lógico del disco mag-
nético, que virtualiza los pedidos de información para que los procesos no deban preocu-
parse de cuál pista o sector del disco contiene el dato pedido. El proceso en ejecución
simplemente solicita el valor de la variable ALFA residente en disco, en el área dedicada
de antemano por el sistema, y entonces este pedido se traduce al disco magnético como
"mover el brazo de lectura del disco al sector 27 de la pista 144, para leer de ahí la informa-
ción que contenga y colocarla en un área de almacenamiento temporal". Una vez hecho
esto, el sistema avisará al proceso (por medio de una interrupción) que ya puede recoger
de dicha área los datos solicitados.
Sección 5.6 Sistemas operativos 231

Manejo de información
Una vez resueltos los problemas del acceso físico a la información, aún queda el poder
hacer uso "humano" de ésta. De ello se encarga una nueva capa del sistema operativo,
conocida como sistema de archivos o manejador de información.
Sus funciones son permitir a los usuarios el manejo libre y simbólico de prácticamen-
te cualquier cantidad de información que deseen almacenar, leer, imprimir, alterar o dese-
char. Se trata de un manejo libre porque en lo posible tendrá el menor número de restricciones
físicas o lógicas, y simbólico porque el usuario no tendrá que preocuparse de los modos
de acceso al disco magnético ni de otros detalles, virtualizados ya por el manejador de
entradas/salidas: simplemente hará referencia a su información por el nombre simbólico
libremente asignado antes. Claro que para ello, el manejador hará múltiples llamadas para
solicitar servicios específicos del núcleo, mediante las ya mencionadas system calls.
No sólo eso, sino que el sistema también puede almacenar la información por plazos
indefinidos y recuperarla en cualquier momento, manejando criterios de seguridad de
acceso y de protección, aun en entornos de redes. Todo sistema operativo moderno garan-
tiza de alguna manera la seguridad e integridad de la información que le ha sido confiada
por los usuarios, y lo logra por diferentes medios, que van desde mantener copias ocultas
hasta realizar periódicamente su respaldo en forma automática en algún lugar seguro.
Muchos sistemas incluso reconstruyen archivos dañados por accidente o negligencia.
A estas alturas para nadie será una sorpresa enterarse que los sistemas operativos cons-
tituyen enormes esfuerzos de ingeniería humana y de programación, de una complejidad
tal que no existe quien por sí solo comprenda en detalle su operación completa, sino que
son producto de equipos de ingenieros, analistas y programadorest. Una versión reciente
del sistema operativo Windows® consta de un total de 18,000,000 de líneas de código
fuente.
Aunque nos hemos extendido en la descripción de un sistema operativo, quedan aún
por explorar muchos conceptos en la siguiente sección, que sirven como apoyo a la pro-
gramación; se trata de utilerías controladas por el sistema (editores, paquetes diversos,
hojas de cálculo, bases de datos, etc.). En otro apartado también se trata la inteligencia
artificial, área que deberá adquirir tal relevancia que las computadoras de la siguiente
generación estén basadas en ellatt.
Por último, es necesario que todo sistema operativo se comunique con los usuarios
de alguna manera. Existen básicamente dos formas de lograrlo: mediante un lenguaje de La comunicación final
control que el interesado aprende, o por medio de menús o gráficas desplegadas en la con el usuario
pantalla para que el usuario escoja la operación deseada. Ambos sistemas tienen ventajas
y desventajas, y para cada uno existen fieles defensores y mordaces críticos, aunque des-
de hace tiempo la batalla fue ganada por el equipo formado por la GUI (Graphical User
Interface) y el ratón.
Por todo lo visto, una computadora (en el sentido amplio) no es tal sin un sistema
operativo que le dé soporte y la haga aparecer como mucho más de lo que realmente es:
un complejo aparato electrónico. De aquí en adelante cuando se hable de un equipo de
cómputo se considerará, sin falta, al sistema operativo como integrante indispensable, no
obstante que ya sabemos que se trata de un conjunto de procesos de la programación de

(t) Esto implica, claro, la inevitable existencia de errores —a veces graves— en los sistemas opera-
tivos, y de las correspondientes versiones "nuevas y mejoradas" que aparecen con regularidad. En
el número correspondiente a enero de 1998 de la prestigiada revista IEEE Spectrum se lee la siguiente
demoledora frase, referida a los programas comerciales: "El software puede ser la única clase de
producto de las sociedades industriales que es distribuido rutinariamente por los fabricantes sa-
biendo que está defectuoso, para luego remediar los problemas después de la venta" (pág. 55).
(t t) Aunque llevamos ya muchos años esperando sus promesas.
232 Capítulo 5 Software de base

sistemas para dar "vida" y potencia a la máquina. Para mayor información de carácter
general se recomiendan los libros [DEIH93] y [LISE93], además de los ya mencionados
[SILG99], [STAW97], [TANW98] y el artículo [DENP71].

5.7 UTILERÍAS: EDITORES, HOJAS DE CÁLCULO,


BASES DE DATOS

Como se mencionó en el capítulo 1, las computadoras personales volvieron muy notoria


la existencia de un conjunto de programas y sistemas utilitarios que, si bien ya existían
en la tercera generación, mejoraron y se han vuelto virtualmente indispensables en la ope-
ración usual de las computadoras debido a sus ventajas de uso, y a que permiten a los no
especialistas emplear eficazmente la máquina, cumpliendo la función de puente de comu-
nicación entre los usuarios y la computadora.
En esta sección se describirá conceptualmente qué es y cómo funcionan estos siste-
mas, pero no se tratarán cuestiones operativas ni de detalle. Existe una gran cantidad de
libros (orientados al mercado llamado "profesional") dedicados a mostrar las particulari-
dades de prácticamente todos los paquetes a continuación mencionados.

Editores
En el ejemplo dado en el capítulo 3, donde se describía una sesión típica en una compu-
tadora multiusuario, se mencionó que un editor es un programa del sistema empleado para
introducir textos mediante la terminal de video, y su función primordial es servir de canal
de entrada de textos, datos y programas fuente, que normalmente residirán en el disco
magnético de la computadora hasta que se decida borrarlos o transferirlos a una cinta o
diskette de respaldo.
En el diseño de un editor existen dos frentes a considerar: la comunicación con el
usuario y la liga con el sistema de archivos de la máquina. Los objetivos de la primera
interfaz son permitir al usuario crear, manipular y borrar información en forma fácil y sin
tener que preocuparse de los detalles físicos de operación de la memoria o los discos; los
de la segunda consisten en lograr que la gran cantidad de operaciones lógicas y físicas
requeridas para interactuar con los sistemas de archivos y de entrada/salida de la compu-
tadora se lleven a cabo rápida y eficazmente, consumiendo la menor cantidad de recursos
posible para evitar que el tiempo de respuesta se degradet.
Es decir, un editor debe ser capaz de traducir a la computadora las órdenes dadas por
Funciones el usuario, normalmente clasificadas en alguna de las categorías mencionadas a continua-
de un editor ción. Cada vez es menos clara la distinción entre un editor y un procesador de palabras, y
posiblemente algunas de las funciones aquí mencionadas estén incluidas en varios de los
más poderosos procesadores de palabras comerciales, aunque no en algunos editores.
Lo más conveniente es considerar a los editores como caso particular de los procesadores
de palabras; es decir, casi todos los procesadores de palabras tienen las capacidades de un
editor además de las propias.
Estas son algunas de las operaciones posibles con los editores actuales:
— Creación de un archivo que contendrá texto y gráficos.
— Facilidades para manipular el archivo, entre otras:
— Agregar texto a un archivo mediante el teclado de la terminal.
— Borrar e insertar caracteres, palabras, renglones completos y grupos de ellos.

(t) Pero se podría pensar exactamente lo contrario al ver la forma de operación de algunos "popu-
lares" editores, que más bien se comportan como verdaderos dinosaurios. No todo es progreso.
Sección 5.7 Utilerías: editores, hojas de cálculo, bases de datos 233

- Buscar algún subtexto en particular y, posiblemente, cambiarlo por algún otro.


– Mostrar en pantalla cualquier porción del texto, determinada por número de
renglón, número de página, contenido de un renglón, etcétera.
- Copiar porciones de texto o gráficos a otros lugares dentro de ese archivo o de
algún otro, así como la acción inversa: traer ("importar") texto o gráficos
de otros archivos para integrarlo al actual.
- Insertar hiperligas de Internet.
- Combinar varios archivos en uno solo y su inversa.
- Crear "ventanas" para tener acceso a contenidos de diferentes archivos en forma
simultánea.
- Posibilidades de edición, entre las que se incluyen paginación, notas al pie, índi-
ces, subtítulos y portadas, alineación del margen derecho y, en algunos casos,
búsqueda (y corrección) de errores tipográficos y ortográficos.
- Configuración de diferentes tipos de letra (conocidos en tipografía como "fuen-
tes" —fonts, en inglés—), en diversos tamaños y presentaciones (negritas, cursi-
vas, subrayados, etcétera.)
- Imprimir el archivo o partes de él.
– Eliminar un archivo.

Existe una gran diversidad de editores, desde los más elementales (que no cubren
todas las funciones recién expuestas) hasta verdaderos sistemas completos de edición
(conocidos en inglés como Desktop Publishing), o bien de ayuda para creación de progra-
mas en algún lenguaje específico de programación, que detectan cierto tipo de errores
léxicos y sintácticos al momento mismo de la creación del programa. Igualmente hay edi-
tores programables en los que el usuario especifica una tarea por realizar (cambiar todas
las apariciones de una palabra en cierto contexto por alguna otra, por ejemplo) y éste se
encargará de aplicarla a todo un conjunto de archivos. La mayoría de los editores actuales
están guiados por menús e interfaces gráficas, de donde se escogen las opciones sin tener
que manejar un lenguaje especial.
En vista de que un editor es un programa que interactúa directamente con el sistema
operativo (porque se encarga de pasar al sistema de archivos de la computadora en la
forma más eficiente posible las órdenes dadas por el usuario), varios de ellos ofrecen
acceso directo a las funciones del sistema sin abandonar el editor, lo cual es muy conveniente
en muchos tipos de aplicaciones complejas en donde se considera una extensión del siste-
ma operativo. Así, por ejemplo, se puede llamar al compilador de C y desde allí escribir un
programa; si hay errores de sintaxis, el editor toma el control y muestra al usuario la línea
del programa que contiene el error, junto con el diagnóstico apropiado, con lo que efecti-
vamente se ligan las funciones de edición y compilación en un todo que parece más natu-
ral. Lo mismo sucede en algunos procesadores de palabras, que detectan errores tipográficos
(o de construcción, como uso excesivo de una misma palabra o sílaba, etc.) e incluso
sugieren la corrección apropiada, para entonces integrarla al texto en proceso.
Todas estas capacidades del editor deben traducirse a operaciones internas de modo
que la computadora pueda efectuarlas a la mayor velocidad posible. Claramente, esto
implica que el texto en proceso debe residir en la memoria central de la computadora:
único lugar donde se garantiza la velocidad de proceso. Aquí, sin embargo, vuelven a surgir
problemas como los que nos ocuparon anteriormente. La capacidad de la memoria central
es relativamente pequeña y, por tanto, no siempre se puede esperar que todo el texto esté en
la memoria. Esto obliga a que en el diseño del programa editor se considere el texto como
un conjunto de cadenas de caracteres y símbolos ligadas mediante estructuras especiales
de datos, para que el programa pueda trabajar sobre grupos de ellas y sea capaz de traerlas
y llevarlas en forma dinámica desde y hacia el disco magnético sin detener la operación ni
degradar demasiado el tiempo de respuesta.
234 Capítulo 5 Software de base

Casi todos los editores, entonces, primero crean en el disco magnético una copia del
archivo —cuando se trata de un archivo no de nueva creación— y la colocan (o al menos
parte de ella) en la memoria central de la computadora para trabajar allí. Al final de la
sesión de edición el archivo se lleva de regreso al disco, en donde reemplaza a la versión
original. Algunos ofrecen la capacidad de "recrear" la sesión de edición completa si hubo
alguna falla en el equipo o un corte en el suministro de corriente eléctrica, recuperando así
el trabajo hecho por el usuario.
Con algunos procesadores de textos se puede incluir gráficas e imágenes fotográficas
(previamente digitalizadas por un scanner o lector óptico como los descritos antes), así
como "importar" datos desde otros programas e integrarlos en el texto. También es posi-
ble añadir porciones con sonido y animación, aunque esto último más bien pertenece a los
sistemas conocidos en inglés como multimedia.
En términos generales, el diseño de un editor representa un esfuerzo muy considera-
ble de ingeniería de software, y son muchos los recursos técnicos y teóricos que intervie-
nen en su elaboración. La creación de un editor es un área especializada, bastante lejana
de las labores usuales de un centro de procesamiento de datos o de la actividad normal de
los programadores de aplicaciones.

Hojas de cálculo
Se conoce como hoja electrónica de cálculo (spreadsheet) a sistemas de manejo virtualizado
de columnas de números, que vuelven fácil la tarea de hacerles modificaciones y operacio-
nes diversas, desde alteraciones sencillas en sus valores hasta el cálculo de cifras adiciona-
les dependientes de relaciones matemáticas entre otras columnas y renglones especificados
por el usuario. La primera hoja de cálculo (VisiCalc, diseñada por Dan Bricklin y Bob
Frankston) fue comercializada en 1979 e inmediatamente constituyó un enorme éxito de
ventas, y fue la razón que impulsó a miles de personas a comprar microcomputadoras. En
general, las hojas de cálculo no existían antes de las microcomputadoras, por lo que sí se
trata de software "nuevo".
La utilidad de mantener columnas de números (sin considerarlas como pertenecientes
a una matriz, sino desde otra perspectiva menos formal) es muy amplia, sobre todo en
aplicaciones de contabilidad, finanzas y presupuestos. Las primeras hojas de cálculo tuvie-
ron tanto éxito que muy pronto se crearon compañías especializadas en ese campo. Actual-
mente existen hojas de cálculo muy complejas y vistosas, pues manejan interfaces con
procesadores de palabras, Internet, bases de datos y sistemas de graficación a color.
La hoja de cálculo es una forma bidimensional consistente en "celdas" almacenadas
en la intersección de renglones y columnas, en donde se puede guardar, borrar o reacomodar
datos para luego manipularlos en diversas formas. El método usual empleado es mediante
la definición de fórmulas aritméticas que relacionan columnas o renglones. Entre muchas
otras cosas, así es factible usar esas celdas como variables de una ecuación para calcular
el retorno esperado de una inversión dentro de un plan financiero, o para llevar el control
de las existencias dentro de un almacén. También es sencillo formular informes a partir de
esas cifras, porque las hojas de cálculo tienen facilidades para poner títulos a las columnas
e imprimirlas en formatos especificados por quien maneja la información.
Por ejemplo, si se tiene una columna de números que representa los sueldos de los
empleados de un departamento, una sencilla fórmula para multiplicar cada elemento por
1.30, y otra para sumar toda la columna proporcionarán un elemental estudio de un
incremento salarial del 30%, tanto en su impacto individual como global.
Funciones de una hoja Entre las funciones más usuales de las hojas de cálculo se encuentran las siguientes:
de cálculo
– Ingreso de números o textos en celdas
– Modificación de valores de celdas
– Copia de celdas de un lugar a otro dentro de la hoja
Sección 5.7 Utilerías: editores, hojas de cálculo, bases de datos 235

- Cálculo de valores de acuerdo con fórmulas especificadas, que pueden llegar a


ser muy complejas y hacer referencias a otras celdas, columnas o renglones
- Modificación de valores en secciones completas de la hoja
- Especificación de funciones diversas que afectan a secciones de la hoja, como
determinación de promedios, cálculo de valores máximos o mínimos, detección
de celdas con valores preespecificados, y otros
- Copia e integración de varias hojas en una sola
- División de una hoja
— "Vistas" parciales de una hoja
- Impresión de la hoja con un formato determinado

Desde un punto de vista computacional, una hoja electrónica de cálculo no es más


que un gran arreglo de celdas (donde cada una contiene una cifra) ligadas entre sí median-
te estructuras de datos internas que permiten su manipulación interactiva desde la panta-
lla, desde donde el usuario puede relacionarlas de múltiples maneras.
Las hojas de cálculo normalmente son definidas y operadas por los directamente
interesados en la información que contienen, y esto es posible gracias a su diseño y pre-
sentación, pensadas para su uso por los propietarios de las computadoras personales. El
lector no debe dejar que esta aparente facilidad le impida enterarse que para diseñar una
hoja de cálculo se requiere una gran cantidad de conocimientos de programación, estruc-
turas de datos e ingeniería de software. Esto ya no debe parecernos raro; el software de
base es una rama muy sólida de las ciencias de la computación cuya finalidad es la crea-
ción de sistemas que den al usuario una imagen de transparencia y sencillez capaz de
esconder la complejidad de su diseño.

Bases de datos
Un sistema administrador de bases de datos (DBMS: Data Base Management System, y
que aquí abreviaremos como SABD) es un complejo programa que virtualiza el manejo
de archivos de datos, para proporcionar acceso a la información en forma lógica y no
física. Es decir, mediante una base de datos (que es también el nombre abreviado de estos
sistemas) es posible procesar archivos guiándose por el significado de sus contenidos, y
no tanto por sus características de organización (registros, campos, tamaños, etcétera.).
Para comprender la razón de ser de las bases de datos es necesario antes hacer una
distinción básica entre datos e información. Por lo primero se entiende tan sólo la repre- Diferencia entre datos
sentación (normalmente en forma magnética u óptica) de números y letras, sin que su e información
significado sea la característica más importante, mientras que la información comprende
esos mismos datos más las relaciones estructurales entre ellos. O sea, puede haber datos
sin información (simples números), pero no puede haber información sin datos, pues ésta
se extrae de los datos mediante relaciones explícitas propuestas. Un conjunto de números,
por ejemplo, en tanto simples cifras, no significa mayor cosa, pero si se decide averiguar
algunas relaciones estadísticas entre sus componentes (distribución, coeficientes de creci-
miento, etc.), entonces la información aparece justamente allí donde antes había tan sólo
datos, y puede incluso servir para determinar acciones a realizar, suponiendo que repre-
senten mediciones de algún parámetro de interés. El conjunto de números (banco de da-
tos) no ha cambiado, sino que se hicieron explícitas algunas relaciones entre sus elementos.
En principio, el conjunto y diversidad de relaciones que se puede establecer en un banco
de datos es prácticamente ilimitado y depende del observador que las propone, y no tanto de
los datos mismos.
Con el uso de una base de datos se obtiene una elevación del nivel de abstracción de
los datos manejados, es decir, de cómo se observa la información. Los niveles más bajos Niveles de abstracción
de abstracción están muy ligados con la arquitectura física de la computadora y de los
dispositivos de almacenamiento secundario; esto es bastante difícil de manejar, y requiere
236 Capítulo 5 Software de base

un buen conocimiento de la computadora y el sistema operativo. Conforme se alcanza una


mayor abstracción en el manejo, los usuarios se apegan a una realidad en particular de su
interés, como pólizas, empleados, departamentos, compras, etc., y al interactuar con el
sistema plantearán sus necesidades en términos de esa realidad superior, sin ocuparse de
los detalles operativos.
El origen de los manejadores de bases de datos se sitúa entonces en la gran cantidad
de información que se puede extraer de un conjunto de datos dado, lo cual vuelve muy
deseable disponer de una especie de "extractor general de información". Para lograr este
objetivo deberá contarse con programas o sistemas encargados de lo siguiente:
— Creación del banco de datos, organizado en alguna forma (registros, campos,
Funciones de una etc.) y residente en algún medio accesible a la computadora. Normalmente esto se
base de datos logra mediante pantallas de captura de datos, en las que inicialmente se describen
sus formatos, longitudes y tipos de agrupación.
— Definición del esquema de organización, para describir la estructura de la
información y sus relaciones.
— Manipulación del esquema, en términos cercanos al problema representado, y
no tanto en función de las características de organización de los datos.
- Obtención de informes y resultados a partir de los datos y las relaciones defini-
das entre ellos.
El mecanismo general con el que se obtiene información mediante un sistema admi-
Cómo se interactúa nistrador de una base de datos profesional es como sigue (no necesariamente se refiere a
con una base de datos los sistemas administradores de archivos e información que funcionan en las computadoras
personales, por razones de mercadotecnia llamados "bases de datos"):
1. El usuario plantea al analista sus requerimientos de información y las fuentes
para obtenerla, y propone la forma que deben tener los informes deseados.
2. El analista determina lo que se conoce como estructura lógica de la base de
datos: un mapa que vuelve explícitas las relaciones entre los componentes de la
información y a partir del cual se podrán extraer resultados útiles para el usuario.
Otro nombre que suele darse a esta representación es esquema de la base de
datos. Esta es la labor más importante, y requiere de un conocimiento formal que
en ciencias de la computación recibe el nombre de diseño de bases de datos
(subárea 7.1.1 de los Modelos Curriculares)
3. Como los datos existen aun cuando no se estén ejecutando los programas, su
forma y estructura también deben estar presentes para el sistema, lo cual se logra
especificando su organización física mediante un lenguaje especial (lenguaje de
definición de datos), aceptado por el SABD. El resultado es una representación
interna de la estructura de la base de datos que contiene, entre otras cosas, las rela-
ciones explícitas entre los datos expresadas mediante algún esquema o lenguaje
(relaciones de dependencia o jerarquía, de inclusión, de ordenamiento, etc.) y los
resultados que se desea obtener al manipular los datos.
4. Se hacen pedidos específicos de información al SABD, que pueden involucrar
una gran cantidad de operaciones internas, llamadas al sistema operativo, y opera-
ciones de E/S y acceso a los sistemas de archivos. Para que los resultados de la
operación sean accesibles, deben ser traducidos de nuevo al nivel de abstracción
correspondiente al usuario. La tendencia actual en los sistemas es que esos pedi-
dos sean expresados en un lenguaje relativamente sencillo (conocido como len-
guaje de consultas, query language), que permite al usuario interactuar con el
sistema. Cuando la aplicación es compleja a veces es necesario escribir, en algún
Sección 5.7 Utilerías: editores, hojas de cálculo, bases de datos 237

lenguaje de programación compatible con el SABD, programas para explotar ca-


racterísticas específicas que no son fácilmente accesibles desde el lenguaje de
consultas. Esta es una tarea especializada, fuera del alcance del usuario final.

En el entorno de las computadoras personales, este tipo de lenguajes compatibles con


el manejador de la base de datos ha adquirido casi la categoría de lenguajes autónomos y
disponen incluso de un compilador, aunque su gama de aplicaciones está limitada a
mantenerse dentro del sistema mismo.

Diseño de bases de datos


Una base de datost debe reflejar la información de una realidad relevante para una orga-
nización. El diseño de la forma y estructura de la base de datos es un proceso muy importan-
te, pues de ello dependerá la simplicidad o complejidad de los programas de aplicación, y
lo compacta o redundante que sea la información dentro del sistema.
Para hacer un diseño adecuado es necesario simular en la computadora de alguna
forma una situación del mundo real. La simulación se realiza mediante un modelo de datos:
una herramienta conceptual que ofrece formas de especificar las entidades de informa-
ción y las relaciones existentes entre ellas. Quienes diseñan la base de datos son los analistas
de sistemas a cargo de un proyecto en particular.
Los sistemas administradores de bases de datos manejan la información y los opera-
dores lógicos para manipularla, y están íntimamente relacionados con algún modelo de El modelo de datos
datos en particular. Existen varios modelos de datos, con mayor o menor capacidad y determina
facilidad para el diseño de alguna base de datos específica. Entre los modelos de datos las características
usuales destacan el modelo de entidades y asociaciones (es el más general, aunque hay
generales del SABD.
pocos SABD comerciales que lo emplean), el modelo de redes, el modelo jerárquico
y el modelo relacional.
Este último es el más usado en la actualidad y con mayor futuro en términos prácti-
cos, porque está formulado con bases teóricas y formales más sólidas'y mejor especificadas,
lo cual permite la existencia de SABD relacionales poderosos y concisos. Las bases de
datos relacionales emplean un conjunto de tablas para describir las conexiones lógicas
entre los diversos elementos de datos, y su conformación está sujeta a una serie de reglas
de normalización[, para optimizarlas y evitar redundancias.
Como se mencionó en el capítulo anterior, mediante una intranet es posible crear
sistemas de información distribuidos, accesibles desde todas las divisiones (locales y remo-
tas) de una gran empresa o conglomerado, y utilizarlos para consultar o explotar los datos

(t) Puede existir confusión en el uso de este término: a veces se refiere a un SABD, y a veces
nombra tan sólo el diseño de los archivos de información sobre los que éste trabaja. Normalmente
el contexto permite determinar el uso correcto. En este caso, por ejemplo, se trata del diseño de los
archivos y su interrelación lógica.
(tt) Existe toda un "álgebra relacional", empleada para construir las tablas con las cuales se
describe el esquema de la base de datos. Algunas de sus reglas (conocidas como formas normales)
especifican cómo definir las tablas: la primera forma normal, por ejemplo, pide que los valores de
las entradas o celdas de las tablas sean atómicos o indivisibles; la segunda forma normal requiere
que todas las columnas de todas las tablas se refieran a un solo campo, con lo cual alguna columna
puede servir como "llave primaria", además de que las tablas deberán estar en primera forma nor-
mal; y la tercera forma normal solicita que todos los campos de un renglón que no sean llaves sean
independientes entre sí, además de que las tablas deberán estar en segunda forma normal. Las
formas normales siguientes determinan relaciones entre tablas completas, y existe también un con-
junto de operadores relacionales. La teoría de bases de datos relacionales se remonta a un artículo
de Edgar E Codd publicado en 1970, por cuyas investigaciones recibió el Premio Alan Turing de la
ACM en 1981.
238 Capítulo 5 Software de base

e información corporativos a través de interfaces gráficas sencillas. El "motor" de un gran


Uso de bases de datos sistema de este tipo es un SABD, que acopla e interconecta diversos bancos y bases de
en sistemas de datos para configurar una especie de almacén general de conocimientos de la empresa,
información conocido en inglés como Daga Wairehomse (bodega de datos). En general, el proceso de
extracción y análisis de los datos allí contenidos recibe el nombre de "data mining" (mi-
nería de datos).
En los últimos años se habla de bases de datos orientadas a objetos (y sus correspon-
dientes manejadores), para manipular información en términos de los elementos (u objetos)
de la realidad que representan. Para ello también hay estándares, como CIMA (Common
Object Request Broker Architecture), descrito en el glosario al final de este libro.
Las bases de datos constituyen, como se ha dicho, todo un campo de estudios dentro de
las ciencias de la computación, y existen cursos dedicados por completo a este tema, así
como estudios de posgrado y especialización (constituyen una sub 'área completa —la 7.1—
dentro de los Modelos Curriculares). Flay también un gran número de textos sobre los
aspectos formales y operativos de las bases de datos, entre los que podemos citar [S1LK98],
[U]LLLJ82] y [ULLW99], todos ellos de gran amplitud y grado de especialización.

Lo que no es la inteligencia
1:1114HLICIMM L\RVMML
artificial
En este largo camino de esfuerzos de comunicación entre la
máquina y el ser humano se puede vislumbrar una última
etapa, que debería ser considerada como la finalidad del soft-
ware de base: la comunicación directa con la máquina, en
lenguaje natural y sin el complicado conjunto de lenguajes
intermedios descritos a lo largo de este capítulo. La idea es,
en principio, inalcanzable en toda su extensión, pues impli-
caría la capacidad de reproducir a la perfección los extraor-
dinariamente complejos mecanismos lingüísticos (entre otros)
que nos caracterizan, lo cual requeriría una virtual reinvención
del ser humano, tarea que evidentemente no nos es accesi-
ble. Sí es posible, sin embargo, acercar aún más la computado-
ra, simulando —aunque sea en grado imperfecto y limitado—
algunas de nuestras aptitudes verbales, de razonamiento y de
entendimiento; y ésta es precisamente la tarea del campo
de técnicas y conocimientos conocida con el equívoco nom-
bre de inteligencia artificial.
Debe quedar claro que inteligencia artificial (término
inventado en 1955 por John McCarthy) no implica compu-
tadoras inteligentes; implica más bien computadoras que eje-
cutan programas diseñados para simular algunas de las reglas
mentales mediante las cuales se puede obtener conocimiento
a partir de hechos específicos, o de entender frases del len-
guaje hablado, o de aplicar estrategias para ganar juegos de
mesa. En ningún caso se habla (o siquiera se vislumbra) de la
capacidad de entender realmente situaciones elaboradas o
complejas, o mucho menos aun, de acercarse a tener indepen-
dencia o remotamente a tener sentimientos. La complejidad
inherente a estas últimas funciones es tan enorme que no las
entendemos siquiera desde un enfoque psicológico, y menos
desde un punto de vista fisiológico.
Sección 5.8 Inteligencia artificial 239

Esto no impide a la inteligencia artificial avanzar con gran rapidez, y ya se tienen


básicamente resueltos los problemas de entendimiento de frases cortas habladas (más no
de historias o razonamientos complejos), del camino a seguir en juegos de mesa que re-
quieren análisis de estrategias, y de la capacidad de decir algo coherente (o incluso hacer
sugerencias) acerca de situaciones específicas previamente descritas a la computadora.
Ha resultado mucho más difícil —y de hecho allí los avances han sido mucho menores—
el campo de la traducción automática de idiomas, el reconocimiento de formas complejas
tridimensionales, la creación de robots con capacidades generales de movimiento, y el
entendimiento de relatos con argumentos no triviales, como se describe en los artículos
[WINT84] y [WALP911.

Sistemas expertos
Tal vez el progreso más visible se ha dado en to conocido como sistemas expertos, que es
el nombre genérico para los programas especializados en algún campo específico del
conocimiento, con capacidad de simular razonamientos parecidos a los que haría una
persona versada sobre el tema en cuestión. En actividades como la explotación de pozos
petroleros o la autorización de cuentas en tarjetas de crédito, entre otras, se pide a estos
sistemas (mediante lenguajes especiales de comunicación a través de la terminal de vi-
deo) que propongan caminos a seguir para la toma de decisiones. Un sistema experto
puede dictaminar la conveniencia de iniciar una exploración preliminar en busca de pe-
tróleo a cierta profundidad luego de analizar los datos geológicos adecuados, por ejemplo,
o bien puede rechazar la autorización de una transacción de una tarjeta de crédito debido a
que la historia reciente de ese cliente es errática y no muestra un patrón de compras previ-
sible. Es importante aclarar que un sistema experto opera sobre un solo campo de conoci-
mientos y, por tanto, está dedicado exclusivamente a eso.
La estructura interna de un sistema experto está basada en lo que se conoce como
"máquina de inferencias": un programa capaz de manejar el conjunto de reglas de razo- Estructura de
namiento necesarias para llegar a una conclusión no existente en el momento sino genera- un sistema experto
da como resultado del análisis. El mecanismo de inferencias puede reconstruir hasta cierto
grado los pasos lógicos necesarios para extraer conocimiento de un conjunto de datos y de
las reglas predefinidas para su manipulación. Para lograr lo anterior el sistema contiene lo
que se conoce como una base de conocimientos (término derivado de base de datos),
formada por reglas y atributos, y un procedimiento para su evaluación y aplicación, liga-
do con un subsistema gramatical para la presentación de preguntas y resultados por la
pantalla. Un ejemplo muy impresionante de un sistema experto geográfico, además de útil y
sin costo, se puede encontrar en la dirección de Internet http : www mapquest com; se
trata de un auxiliar gráfico para llegar desde un punto a otro dentro de Estados Unidos.
Se proporcionan dos direcciones (calle y número) dentro de una ciudad especificada
—o en ciudades diferentes—, y el servidor envía a la computadora del usuario un mapa
con las instrucciones detalladas para trasladarse de un punto a otro, incluyendo las dis-
tancias, tiempos y nombres de las calles donde debe darse vuelta (y, si es necesario, se
dan las indicaciones sobre cuáles carreteras tomar para cruzar todo el país). (Para una
concisa descripción técnica de lo que constituye un sistema experto, véase el artículo
[THBW85].)
Existen sistemas que pueden aprender con base en experiencias recién adquiridas, por
ejemplo, en juegos de estrategia; y en inteligencia artificial se habla también de "programa-
ción heurística", referida a técnicas de búsqueda y análisis para producir comportamientos
menos rígidos que los ordinariamente esperados de un programa de computadora, porque
internamente determinan el camino óptimo a seguir para llegar a un fin propuesto. Igual-
mente se habla recientemente de "algoritmos genéticos", que desarrollan los resultados a
medida que se ejecutan. (Consúltese, por ejemplo, el artículo [HOLJ92] o el número de
enero de 1995 de la revista Soluciones Avanzadas.)
240 Capítulo 5 Software de base

En el caso de sistemas matemáticos sobresalen los que pueden manipular y demos-


trar algunos teoremas, y otros con capacidades algebraicas casi ilimitadas, para hacer
derivación e integración simbólicas, resolver series y simplificar y transformar ecuaciones
extraordinariamente complejas. En el artículo [PAVR81] se muestra cómo uno de estos
sistemas encontró tres errores en un gigantesco conjunto de ecuaciones empleado el siglo
pasado por el astrónomo francés Charles Delaunay para determinar la órbita lunar. La
cantidad y complejidad de ese sistema de ecuaciones de mecánica newtoniana fue tal que
el autor dedicó diez años a definirlas y otro tanto a verificar sus resultados. Un sistema
computacional algebraico analizó ese trabajo de 20 años (contenido en 2 volúmenes) y
luego de 20 horas encontró dos errores menores y uno sustancial, ninguno de ellos numé-
rico, todos simbólicos.
Este tipo de sistemas, y en general casi todos los relacionados con inteligencia artificial,
suelen estar escritos en lenguajes de programación especializados, entre los que destacan
LISP y Prolog, aunque en principio cualquier lenguaje de programación podría servir
para ese propósito.
Como ejemplo de lo logrado hasta ahora en el área de simulación de algunos proce-
sos mentales, considérese que una computadora ganó el campeonato mundial del juego de
salón de origen árabe conocido como backgammon, celebrado en Montecarlo en 1979
[BERH80]. De la misma forma, los programas para jugar damas lo hacen en el nivel de
campeonato mundial, aunque no sucede lo mismo con juegos más complejos, como brid-
ge (de baraja) o go (juego de mesa de origen japonés).

Ajedrez
Los programas para jugar ajedrez lo hacen en un nivel de aproximadamente 1900 puntos,
y existen además máquinas especializadas para ello, como la descrita en el artículo
[HSUF90]. Todo esto coloca a la computadora en la categoría de experto (aunque superior
a la media nacional de países como Estados Unidos, aún muy por debajo de los niveles de
competencia humana internacional; cuando fue campeón mundial en los años 70, "Bobby"
Fischer tenía una clasificación superior a 2,700, y el actual campeón mundial, Gary
Kasparov, está en los 2,785 puntos).
El nivel de juego de ajedrez de la computación sigue avanzando, y el 11 de mayo de
El campeón perdió. 1997 sucedió lo impensable: la computadora de ajedrez Deep Blue de IBM derrotó al
campeón mundial Gary Kasparov, bajo las condiciones estrictas de un torneo completo (a
las 19 movidas del sexto juego). El resultado final fue 3.5 contra 2.5 puntos; Kasparov se
llevó un premio de 400,000 dólares, y Deep Blue obtuvo 700,000. Esta computadora de
propósito exclusivo consta de 32 microprocesadores operando en paralelo, y el programa
es capaz de analizar 200 millones de posiciones del tablero cada segundo, empleando bási-
camente un enfoque de "fuerza bruta" (es decir, revisando todas las posibilidades existentes
hasta cierto nivel de profundidad), enriquecido con un gran conjunto de reglas ajedrecísticas
(una base de datos de 700,000 juegos de nivel de gran maestro, y otra con varios millones
de posiciones de apertura), codificadas bajo la supervisión de un gran maestro.
Meses antes, quien es considerado como el mejor jugador de toda la historia había
perdido un juego inicial contra la computadora, pero logró recuperarse y ganar los demás.
En aquel torneo, Kasparov explicó que le tomó un tiempo comprender que su oponente en
realidad no entendía el ajedrez (aunque durante la primera partida llegó a creer que sí), y
entonces ya no le fue tan difícil plantear pequeñas variaciones a los esquemas usuales de
juego, ante los cuales la computadora no pudo reaccionar adecuadamente. En una entre-
vista de diciembre de 1996, dijo: "Sigo con la bandera del género humano en lucha contra
el chip". Y sin embargo pocos meses después perdió el nuevo torneo, aunque no el título
de campeón mundial, porque las computadoras aún no son aceptadas oficialmente. Caben
dos comentarios: 1) el juego de ajedrez es demasiado complejo como para que se conside-
re "resuelto" desde un punto de vista matemático o algorítmico, pues para ello la máquina
Sección 5.9 Resumen del capítulo 241

tendría que calcular 10 120 posiciones, y media eternidad no le alcanzaría y, 2) para fortuna
de todos nosotros, Kasparov se prepara para la revancha.
Por otra parte, los grandes bancos emplean ya como rutina sistemas expertos para el
análisis de cuentas de inversión, y lo mismo sucede con empresas petroleras, con los diag-
nósticos en algunos hospitales especializados y con la determinación de riesgos y pérdi-
das en líneas de producción automatizada de ensambles para la industria aeronáutica. En
el artículo [DEWA84] se describen algunas de las estructuras de funcionamiento de pro-
gramas para jugar damas que están en un nivel de competencia de campeonato mundial.
En el enfoque aquí propuesto, la inteligencia artificial ocupa el sitio más alto dentro
de la programación de sistemas y, por ello, no debe resultar raro que sea un campo avan-
zado de conocimientos dentro de las ciencias de la computación (es la subárea 8.2 de los
Modelos Curriculares). En general, se estudia en cursos de posgrado o en los últimos
niveles de una carrera universitaria en computación, y se puede considerar como un cam-
po abierto a las labores de investigación. La referencia [WINP94] constituye un ejemplo
del la especialización de este tipo de estudios; la revista Byte dedicó el número de abril de
1985 al tema de la inteligencia artificial y constituye una referencia mucho más accesible,
donde se trataron temas sobre lenguajes de programación dedicados, máquinas especia-
les, visión, aprendizaje y sistemas expertos. Allí se incluye además un artículo de Marvin
Minsky, autor pionero de este campo, quien también será mencionado en el siguiente
capítulo por sus trabajos sobre computabilidad.
El artículo [ARTI90] contiene un interesante debate sobre los alcances de la inteligencia
artificial y se pregunta si las computadoras podrán algún día llegar a pensar. Por último, el
número de septiembre de 1995 de la revista Scientific American tiene un corto artículo
("Artificial Intelligence", de Douglas B. Lenat) sobre el estado actual de la disciplina,
escrito por un investigador que ha dedicado ya más de diez años a "enseñar" conceptos
elementales de sentido común a una computadora, con la idea de que sólo así se podrán
obtener sistemas semi-inteligentes, y no sólo super-expertos en un tema específico pero
por completo ignorantes sobre todo lo demás. El sistema se llamaba CYC (de la palabra
enCYClopedia) y, en palabras de su autor, puede entender metáforas y emplear analogías
para resolver problemas: "CYC sabe que Drácula es un vampiro, pero a la vez sabe que los
vampiros no existen". Lenat augura que la meta de la inteligencia artificial está a la vista
y que el mundo del siglo xxi cambiará radicalmente como resultadot.

5.9 RESUMEN DEL CAPÍTULO

Si el lector siguió con detenimiento las explicaciones dadas a lo largo de todas estas pági-
nas, al menos tendrá claras dos cosas: una computadora no es tal sin todo el complejo de
programas del sistema que la acompañan y, el software de base es un área especializada
dentro de las ciencias de la computación, con niveles de profundidad que rebasan amplia-
mente los límites de nuestro libro y se extienden a estudios de posgrado.
Sin embargo, podemos intentar resumir aún más la información porque en realidad el
software de base es un tema que concierne sobre todo a los diseñadores y estudiosos de las
ciencias de la computación, y no tanto a los usuarios de una computadora. Como se expre-
só antes, cuanto más sepa un usuario acerca de la filosofía y modo de operación de un
sistema de cómputo, mejor uso potencial podrá hacer tanto de él como de las posibilida-
des de desarrollo que le ofrece.

(t) Pero en 1997 Lenat se vio obligado a abandonar el proyecto por problemas de financiamiento y
falta de resultados concretos. Entonces se fundó una empresa comercial dedicada a obtener aplica-
ciones a partir del enfoque de Cyc : http: / /www. cyc . com .
242 Capítulo 5 Software de base

Sabemos que una computadora tiene capacidades bastante limitadas en lo referente a


la cantidad y diversidad de operaciones elementales que puede procesar, pero que a cam-
bio es capaz de ejecutarlas a enormes velocidades. Esto da lugar a una aparente paradoja:
se dispone de una máquina que rebasa inimaginablemente al ser humano en velocidad y
capacidad de proceso, pero sus habilidades son tan restringidas que no resulta de mucha
utilidad práctica.
¿Qué hacer para aprovecharla cabalmente? La respuesta está en el software de base,
El papel del software que utiliza precisamente esas virtudes electrónicas de velocidad para enriquecer en forma
de base gradual la complejidad de sus operaciones.
La idea detrás de esto es sencilla, y consiste en armar herramientas de programación
integradas a la computadora, que la hagan parecer dotada de capacidades cualitativamente
superiores.
En la sección 5.1 se explicó cómo es y qué se puede esperar del idioma propio de las
computadoras, llamado lenguaje de máquina. Se vio que, por sus características constructi-
vas, la unidad central de procesamiento puede ejecutar un pequeño número de instrucciones
de ;máquina que le son particulares, y cuyo radio de acción alcanza tan sólo a los elementos
que la constituyen (registros, acumulador, celdas de memoria central, etc.). Las desventajas
de hacer programas en lenguaje de máquina son múltiples, y pueden resumirse así:

- Un programa en el lenguaje de una máquina no puede ser "entendido" por otra de


Lenguaje de máquina características diferentes.
— Un programa en lenguaje de máquina resulta por completo incomprensible para
nosotros ya que, por definición, forma parte de los llamados programas objeto:
únicos ejecutables directamente por un procesador y escritos en sistema binario.
- La programación de este tipo no puede alcanzar niveles considerables de comple-
jidad, pues carece casi por completo de estructura y contenido semántico, porque
las instrucciones de máquina no pueden sino remedar las particularidades y limita-
ciones de la unidad central de procesamiento.

Pero la inevitabilidad de los programas en lenguaje de máquina se deriva, por su-


puesto, de que logran que una computadora efectúe las tareas encomendadas, en forma
automática y a gran velocidad.
El desafío está claro: debe darse respuesta al problema con que nos enfrentamos, que
se puede incluso caracterizar en términos filosóficos, si nos detenemos a considerar la
razón de ser de las computadoras. Proponemos la idea (desarrollada aparte) de que una
máquina de esta clase tiene posibilidades de servir no sólo como instrumento de cálculo,
sino como una herramienta con posibilidades de extender las capacidades de la mente y
ayudarla a modelar en forma adecuada la realidad.
Pues bien, como existe una diferencia considerable entre las computadoras y noso-
tros, tanto en velocidad como en capacidad y alcances, requerimos de un hilo conductor
para servir de guía en el estudio del problema de la comunicación entre humano y máqui-
na, y éste no puede ser otro que lo que llamaremos la "distancia gnoseológica" (en térmi-
nos de teoría del conocimiento) entre ambos. Por esto nos referimos a las capacidades
de abstracción, por un lado, y a la facilidad de comunicación, por el otro. Claramente, a
medida que se utilizan menos recursos para entablar comunicación con una computadora
(como en el caso del lenguaje de máquina), menor resulta la calidad de lo que se puede
comunicar; y si se requiere un alto nivel de comunicación, que incluso maneje ciertas
formas de abstracción, hay que pagar el precio derivado precisamente de esa distancia.
Esto resulta aparente al hacer el análisis de la función de los ensambladores, compiladores
y sistemas operativos, de acuerdo con el siguiente esquema, que ilustra simbólicamente
las distancias por recorrer para, partiendo de los niveles de comunicación a los que esta-
mos acostumbrados, llegar a establecer contacto con una computadora.
Sección 5.9 Resumen del capítulo 243

Ser humano, con ilimitadas capacidades de abstracción

Inteligencia
artificial
NIVEL 5
Sistemas
operativos
NIVEL 4

NIVEL 3

NIVEL 2

NIVEL 1 Computadora (lenguaje de máquina)

Se han resumido ya las limitaciones del lenguaje de máquina, y desde ellas se anali-
zará del mismo modo el problema de los ensambladores.
La idea de un ensamblador consiste sencillamente, como se dijo en el apartado 5.2,
en que la propia computadora sea la que traduzca a lenguaje de máquina las expresiones
escritas en lenguaje ensamblador. El lenguaje ensamblador dispone de algunas facilida-
des adicionales sobre el limitado lenguaje de máquina, pues permite trabajar con cierta
independencia de la arquitectura (configuración física) de la unidad central de procesa-
miento y la memoria, aunque tampoco constituye la respuesta completa al problema de la
distancia que nos separa de un sistema de cómputo en este segundo nivel.
Las desventajas principales de la programación en lenguaje ensamblador son:
Lenguaje
- Un programa escrito en el lenguaje ensamblador de una máquina no puede ser ensamblador
"entendido" por otra de tipo diferente.
- El lenguaje ensamblador sigue dependiendo en gran medida de las particularida-
des de la unidad central de procesamiento y las celdas de memoria, lo cual vuelve
difícil y penosa la tarea de hacer programas complejos.
— Es prácticamente imposible mantener la estructura y riqueza expresiva de una
idea cuando ésta se expresa en lenguaje ensamblador.
Por otro lado, sus ventajas respecto al primer nivel (lenguaje de máquina) son eviden-
tes: libera al programador de la dependencia total de las direcciones absolutas de memoria
y le permite la posibilidad de emplear variables simbólicas y etiquetas en sus programas.
Se dijo además que es posible enriquecer sustancialmente la idea del ensamblador si
se le añade la capacidad de manipular grupos de instrucciones como si fueran una sola
unidad, dando origen al concepto de los macroprocesadores y los macroensambladores.
Por medio de esta nueva idea somos ya capaces de comunicarnos con la máquina sin
necesidad de repetir en los programas conjuntos de instrucciones que son necesarias, de-
jando esta tarea al nuevo traductor.
Como resulta comprensible, el método "genético"t empleado no es sin costo: aunque
resulta más atractivo para nosotros trabajar en este segundo nivel, no lo es tanto para la

(f) Lo llamamos así para enfatizar su característica evolutiva; es decir, un nivel superior está formado
con los elementos que el anterior hace posibles, y los emplea de forma integral para ser lo que es.
El concepto mismo de herramienta tiene estas características, pues por medio de las más primitivas
es posible construir otras más avanzadas, que a su vez servirán para continuar el proceso.
244 Capítulo 5 Software de base

máquina, que ahora tiene la tarea de traducir primero a lenguaje de máquina todo lo que se
le dice, para poder entonces cargarlo a la memoria y ejecutarlo. Es por esto que se dedicó
un espacio (Sec. 5.4) a describir la idea fundamental de los cargadores, cuya función
consiste precisamente en tomar un conjunto de instrucciones de máquina almacenadas,
por ejemplo, en el disco magnético, y depositarlas en la memoria central.
Si se desea dar otro paso hacia arriba habrá que detenerse y considerar con mucho
cuidado las tareas que siguen, porque implican la capacidad —por primera vez— de ex-
presar ideas en términos dotados de una estructura, como se explicó en el apartado 5.5.
Esto significa que hay que "enseñar" a la computadora a analizar frases completas, y ya
no simples conjuntos de instrucciones del procesador, lo cual se logra por medio de un
traductor especializado llamado compilador. Los problemas teóricos a los que hubo que
enfrentarse son de magnitud tal que todavía siguen despertando interés entre la comuni-
dad académica internacional, pues de ninguna manera está cerrado el desarrollo en este
campo.
Como ya se señaló, existen múltiples lenguajes de programación de alto nivel expre-
sivo que comparten, de alguna u otra manera, las siguientes ventajas con respecto al nivel
anterior:

– En principio, es posible compartir un programa escrito en alguno de estos lengua-


Lenguajes jes con cualquier computadora que disponga del compilador adecuado para con-
de alto nivel vertir el programa fuente original en un programa objeto directamente ejecutable
por el procesador.
– Ya es posible respetar, en buena medida, la estructura original de una idea des-
crita detalladamente, y más aún si ha sido definida por medio de una metodología
adecuada.
Sin embargo, sigue siendo necesario un entrenamiento formal para adquirir la capa-
cidad de programad, y las computadoras aún están lejos de los niveles de abstracción y
comunicación que usamos normalmente.
Si se mira hacia atrás se podrá distinguir que en este tercer nivel las cosas han cam-
biado bastante, pues ahora todo nos resulta más fácil, aunque es más complejo para la
computadora. Se requiere coordinar múltiples acciones, todas tendientes a que la máquina
misma traduzca lo que se le expresa, paulatinamente bajándolo de nivel hasta llegar al
lenguaje de máquina, que es lo único que puede procesar.

(t) A ello se dedica la segunda parte de este libro. Por otra parte, William ("Bill") Gates, fundador
de la empresa Microsoft, advierte a uno de los lectores de su columna (que aparecía publicada en
algunos periódicos):
"Es bueno aprender programación, inclusive si usted se limita a usar computadoras. Si apren-
de a programar, sabrá cómo funcionan las computadoras y en qué clase de cosas resultan útiles.
Si desea escribir programas profesionales, debe aprender el lenguaje de programación C++ y
algún lenguaje de máquina.
Si se considera un buen programador o quiere descubrir cuánto sabe, lea The Art of Computer
Programming, escrito por Donald Knuth. Trate de resolver los problemas que se plantean en el
libro.
La obra de Knuth está publicada por Addison-Wesley. Hasta ahora han aparecido tres volúme-
nes: Fundamental Algorithms [KNUD97], Seminumerical Algorithms y Sorting and Searching.
Si algunos tienen la audacia de pensar que saben todo, Knuth les ayudará a comprender que el
mundo es profundo y complicado.
Me tuve que someter a una increíble disciplina y me tomó varios meses concluir la lectura de
esos volúmenes. Estudié 20 páginas del primer volumen, puse el libro a un lado durante una sema-
na y volví al mismo para leer otras 20 páginas. Si usted puede leer los tres volúmenes en su totali-
dad, envíeme su currículum."
Sección 5.9 Resumen del capítulo 245

Esto ya es razón suficiente para decidirnos a estudiar alguna forma de reducir la


complejidad de las tareas adicionales, lo cual lleva directamente al tema de los sistemas
operativos.
La función general de un sistema operativo es controlar y dirigir la operación de las
computadoras, de forma tal que presenten una imagen monolítica y virtual (en contra- El sistema operativo
posición con real o electrónica o ingenieril) ante los usuarios —no sólo uno— del sistema
de cómputo. Hemos mencionado que consideramos tan importante al sistema operativo
como a las facilidades físicas mismas del equipo y que, de hecho, no se puede hablar de
una computadora sin suponer implícitamente al sistema operativo que la controla y
coordina.
Se espera de un sistema operativo, grosso modo, que sea perfectamente capaz de
atender la operación concurrente de múltiples pedidos de atención por parte de procesos
que están en ejecución en la computadora; que pueda mantener toda la operación bajo
control sin perder detalle alguno; que logre un óptimo grado de utilización de los recursos
físicos de la máquina (procesador, memoria, periféricos)... y, por último, que haga todo
esto callada y eficientemente.
Este cuarto nivel de comunicación con la máquina no es tan conveniente para la
máquina como para los humanos, porque debe dedicar muchos recursos auxiliares para
lograr la ejecución de la tarea original, lo cual es absolutamente justificable dados los
beneficios que reporta.
El último nivel del diagrama muestra un área que aún no adquiere importancia capi-
tal, pero que está destinada a desempeñar un papel relevante en el futuro: la inteligencia Inteligencia artificial
artificial. Con este término no debemos aceptar implicaciones de ciencia ficción, sino un
estudio científico y formal de algunos de los mecanismos con los cuales funcionan las
capacidades humanas de entendimiento, razonamiento y aprendizaje. Existen suficientes
razones teóricas para denegar la idea de que las computadoras tomarán el control en algún
momento, así como también para suponer no demasiado lejano el día en que se puedan
encargar de una fracción de las tareas que actualmente recaen sobre nosotros, desde la
automatización ("robotización") de muchos procesos de producción hasta la exploración
del espacio exterior y el mejoramiento de las cosechas.
Por lo pronto, las tareas de la inteligencia artificial se han enfocado a la integración de
sistemas dotados de capacidades limitadas de síntesis de voz, movimiento y percepción,
así como al desarrollo de estrategias y esquemas de manejo de información (sistemas
expertos), con la posibilidad tanto de externar opiniones autorizadas sobre temas especí-
ficos (perforaciones de pozos petroleros, geotermia, diagnósticos clínicos, etc.) como de
aprender más sobre el tema.
Es mucho lo que debe esperarse en el futuro próximo, y de hecho existen esfuerzos
de envergadura nacional para este propósito tanto en los Estados Unidos como en Euro-
pa y Japón, porque la siguiente generación de computadoras estará caracterizada por la
inteligencia artificialt. Para obtener un panorama amplio de lo posible en los próximos
años, tanto en el campo de la inteligencia artificial como en la tecnología de computadoras
y sus aplicaciones en general, consúltese el ya referido número completo que la revista
Scientific American dedicó a "La siguiente revolución de las computadoras" en octubre
de 1987, así como el también mencionado número de septiembre de 1995. La prestigiosa
revista Communications of the ACM del mes de enero de 1996 se dedicó a analizar los
sistemas actuales de procesamiento de lenguaje natural, que aún no han llegado a los nive-
les previstos.

(t) Lo cual aún no sucede. En 1990 terminó el período de 10 años que Japón dedicó a la llamada
"quinta generación de computadoras", pero los resultados fueron bastante más grises de lo pro-
metido.
246 Capítulo 5 Software de base

5.10 ANEXO: LENGUAJES DE PROGRAMACIÓN

En la sección 5.5 se estudiaron los compiladores; aquí se mencionan algunos de los len-
guajes de programaciónt más importantes y usuales.
Ada, que debe su nombre a Ada Byron,
condesa de Lovelace (1815-1852), asisten-
te de Babbage en el desarrollo de la má-
quina analítica, fue un intento más por tener
un único lenguaje de programación de uso
verdaderamente universal. El diseño de
este lenguaje tomó varios años y fue aus-
piciado por el Departamento de Defensa
de los Estados Unidos, que desde 1983
exigía que toda la programación desarrolla-
da internamente esté escrita en Ada; el gru-
po que produjo Ada estuvo a cargo de Jean
Ichbiah. Definitivamente se trata de un
lenguaje de características avanzadas, con
manejo dinámico de memoria y recursivi-
dad así como facilidades integradas para
programación concurrente, pero la reali-
dad es que no tuvo la difusión prevista, y
resultó sólo uno más en la gran familia de
lenguajes de programación. Incluso, Ada
Ada Byron, la primera causó una polémica en términos académi-
programadora cos, porque se argüía que el lenguaje (y su
correspondiente compilador) era demasiado grande y poco elegante, y la tendencia mo-
derna en lenguajes de programación es hacia los pequeños y funcionales. Véase, por ejem-
plo, la dirección Internet http: //info.acm.org/sigada.
ALGOL (ALGOrithmic Language: lenguaje algorítmico), puede considerarse como el
iniciador de la familia de lenguajes de programación estructurada. Diseñado para aplica-
ciones científicas en 1960 por un comité internacional con sede en Europa, fue el primero
con sintaxis definida de manera formal y matemática. Existió una versión más moderna
usada en algunas universidades (y de amplio uso en Europa), llamada ALGOL 68. Un
programa en ALGOL consistía en módulos conocidos como procedures, que podían estar
anidados y ser llamados recursivamente. Como el manejo de memoria era dinámico, las
dimensiones de los arreglos podían variar durante la ejecución. Entre los miembros del
comité se pueden encontrar figuras tan importantes para la computación como John Backus
(diseñador de FORTRAN), Alan Perlis, (diseñador del pionero lenguaje IT, de 1957),
Friedrich Bauer (diseñador de un lenguaje algebraico en los años 50) y Peter Naur (coau-
tor de la notación formal BNF: Backus-Naur Form, empleada para la especificación
sintáctica de lenguajes) "Algo" (demonio) también es el nombre de una estrella doble de
la constelación Perseo, a 82 años luz de la Tierra.
APL (A Programming Language: un lenguaje de programación), es un interesante, con-
ciso y poderoso lenguaje de aplicación especial y matemática, inicialmente usado en má-

(t) Consúltense los libros [APPV98], [PRAZ97], [SEBR99] y [SETR92] para una referencia am-
plia sobre los lenguajes de programación, así como el artículo [OKTH96].
Sección 5.10 Anexo: lenguajes de programación 247

quinas IBM, diseñado en 1961 (aunque existen versiones nuevas). Una sola expresión en
APL puede ser equivalente a decenas de renglones de los lenguajes "normales". Supues-
tamente es de uso general pero en realidad su manejo nunca fue muy conocido. También
es un medio de representación de notación matemática, con más énfasis en la defini-
ción de formas que de procedimientos. Su autor, Kenneth Iverson, de IBM, explicaba: "el
lenguaje está basado en una unificación y extensión consistentes de las notaciones ma-
temáticas existentes, y en una extensión sistemática de un pequeño conjunto de operacio-
nes matemáticas y lógicas sobre vectores, matrices y árboles". En la dirección Internet
http://www.acm.org/sigapl hay todo un "grupo de interés" dedicado al lenguaje.

BASIC (Beginners All purpose Symbolic Instruction Code: código simbólico de propó-
sito general para principiantes). Lenguaje dedicado inicialmente a la programación en
máquinas pequeñas. Fue diseñado en 1964 por Thomas Kurtz y John Kemeny, del Dartmouth
College, en New Hampshire, Estados Unidos. Existe un gran número de versiones dife-
rentes, por lo que podría decirse que es casi el único de los lenguajes de programación no
estandarizado.
Un programa en BASIC consta de un conjunto de renglones numerados (aunque las
versiones nuevas ya no requieren esto). Se puede llamar a subrutinas, pero algunos tipos
de BASIC no permiten el paso de parámetros. El manejo de memoria es estático, aunque
con algunos intérpretes, que requieren un subsistema durante la ejecución, es posible que las
dimensiones de los arreglos varíen en forma dinámica.
Hubo gran controversia acerca de si BASIC debe enseñarse como primer lenguaje de
programación; un punto de vista sostiene que sí, porque es sencillo y fue originalmente
diseñado para ese fin, mientras que el otro argumenta que con las prácticas actuales de pro-
gramación estructurada, no sólo es un lenguaje pobre, sino que además tiene efectos con-
traproducentes. Un autor muy conocido llegó a hacer la cáustica observación de que BASIC
"causa daño cerebral en los que aprenden a programar con él". Las versiones actuales de
QBASIC, y Visual Basic para Windows", son excelentes y han dado a este lenguaje una
dimensión mucho más elevada. De hecho, Visual Basic inició en 1991 la tendencia actual
de la programación interactiva, en donde el programador arma interfaces gráficas (venta-
nas) que contienen "íconos" gráficos activables por el usuario mediante el mouse y el te-
clado. Véase, por ejemplo, la dirección Internet http : / / advancedbasic hypermart . net.
C Lenguaje especializado en la programación de sistemas. Diseñado a principios de la
década de 1970 por Dennis Ritchie, de los Laboratorios Bell, en New Jersey, Estados
Unidos, se emplea para escribir compiladores y sistemas operativos; desde hace tiempo
una buena parte de ellos se escribe en C.
El poder de este lenguaje estriba en que el código producido es bastante similar al que
un programador lograría si escribiera en ensamblador, con la enorme ventaja de ser un
lenguaje de alto nivel. Es posible, por ejemplo, asignar registros de la UCP durante la
compilación, y llamar a rutinas y subsistemas del sistema operativo desde el programa,
sobre todo cuando se emplea desde el sistema operativo Unix.
Un programa en C consta de módulos que pueden ser llamados de manera recursiva,
pero no anidados. Su manejo de memoria es dinámico.
Existen muchas direcciones de Internet dedicadas a C, por ejemplo:
http://cm.bell-labs.com/m/cs/who/dmr/chist.htmi
y también http : / /www cprog ramming . com.
C++ es una versión aumentada del lenguaje C, diseñado hacia 1982 por Bjarne
Stroustrup, de los Laboratorios Bell. Es una extensión que permite la programación orien-
tada a objetos (00P: Object Oriented Programming), además de emplear recursos grá-
ficos estandarizados. Gran parte de los sistemas modernos de todo tipo están escritos en
este poderoso y complejo lenguaje. El capítulo 9 de este libro explica los inicios de la
programación en C++. La programación por objetos tiene entre sus fundamentos el con-
248 Capítulo 5 Software de base

cepto de clase, originalmente incluido en el lenguaje Simula 67. Una de tantas direccio-
-

nes de Internet dedicadas a C++ es http: //www. topcode com/c++/main . shtml.


COBOL (COmmon Business Oriented Language:
lenguaje general para uso en negocios) Uno de los
primeros lenguajes de programación. Fue diseñado
hacia 1958 por la doctora en matemáticas y almirante
Grace Murray Hopper dentro de un comité norteame-
ricano llamado CODASYL (COnference on DAta
SYstems Languages), y se sigue usando mucho para
aplicaciones comerciales y administrativas.
Un programa en COBOL consta de cuatro "divi-
siones" (IDENTIFICATION, EIVVIRONMENT, DATA
y PROCEDURE), que tienen que ser especificadas
siempre. Los programas tienden a ser grandes y están
llenos de palabras y frases cortas en inglés, por lo que
son bastante legibles, si bien no muy estructurados.
Grace Hooper, creadora Su manejo de memoria es estático, aunque la mayoría
de COBOL de las versiones incluyen subsistemas de manejo di-
námico de información en disco magnético.
Se suele considerar a COBOL como un mal lenguaje de programación, anticuado
y engorroso, cuando en realidad el problema estriba en la deficiente preparación en
computación por parte de algunos de sus adeptos. Desde hace muchos años se augura
la desaparición de este lenguaje, pero esto no ha sucedido, sobre todo en el entorno de las
grandes computadoras y centros de cómputo. Existen excelentes libros sobre progra-
mación estructurada y diseño modular en COBOL, y últimamente hasta hay versiones
del lenguaje "orientadas a objetos". Quien crea que COBOL ha muerto puede consul-
tar, por ejemplo, las direcciones Internet http: / /www. cobolportal . com y también
http://almeria.net/usuarios/cobol.

Delphi Es un poderoso entorno de programación y ejecución para el sistema operativo


Windows, derivado del lenguaje de programación Object Pascal, y guiado por los objetos
gráficos que el programador coloca en las ventanas de la pantalla, que a su vez responden
a las activaciones del ratón o el teclado por parte del usuario. Se inicia con la definición de
las interfaces con el usuario, utilizando de lleno la Graphical User Interface (GUI) y sus
bibliotecas de objetos predefinidos, para producir pantallas con un mínimo esfuerzo. Lue-
go, el programador escribe código para "reconocer los sucesos" activados por el usuario y
actuar en consecuencia, ejecutando el código predefinido para cada evento. En Delphi
(y los demás lenguajes "visuales") se emplea el término "proyecto" en lugar de "progra-
ma" para referirse a la combinación implícita de los diferentes códigos (para responder a
los eventos, incluyendo el manejo de errores) y las interfaces gráficas. Así, se dice que es
el usuario el que controla el programa, y no al revés (de hecho, un programa en Delphi no
tiene un punto único de inicio). Para mayor información, véase, por ejemplo, la dirección
Internet http: / /www. doit . com/delphi.
FORTRAN (FOrmula TRANslation: traducción de fórmulas), —diseñado en 1957
por John Backus, de IBM— fue el primer lenguaje de programación ampliamente usa-
do, sobre todo en las áreas de ingeniería. A partir del estándar FORTRAN IV han exis-
tido ediciones mejoradas, aptas para programación moderna: FORTRAN 77, 90 y 95
(que producen código "paralelizable", para procesamiento en supercomputadoras). Al
igual que con COBOL, ha habido multitud de predicciones sobre la pronta desaparición
de este lenguaje de los centros de cómputo, pero no ha sido así, aunque ciertamente su
Sección 5.10 Anexo: lenguajes de programación 249

uso disminuyó fuertemente, pues en casi todas las escuelas se enseña Pascal, C o Java
en su lugar.
Un programa en FORTRAN puede estar constituido por un programa principal y
varias subrutinas. Una de las ventajas de emplearlo es el acceso a una enorme cantidad
de bibliotecas (libraries) especializadas en manejos numéricos y matemáticos. Sigue
siendo el lenguaje de uso para procesos numéricos en las supercomputadoras.
FORTRAN sigue vivo, y hay nuevas versiones; véase, por ejemplo, la dirección de Internet
http://www.fortran.com/info.html.

Java Lenguaje diseñado en la década de 1990 por


James Gosling, vicepresidente de Sun Microsystems,
los fabricantes de equipos y pioneros en las redes de
minicomputadoras mencionados en el capítulo 1. Su
uso inicial ha sido como vehículo para la produc-
ción de aplicaciones autónomas llamadas applets,
que funcionan en las hojas Web de Internet, y que
los usuarios pueden "bajar" de la red global para eje-
cutarlas en su propio entorno Java local. Pero ade-
más se trata de un lenguaje moderno, orientado a
objetos, e independiente de la arquitectura de la compu-
tadora en donde opera, debido a que los compiladores James Gosling, creador
producen un código intermedio especial llamado de Java

Java bytecodes, que luego es interpretado por cada


computadora particular. Todo en Java son clases, de las cuales se derivan ejemplos
(instances) de objetos, compuestos a su vez por campos (o datos) y métodos (o procedi-
mientos). Se le considera como una buena alternativa al lenguaje C++. Una dirección
Internet "oficial" de Java es http://www.sun.com/products-n-solutions/software/
oe-platforms/java2.html.

LISP (LISt Processing: procesamiento de listas)


es un lenguaje muy utilizado en la comunidad aca-
démica dedicada a investigación en inteligencia
artificial. Maneja en forma dinámica conjuntos lla-
mados listas, que el programador construye por
medio de elementos primitivos llamados átomos.
Es, en esencia, un medio para representar funciones
parcialmente recursivas en matemáticas, y con él se
pueden definir funciones complejas y evolutivas
(porque se desarrollan siguiendo esquemas forma-
les). El procesamiento de listas fue inicialmente de-
sarrollado por Allen Newell, John Cliff Shaw y Herbert Simon, destacada
Herbert A. Simon (n. 1916; premio Alan Turing en figura de la computación
1975 y además Premio Nobel de economía en 1978), (premio Turing), la economía
(Premio Nobel) y la psicología
y el lenguaje LISP fue diseñado por John McCarthy cognitiva --
(n. 1927) en 1958. Durante muchos años estuvo
ligado con las labores desarrolladas en el Institu-
to Tecnológico de Massachusetts (MIT), hasta que fue reconocido como el lenguaje
de la inteligencia artificial, lugar que últimamente le disputa Prolog. El lenguaje se
emplea en el diseño y desarrollo de poderosas herramientas de diseño asistido por
computadora (CAD), y se han desarrollado múltiples versiones nuevas de LISP para
las computadoras personales y para Unix/Linux; véanse, por ejemplo, las direcciones
250 Capítulo 5 Software de base

Internet http: / /www. ai . mit .edu/docs/articles/ /good -news/good -news.html


y también http: / /www.ctima. uma.es/turing/turing7/mccarthy.html
además de http: / /www-formal stanford .edu/jmc/biography.htm

Logo es un lenguaje basado en los mismos principios de LISP, pero expresados en otra
forma, por lo que es mucho más fácil de manejar, aunque menos poderoso y general. Fue
diseñado por un educador y matemático del MIT, Seymour Papert. Adquirió cierta popu-
laridad para computadoras personales porque hay versiones que producen gráficas de
manera casi natural, y se prestan, por tanto, para aplicaciones de tipo educativo. La direc-
ción Internet http: / /el .www. media . mit .edu/logo foundation/logo. html está dedi-
-

cada a aplicaciones y desarrollos en Logo.

Modula-2 fue diseñado en 1979 por el creador de Pascal, Niklaus Wirth, e incluye algu-
nas mejoras sobre su antecesor, así como un concepto un tanto diferente para la creación
de sistemas de programación. Recibe su nombre de la importancia adjudicada al manejo
unidades lógicas de programa en forma de módulos. Dispone de formas novedosas de
manejo de paso de parámetros, y de operadores para "importar" y "exportar" datos entre mó-
dulos, lo cual le da gran flexibilidad y permite la compilación separada e independiente de
rutinas que colaborarán entre sí al momento de la ejecución. Años después, Wirth diseñó
un lenguaje integrado a un sistema operativo, llamado Oberon, para continuar realizando
sus ideas sobre entornos modulares de programación. Véanse, por ejemplo, las direccio-
nes Internet http: / /www. cetus links.org/oo_oberon.html
-

y también htt p : / /www. oberon . ethz . ch .


Existe también una nueva versión del lenguaje, orientada a objetos: Modula-3,
que se puede consultar en la dirección Internet
http://www.research.digital.com/SRC/modula 3. -

Pascal, diseñado en 1970 por Niklaus Wirth, del


Instituto Tecnológico de Zurich, en Suiza, es un len-
guaje de programación popular, sobre todo para es-
tudiantes. Existe una versión muy empleada que
debe su gran éxito a un compilador (para compu-
tadoras personales) prodigiosamente veloz, diseña-
do en 1983 por Philippe Kahn, de la recién creada
empresa Borland. Un programa en Pascal consiste
en módulos que pueden ser anidados y llamados
recursivamente. El lenguaje permite la definición
de estructuras de datos más allá de las tradiciona-
les (entero, real, etc.), por lo que es muy adecuado
El lenguaje de programación
recibió ese nombre en honor para la programación estructurada. El capítulo 10
de Blaise Pascal. de este libro muestra los inicios de la programa-
ción en Pascal. Existen versiones de un Pascal ex-
tendido con operaciones para control de procesos en paralelo (Pascal concurrente). Los
nuevos compiladores ofrecen además capacidades de programación orientada a obje-
tos, y esto (combinado con interfaces gráficas) configura un nuevo lenguaje, llamado
Delphi.
Wirth desarrolló posteriormente el lenguaje Modula-2, que incorpora algunas mejo-
ras y corrige algunos de los errores conceptuales de Pascal; luego vino Modula-3, para
programación orientada a objetos. Sobre Pascal se pueden consultar, por ejemplo, las
direcciones Internet http: / /www. cit .ac. nz/smac/pascal/default htm
y también http: / /members. xoom . com/tutoriales / index . htm.
Sección 5.10 Anexo: lenguajes de programación 251

Perl (Practical Extraction and Report Language: lenguaje práctico para extracción y
reportes) Lenguaje de manipulación de textos, archivos y procesos, de uso restringido al
sistema operativo Unix, creado por Larry Wall y Randal Schwartz, y empleado funda-
mentalmente por administradores de sistemas Unix. El lenguaje recoge elementos de C y
de un lenguaje anterior (interpretado, no compilado) especial para manipulación de datos,
llamado awk, pues sus autores fueron Alfred Aho, Peter Weinberger y Brian Kernighan.
Tanto en Awk como en Perl se pueden realizar con facilidad tareas de procesamiento de
patrones y filtraje de datos, que en otros lenguajes representarían verdaderas hazañas
de programación. Véanse, por ejemplo, las direcciones Internet http: / / www . pe rl . com y
también http: / /lamport.rhon.itam.mx/dcomp/awk.html.

PL/I (Programming Language 1: lenguaje de programación I) fue un lenguaje muy amplio


y extenso propuesto por IBM en 1965 como alternativa para trabajos científicos y comer-
ciales. El lenguaje y el compilador fueron diseñados por un equipo encabezado por George
Radin. Aunque nunca fue de uso generalizado en todas las computadoras, se empleó mu-
cho en los grandes ambientes de cómputo de IBM, y fue considerado como un lenguaje de
programación bueno y completo.
Los compiladores de PL/I eran grandes, por lo que se trabajaba con subconjuntos del
lenguaje para lograr resultados eficientes.
Un programa en PL/I consistía en un procedimiento principal, dentro del cual se pueden
anidar bloques. Su manejo de memoria puede ser estático o totalmente dinámico y recursivo.
Las versiones completas permitían la programación de "tareas" (tasks) para simular mul-
tiprocesamiento e incluían el concepto de apuntador, ahora muy empleado. Hay al menos
una dirección de Internet dedicada al lenguaje: http: //www 4.ibm.com/software/ad/pli.
-

Prolog (PROgramming LOGic: lógica de programación) forma parte de un enfoque de


programación diferente del tradicional y estructurado o imperativo (lo mismo sucede con
APL y LISP), porque considera que la programación puede ser vista desde una perspecti-
va de aplicación de funciones lógicas sobre predicados basados en el cálculo proposicional.
Este lenguaje, creado en 1972 por Alan Colmerauer y Philippe Roussel de la Universidad
de Marsella, Francia, y por Robert Kowalski, de la Universidad de Edinburgo, fue adop-
tado por el proyecto de la quinta generación de computadoras. emprendido por Japón,
como vehículo de creación de los sistemas y programas para manejo de inteligencia arti-
ficial. Una dirección Internet dedicada es http: / /www.lpa.co.uk .
Un nuevo lenguaje considerado como sucesor de Prolog se llama Giidel, y está dispo-
nible en la dirección Internet http://dmoz.org/Computers/Programming/Languages/Godel.
RPG (Report Program Generator: programa generador de reportes) Lenguaje diseña-
do por IBM para producir, como su nombre lo indica, informes administrativos y comer-
ciales. En su forma original no es realmente un lenguaje de programación genérico, ni un
vehículo adecuado para hacer buena programación. Los programas en RPG no son legibles
ni claros, y deben ser codificados en hojas especiales. Existen versiones actualizadas que
eliminan algunas de las desventajas mencionadas. Véase, por ejemplo, la dirección Internet
http://www 4.ibm.com/software/ad/varpg.
-

Además, hay y hubo muchos otros lenguajes, algunos de ellos de propósito especial.
Forth, creado a inicios de la década de 1970 por Charles H. Moore, fue un lenguaje para
microcomputadoras que intentó combinar características del lenguaje ensamblador con
una metodología de diseño de software conocida como "código hilvanado", que resultó
de interés para algunas aplicaciones, donde reemplazaba al lenguaje ensamblador. El len-
guaje sigue teniendo sus seguidores, y nuevas versiones; véase, por ejemplo, la dirección
Internet http: //www.forth.org/fig.html.
252 Capítulo 5 Software de base

Jovial (Jim's Own Version of Algol) fue uno de esos lenguajes de los años 70 que estuvo
fuertemente ligado a ideas "extrañas" de su autor (llamado Jaime, como podía esperarse).
Sería una intrascendente curiosidad histórica de no ser porque buena parte del (desde hace
muchos años parcialmente obsoleto) sistema de control aéreo estadounidense está codifi-
cado en Jovial, y por ello hay fuertes problemas para actualizarlo (véase, por ejemplo, el
número de agosto de 1977 de la revista Spectrum, de la IEEE, dedicado por completo al
tema del control de tráfico aéreo). En la dirección Internet htt p : / /www. ddc . d k hay más
información.
Simula, lenguaje para simulación de procesos diseñado en Noruega en los años 60
por Ole-Johan Dahl y Kristen Nygaard, y originador de lo que ahora se conoce como
programación orientada a objetos, debido a su concepto de "clase" como unidad in-
dependiente de datos. En la referencia [MEYB99], citada en el capítulo 7, hay una
sección completa sobre Simula. También se puede consultar la dirección Internet
http://www.jsp.umontreal.ca/~simula.
SNOBOL (String-Oriented Symbolic Language: lenguaje simbólico para manejo de
cadenas), para manipulaciones simbólicas, diseñado por D. Farber, Ralph Griswold y F.
Polensy en los Laboratorios Bell en los años 60, y que sirvió como guía conceptual en el
diseño de lenguajes posteriores bajo el sistema operativo Unix. Algunas versiones ante-
riores de Unix incluían un intérprete de Snobol.
Se puede consultar, por ejemplo, la página de Internet
http://irisl.let.kun.nl/TSpublic/coppen/SNOBOL.html.
Smalltalk y Eiffel son lenguajes por objetos (donde el programador no enfoca su aten-
ción en variables o estructuras de control, sino en los objetos sobre los que trabaja el
programa). Smalltalk fue diseñado en el centro de investigaciones de Xerox, en California,
a inicios de la década de 1970 por Adele Goldberg y Alan Kay y ha tenido versiones
posteriores. Véase, por ejemplo, la dirección Internet ht tp : / / www . smallt alk . o rg.
Eiffel, diseñado por Bertrand Meyer, apareció hacia 1986 y es empleado para la pro-
gramación por objetos "pura" (es decir, no tan sólo orientada a objetos). El libro [MEYB99],
citado en el capítulo 7, se dedica por completo a Eiffel. Además, se puede consultar, por
ejemplo, la dirección Internet http: / /www. eiff el . com/eiff el/index html.

El asunto de las preferencias individuales en los lenguajes de programación a veces


adquiere características casi místicas, derivadas de experiencias iniciales, prejuicios o
afinidades difíciles de explicar. El lector debería escuchar nuestro consejo y adoptar la
conclusión de que no resulta fácil, ni aconsejable para conservar amistades, tratar de de-
terminar cuál lenguaje es "el mejor".

5.11 ANEXO: "SI LOS SISTEMAS OPERATIVOS FUERAN


AEROLÍNEAS"t

Aerolíneas DOS: Todos los pasajeros empujan el avión hasta que despega, y luego
brincan para subirse un rato hasta que nuevamente golpea el suelo; después lo vuelven a
empujar y se suben otro momento, y así continúan.

(t) A mi amigo John Newland, ingeniero ex habitante del Silicon Valley, le debo el conocimiento
de esta demoledora visión sobre el software moderno que (en inglés) circulaba en Internet.
Ej ercicios 253

Aerolíneas System 7/MacIntosh: Todos los y las sobrecargos, los capitanes, los carga-
dores de maletas y los agentes de boletos se ven iguales, actúan igual y hablan igual. Cada
vez que uno intenta averiguar algún detalle se le dice que no hace falta saberlo, que no
debe saberlo y que todo se hará por uno sin necesidad de saber nada, así que mejor guarde
silencio.
Aerolínea OS/2: Para abordar el avión se debe sellar el boleto diez veces en diez dife-
rentes colas. Luego se llena una forma indicando dónde desea sentarse y si quiere tener la
sensación de que va en barco, en tren o en autobús. Si logra subirse al avión y el avión logra
despegar entonces se tendrá una excelente travesía, ...excepto por las veces en que el timón
y los alerones se congelan en sus posiciones, en cuyo caso se dispone de tiempo para
rezar y prepararse para la caída.
Aerolínea Windows 3.11: El aeropuerto es bonito y lleno de color, con sobrecargos
amables, fácil acceso al avión y un buen despegue... y luego la nave estalla sin ningún
aviso previo.
Aerolínea Windows 98: Se muestran fotografías de los mejores y más modernos avio-
nes jamás vistos. El aeropuerto tiene incluso más color que el de las aerolíneas Win 3.11. Sin
embargo, luego de que los pasajeros abordan el avión, se dan cuenta de que los pilotos, los
sobrecargos e incluso los aviones son los mismos que en las aerolíneas Win 3.11, y cuan-
do ya se alcanzó la altitud de crucero... entonces la nave estalla sin ningún aviso.
Aerolínea NT: Todo mundo corre en la pista, dice el password al unísono y forma la
figura de un avión. Luego todos se sientan y hacen sonidos como si fueran volando.
Aerolínea Unix: Cada quien trae consigo una pieza del avión cuando llega al aeropuerto.
Se reúnen en la pista y lo arman, discutiendo todo el tiempo acerca de cómo debe verse el
avión cuando lo terminen, pero nunca lo completan.

EJERCICIOS

1. ¿Puede existir un procesador que como parte de su lenguaje de máquina tenga la


instrucción INT(C,I,N,P) que obtiene la tabla de intereses que produce el capital C
a la tasa I durante N períodos P? ¿Qué implicaciones tendría esta posibilidad? ¿Por
qué si existen algunas calculadoras financieras que pueden hacerlo?
2. En realidad, un ensamblador no requiere dar un segundo paso completo sobre el
texto del programa fuente, pues basta con que almacene la posición, dentro del pro-
grama fuente, de los identificadores (variables o etiquetas) a los que se hace refe-
rencia pero que aún no han sido definidos. Para esto es suficiente con una tabla de
referencias pendientes. Describa con el mayor detalle posible cómo lograr esto
para obtener un ensamblador de "un paso y medio".
3. Un desensamblador es un programa que lee un programa objeto y reconstruye el
programa fuente original escrito en lenguaje ensamblador. En principio, esto es
posible porque existe una correspondencia uno a uno entre las instrucciones del
lenguaje de máquina y los mnemónicos de un lenguaje ensamblador, como se ha
explicado. Diseñe un desensamblador, con el mismo nivel de detalle que el em-
pleado en la sección 5.2 del texto.
4. ¿Puede un desensamblador reconstruir los nombres simbólicos de las etiquetas de
un programa? Justifique cuidadosamente su respuesta.
5. Distinga y caracterice las macroinstrucciones, las instrucciones de ensamblador,
las instrucciones de máquina y las microinstrucciones o micropasos.
254 Capítulo 5 Software de base

6. ¿Puede una macrodefinición lograr resultados que no estén considerados dentro de


las capacidades directas del lenguaje de máquina? Justifique su respuesta.
7. ¿Podrá haber macrollamadas dentro de las macrodefiniciones? Analice el caso. Dé
ejemplos.
8. Encuentre y analice ejemplos del problema del bootstrap en la vida cotidiana (hay
muchos). He aquí uno de ejemplo: la Asociación de Ciudadanos Distinguidos de-
cide, mediante consenso de su comité de admisiones, quiénes pueden adquirir la
categoría de ciudadano distinguido; ¿quién nombró inicialmente al comité?
9. ¿Puede un programa escrito en un lenguaje de programación compilable lograr
resultados que no estén considerados en las capacidades directas del lenguaje de
máquina? Justifique su respuesta.
10. ¿Por qué es posible que un programa esté sintácticamente bien pero semánticamente
mal? Dé ejemplos.
11. ¿Puede existir un "descompilador"? Justifique su respuesta.
12. ¿Por qué sí puede haber varios compiladores diferentes en una misma computado-
ra, pero tan sólo un ensamblador?
13. ¿Cuáles son las ventajas de la portabilidad del software y de qué depende?
14. Proporcione un orden natural para ejecutar las siguientes tareas involucradas en el
desarrollo de un programa: análisis del problema, carga, compilación, diseño de la
solución, ejecución, ensamblado, ligado, programación.
15. La política de admisiones empleada en algunos restaurantes, consistente en sólo asignar
mesas pequeñas a clientes que llegan solos o en pareja parece adecuada en general.
Sin embargo, no siempre es la mejor. Considere el caso cuando todas las mesas peque-
ñas están ocupadas y no se deja entrar a una persona sola, siendo que hay varias
mesas grandes libres. ¿Cuál sería el tiempo mínimo que debería esperarse antes de
dejarlo pasar? ¿Qué pasa si se le permite pasar e inmediatamente después llega un
grupo de varias personas? Diseñe diversas políticas de asignación de mesas para el
ejemplo anterior y compárelas con lo que el texto menciona en la sección de manejo
de memoria y del procesador, suponiendo que las mesas son las áreas de memoria,
los clientes son los procesos y el encargado de las admisiones es el scheduler.
16. El señor K hace una reservación para un vuelo en una oficina de una aerolínea en
su ciudad. Él cree que la terminal en la que se teclearon sus datos es la computado-
ra, pero en realidad no es sino la terminal de video. En muchos casos, la compu-
tadora (o el servidor de la red) no está en la misma ciudad y a veces ni siquiera en
el mismo país. Describa, lo más estructuradamente posible (empleando el modelo
de sistemas operativos descrito), los pasos necesarios para que la computadora
remota almacene los datos del señor K.
17. En la maravillosa película 2001: Odisea del espacio, de Stanley Kubrick (1928-1999),
filmada en 1968, la inteligente, sensible
(y asesina) computadora HAL 9000
dice: "comencé a funcionar en la planta
H.A.L. en Urbana, Illinois, el 12 de ene-
ro de 1992". (Aunque en la novela origi-
nal del mismo nombre, el escritor Arthur
C. Clarke era un poco más cauto y lle-
vaba el año a 1997.) ¿A qué se debe que
varios años después de esa fecha los al-
cances de la inteligencia artificial estén
2001: Odisea del espacio tan lejanos de lo allí expuesto?
Palabras y conceptos clave 255

PALABRAS Y CONCEPTOS CLAVE

En esta sección se agrupan las palabras y conceptos de importancia estudiados


en el capítulo, para que el lector pueda hacer una autoevaluación, consistente en ser
capaz de describir con cierta precisión el significado de cada término y no sen-
tirse satisfecho sino hasta haberlo logrado. Se muestran en el orden en que se
escribieron o se mencionaron.

PROGRAMA DE APLICACIÓN
PROGRAMA DEL SISTEMA '
SOFTWARE DE BASE
DIRECCIONAMIENTO INMEDIATO
DIRECCIONAMIENTO DIRECTO
DIRECCIONAMIENTO INDIRECTO
DIRECCIONAMIENTO INDIZADO
TRADUCTOR ENSAMBLADOR
DIRECCIÓN ABSOLUTA
REFERENCIA SIMBÓLICA
VARIABLE
TABLA DE SÍMBOLOS
PSEUDOINSTRUCCIONES
ETIQUETA
ENSAMBLADOR
MACROPROCESAMIENTO
MACROS
PARÁMETROS
MACROENSAMBLADOR
CARGADOR
BOOTSTRAP
IPL
ESPACIO DE DIRECCIONES
MÓDULO ABSOLUTO DE CARGA
COMPILADOR
ESTRUCTURA DE UN LENGUAJE
ANÁLISIS LÉXICO
TOKEN
ANÁLISIS SINTÁCTICO
ANÁLISIS SEMÁNTICO
GRAMÁTICA
GENERACIÓN DE CÓDIGO
OPTIMIZACIÓN
INTÉRPRETE
COMPILADOR DE COMPILADORES
PORTABILIDAD
SISTEMA OPERATIVO
KERNEL
SYSTEM CALL
PROGRAMA (CONJUNTO DE INSTRUCCIONES)
PROCESO (CONJUNTO DE ACCIONES)
PROCESADOR
CONCURRENCIA
SIMULTANEIDAD
MULTIPLEXACIÓN EN TIEMPO
OPERACIÓN PRIVILEGIADA
DESPACHADOR
256 Capítulo 5 Software de base

PSW
ESTADOS DE UN PROCESO
MULTIPROGRAMACIÓN
PARTICIONES
FRAGMENTACIÓN
RELOCALIZACIÓN
SWAPPING
PAGINACIÓN
MEMORIA VIRTUAL
MMU
SEGMENTACIÓN
SCHEDULER
THREAD
MULTITASKING
SPOOL
SISTEMA DE ARCHIVOS
LENGUAJE DE CONTROL
GUI
EDITOR
HOJA DE CÁLCULO
BASE DE DATOS
DBMS
ESQUEMA DE LA BASE DE DATOS
QUERY LANGUAGE
MODELOS DE DATOS
NORMALIZACIÓN
FORMAS NORMALES
INTELIGENCIA ARTIFICIAL
SISTEMA EXPERTO
DESENSAMBLADOR

REFERENCIAS PARA
En términos generales (excepto para los artículos) estas referencias son de nivel avan-
EL CAPÍTULO 5
zado, porque el software de base es un área de estudio especializada. Algunos de estos
libros son propios para estudios de posgrado en computación, mientras que otros se
emplean generalmente en las licenciaturas en computación o ingeniería electrónica.

[AHOS90] Aho, Alfred, Ravi Sethi y Jeffrey Ullman, Compiladores: Principios, técni-
cas y herramientas, Addison-Wesley, México, 1990.
Traducción de la segunda versión de un muy completo libro de com-
piladores que incluye las técnicas más importantes de diseño y de gene-
ración de código. Es un texto avanzado, y emplea una buena cantidad de
notación matemática para las explicaciones.

[APPV98) Appleby, Doris y Julius Vandekopple, Lenguajes de programación. Para-


digma y práctica, McGraw-Hill, México, 1998.
Traducción de un excelente —y diferente— libro sobre teoría y prác-
tica de lenguajes. Tiene tres partes principales (además de una de con-
ceptos preliminares sobre tipos de datos y abstracción): lenguajes
imperativos (por bloques: Algol, Pascal, Ada, C; para programación orien-
tada a objetos: C++, Smalltalk, Java; y para procesamiento en paralelo),
lenguajes formales (gramáticas y lenguajes), y lenguajes declarativos (pro-
gramación lógica: Prolog; programación funcional o aplicativa: LISP, APL,
ML; y lenguajes para bases de datos: SQL). Es un libro serio, ameno y
completo, aunque la traducción tiene los consabidos errores que los lec-
Referencias para el capítulo 5 257

tores ya no deberían aceptar ("librería" en lugar de biblioteca, "soportar"


en lugar de manejar y otros por el estilo).
[ARTI90] "Artificial Intelligence: A Debate", en Scientific American, enero, 1990.
Dos artículos que analizan el concepto de inteligencia e intentan
responder la cuestión de si podrá haber computadoras que emulen este
comportamiento humano. El primer artículo concluye que simulación no
es sinónimo de duplicación, y por ende la computadora no puede ni po-
drá crear pensamiento. El otro explica que el cerebro humano no tiene
porqué ser el único sistema físico capaz de resolver tareas cognoscitivas.
[BECL88] Beck, Leland, Software de sistemas: Introducción a la programación de
sistemas, Addison-Wesley, México, 1988.
Traducción de un buen libro dedicado a la programación de siste-
mas. Trata los temas en un nivel introductorio, pero tiene la ventaja de
que trabaja sobre una computadora "de papel" diseñada ex-profeso para
propósitos académicos, y los ejemplos del texto usan ese lenguaje de
máquina. También tiene algunas descripciones de sistemas reales. Las
nuevas ediciones del original ya no se han traducido al español, por la
baja demanda de este tipo de cursos en nuestros países, tan provecho-
samente consumidores de paquetes y programas predigeridos.
[BERH80] Berliner, Hans, "Computer Backgammon", en Scientific American, junio,
1980.
El autor del programa que le ganó al campeón mundial de backgam-
mon explica en este artículo de divulgación sus puntos de vista sobre la
inteligencia artificial y la forma en que las computadoras manejan juegos
de estrategia. En un número anterior de la misma revista se dedicó un
artículo a un programa para jugar póquer.
[DEIH93] Deitel, Harvey, Introducción a los sistemas operativos, segunda edición,
Addison-Wesley, México, 1993.
Traducción de un exitoso libro sobre sistemas operativos. De gran
volumen (930 págs.), describe el funcionamiento de cada parte de un
sistema operativo en un nivel conceptual, apto para un primer curso se-
rio de nivel universitario. Se dedican varios capítulos a explicar las carac-
terísticas conceptuales de algunos sistemas operativos comerciales.
[DEWA84] Dewdney, A. K., "Computer Recreations", en Scientific American, julio, 1984.
Artículo que explica algunos de los procesos mediante los cuales
una computadora puede jugar "damas", y aprovecha para hacer consi-
deraciones sobre el futuro de la inteligencia artificial. Esta sección de
la revista a cargo del Sr. Dewdney se dedica a presentar mes con mes
pasatiempos y consideraciones de muchísimo interés sobre la teoría de
la computación y las computadoras.
[DONJ72] Donovan, John, Systems Programming, McGraw-Hill International, Tokio,
1972.
Venerable libro especializado en la programación de sistemas, enfo-
cado al funcionamiento de la serie IBM 360. Durante los años iniciales
fue prácticamente el único libro sobre programación de sistemas en ge-
neral, y se cita aquí más bien por motivos sentimentales. Existe traduc-
ción al español.
[H0LJ92] Holland, John, "Genetic Algorithms", en Scientific American, julio, 1992.
Artículo que intenta explicar la filosofía y uso de un tipo especial de
métodos para inteligencia artificial que llegan a una solución óptima lue-
go de repetidos intentos similares, en donde cada uno efectúa una ligera
variante sobre el anterior, en forma que —dice el autor— emula los proce-
sos de selección evolutiva. La misma revista contiene otro artículo sobre
el tema, "Survival of the Fittest Bits" (supervivencia de los bits más
aptos), con un ejemplo concreto.
258 Capítulo 5 Software de base

[HSUF90] Hsu, Feng, et al., "A Grandmaster Chess Machine", en Scientific American,
octubre, 1990.
Artículo de los cuatro creadores de la computadora "Deep Thought"
(Pensamiento profundo, título que ironiza con el de la primera película
pornográfica comercial), que en 1988 derrotó a un gran maestro de aje-
drez. Los autores explican el desarrollo de algoritmos de búsqueda para
el juego de ajedrez, que se ejecutan en hardware especializado con capa-
cidad de examinar un millón de posiciones por segundo. Cuando termi-
nen de desarrollar la siguiente versión, capaz de analizar mil millones de
posiciones en un segundo, esperan "ser un fuerte desafío para el cam-
peón mundial".

[KERB76] Kernighan, Brian y P. J. Plauger, Software Tools, Addison-Wesley,


Massachusetts, 1976.
Los autores diseñaron el lenguaje de programación C y el sistema
operativo Unix, por lo que este libro debe ser tratado con mucho respeto,
no obstante no ser de actualidad. Aquí es donde proponen su filosofía
computacional, consistente en diseñar herramientas de programación
para auxiliarse en la construcción de sistemas complejos. Posteriormen-
te (pero antes de las computadoras personales) escribieron una versión
para Pascal, titulada —claro— Software Tools in Pascal.

[KNUD97] Knuth, Donald, The Art of Computer Programming, Vol. 1: Fundamental


Algorithms, tercera edición, Addison-Wesley, Massachusetts, 1997.
Reedición del primer volumen de la gran enciclopedia que Knuth co-
menzara a escribir en 1968. Inicia con más de cien páginas de "prepara-
ción preliminar de matemáticas" (inducción matemática, teoría de números
y análisis de algoritmos) y trata luego los conceptos básicos de la progra-
mación en lenguaje de máquina y las principales estructuras de datos
(listas, pilas, árboles y estructuras dinámicas en memoria). Se sigue consi-
derando a estos libros como verdaderos clásicos de la literatura compu-
tacional, tanto por su calidad y profundidad matemática como por los
ejercicios propuestos.
Existe traducción al español del primer volumen.
Hasta 1998 sólo habían aparecido los tres primeros tomos de la
proyectada serie de siete (el cuarto está en prensa), porque Knuth "se
distrajo" escribiendo los seis libros no previstos sobre tipografía mate-
mática: Computers and Typesetting. En 1974 produjo la "noveleta matemá-
tica" Números surreales, traducida por Editorial Reverté, España, 1979,
en donde narra "cómo dos ex estudiantes redescubren las matemáticas
puras y encuentran la felicidad total". En 1989 publicó el libro de texto
Concrete Mathematics junto con Ronald Graham y Oren Patashnik.
En otros capítulos de este libro hay más citas a obras de Knuth.

[LEMK96] Lemone, Karen, Fundamentos de compiladores. Cómo traducir al lengua-


je de computadora, CECSA, México, 1996.
Traducción de un conciso libro sobre compiladores, escrito en 1991.
La autora explica que el proyecto considera dos libros, y éste se dedica
más a la comprensión de los principios de operación que a su diseño o
construcción. Tiene un buen número de ejercicios resueltos.

[LISE93] Lister, M. A. y R. Eager, Fundamentals of Operating Systems,


Springer-Verlag, Nueva York, 1993.
Quinta edición de una excelente descripción de un sistema operati-
vo "de papel", tratado en nivel intermedio. Incluye prácticamente todos
los temas de estudio en forma concisa y clara. En el prefacio a la nueva
edición el autor dice: "Han pasado veinte años desde la primera edición
de este libro. Mucho ha cambiado, pero mucho se mantiene igual". Se
recomienda para un primer curso de este tema.
Referencias para el capítulo 5 259

[MACA95] Maccabe, Arthur, Sistemas computacionales: Arquitectura y organización,


McGraw-Hill, Colombia, 1995.
Traducción de un libro serio y bastante completo sobre organización
de computadoras, con un fuerte enfoque en los aspectos detallados del
diseño y uso del lenguaje de máquina y el lenguaje ensamblador. Dedica,
por ejemplo, casi cien páginas a los modos de direccionamiento, y dos
capítulos a la representación interna de números en la memoria. Ade-
más, tiene capítulos completos sobre la programación de sistemas (en-
sambladores y cargadores en uno, y monitores en otro); por último, trata
las máquinas paralelas.
[ MADS74] Madnick, Stuart y John Donovan, Operating Systems, McGraw-Hill, Nueva
York, 1974.
Se puede considerar a este libro-reliquia como uno de los pioneros
en los textos sobre sistemas operativos. Está tratado desde una perspec-
tiva entre lo descriptivo y lo operativo, y varios de sus capítulos siguen
siendo vigentes. Contiene un ejemplo completo de un núcleo codificado
en ensamblador IBM 370.
Existe traducción al español.
[OKTH96] Oktaba, Hanna, "Lenguajes de programación. ¿Por qué hay tantos y apa-
recen nuevos", en Soluciones Avanzadas, México, Núm. 39, noviembre,
1996.
Corta y accesible reseña sobre los diversos tipos de lenguajes (im-
perativos, funcionales, lógicos, orientados a objetos, y concurrentes y
paralelos) aparecida en una de las pocas revistas mexicanas dedicadas
a la computación desde una perspectiva más técnica y académica que
comercial o publicitaria. La doctora Oktaba fue coordinadora de la maes-
tría en computación impartida en el Instituto de Investigaciones en Mate-
máticas Aplicadas y en Sistemas (IIMAS) de la UNAM.
[PAVF181] Pavelle, Richard, M. Rothstein y J. Fitcher. "Computer Algebra", en
Scientific American, diciembre, 1981.
Artículo sobre los sistemas computacionales para manejo algebraico.
Tiene varios ejemplos de las transformaciones y simplificaciones logra-
das mediante la manipulación algebraica por computadora y muestra al-
gunos de los métodos con los que funciona.
Existe traducción al español.
[PRAZ97] Pratt, Terrence y Marvin Zelkowitz, Lenguajes de programación, tercera
edición, Prentice Hall, México, 1997.
Traducción de una nueva edición de un texto sobre conceptos de
lenguajes, desde una perspectiva moderna y enfocada desde las
metodologías ("paradigmas") de la programación: lenguajes imperativos
(casi todos los comunes), aplicativos (LISP, ML), basados en reglas (Prolog)
y orientados a objetos (aunque no menciona Eiffel ni considera al enton-
ces muy reciente Java).
[SCIA71] Scientific American, Computers and Computation, W. H. Freeman, San
Francisco, 1971.
Aunque este libro definitivamente no toca temas de moda ni de ac-
tualidad, es una excelente compilación de artículos de interés general
sobre computación publicados por esta prestigiada revista en los años
70. Incluye artículos considerados como clásicos, cuya vigencia actual
sigue siendo evidente.
Existe traducción al español.
[SEBR99] Sebesta, Robert, Concepts of Programming Languages, cuarta edición,
Addison-Wesley, Massachusetts, 1999.
Excelente y amplio texto en donde se explican prácticamente todos
los conceptos de la programación moderna desde la perspectiva de los
260 Capítulo 5 Software de base

principales lenguajes. Junto con los temas tradicionales, dedica capítu-


los a conceptos de tipos abstractos de datos, programación por objetos,
concurrencia y manejo de errores. Además de la gran cantidad de cono-
cimientos técnicos que contiene, ofrece múltiples referencias de valor
histórico e incluye las fotografías de los principales diseñadores de len-
guajes, lo cual lo vuelve aún más interesante.
[SETR92] Sethi, Ravi, Lenguajes de programación, Addison-Wesley, México, 1992.
Traducción de un libro avanzado sobre "conceptos y constructores"
de lenguajes de programación. En una primera sección presenta el ma-
nejo de los conceptos principales, para examinar a continuación las carac-
terísticas de varios lenguajes: imperativos, por objetos, funcionales y lógicos.
En la tercera y última parte escoge como vehículo expresivo un dialecto de
LISP para describir lós lenguajes desde una perspectiva formal.
[SILG99] Silberschatz, Abraham, y Peter Galvin, Sistemas operativos, quinta edi-
ción, Pearson, México, 1999.
Traducción de la nueva edición de un excelente libro de 891 páginas
sobre sistemas operativos que describe, con un adecuado nivel de deta-
lle, múltiples conceptos tanto teóricos como prácticos, e incluye ejem-
plos específicos de cada tema, particularmente referidos a Unix, Linux y
Windows. Resulta una sencilla, magnífica y amplia fuente de estudio glo-
bal. Muy recomendable.
[SILK98] Silberschatz, Abraham, Henry Korth y S. Sudarshan, Fundamentos de
bases de datos, tercera edición, McGraw-Hill, Madrid, 1998.
Traducción de un excelente y amplio (640 pp.) texto moderno de ba-
ses de datos. Explica con todo detalle la filosofía y fincionamiento de los
modelos de datos de entidad-relación, relacional, de red y jerárquico,
para pasar luego al diseño de bases de datos relacionales. Tiene capítu-
los completos sobre manejo de archivos e índices, y sobre bases de datos
distribuidas. Además de todo eso, incluye fragmentos de programas y de-
mostraciones matemáticas.
[STAW97] Stallings, William, Sistemas operativos, segunda edición, Prentice-Hall,
México, 1997.
Traducción de un excelente y moderno texto sobre sistemas opera-
tivos, de un prolífico autor de libros sobre redes de computadoras. En
catorce capítulos explora con detenimiento los principales conceptos de
control y manejo de procesos, concurrencia, manejo de memoria, me-
moria virtual, manejo del procesador, multiprocesamiento, manejo de
entradas y salidas, manejo de archivos, procesamiento en redes, proce-
samiento distribuido, y seguridad. Muestra ejemplos reales de sistemas
operativos comerciales y ofrece además una amplia fundamentación for-
mal y matemática.
[TANW98] Tanenbaum, Andrew y Albert Woodhull, Sistemas operativos: diseño e
implementación, segunda edición, Prentice-Hall, México, 1998.
Traducción de un nuevo y amplio libro sobre sistemas operativos,
que trata con detalle los temas fundamentales en más de 900 páginas.
Reutiliza parte de materiales publicados por el primer autor en libros ante-
riores sobre el tema, y contiene el listado completo de un sistema opera-
tivo de fines didácticos escrito en lenguaje C y basado en la filosofía de
Unix. Aunque el sistema se llama MINIX (porque es una especie de Unix
en miniatura, del cual posteriormente se derivó Linux), ocupa más de
250 páginas en un apéndice del libro y viene incluido en un CD-ROM.
De Tanenbaum también se tradujo el libro Sistemas operativos dis-
tribuidos, Prentice-Hall, México, 1996, y el amplio volumen sobre redes,
comentado en otro capítulo.
[TEUT95] Teufel, Bernard, S. Schmidt y T. Teufel, Compiladores, conceptos funda-
mentales, Addison-Wesley, México, 1995.
Referencias para el capítulo 5 261

Traducción de un conciso pero completo libro sobre compiladores,


su teoría y su diseño. En tan sólo 170 páginas, los autores logran un
excelente compendio del extenso tema, pero no se quedan sólo en gene-
ralidades vagas, pues incluyen secciones completas de un compilador
escritas tanto en pseudocódigo como en Pascal.
[THBW85] Thompson, Beverly y William Thompson, "Inside an Expert System", en
Byte, abril, 1985.
Muy interesante artículo que muestra, con cierto detalle técnico, el
funcionamiento de una máquina de inferencias para procesar información
con base en el significado y aplicación de reglas predefinidas que forman
parte de una base de conocimientos. Se muestran algunos ejemplos de
componentes de los programas que constituyen un sistema experto.
[ULLJ76] Ullman, Jeffrey, Fundamenta. I Concepts of Programming Systems,
Addison-Wesley, Massachusetts, 1976.
Excelente revisión de la programación de sistemas (no incluye los
sistemas operativos), por uno de los más autorizados y prolíficos auto-
res de la literatura computacional de primer nivel. No obstante tratarse
de un libro "viejo", debe considerarse como uno de los pilares del tema.
[ULLJ82] Ullman, Jeffrey, Principies of Database Systems, Computer Science Press,
Maryland, 1982.
Libro de teoría matemática de bases de datos. A diferencia de casi
todos los demás textos sobre bases de datos (tanto de esa fecha como
los actuales), éste se dedica a la fundamentación formal de las propie-
dades de los diferentes modelos (jerárquico, de red, relacional), y hace
uso de teoremas y demostraciones, además de tratar con rigor temas
diversos, como las bases de datos distribuidas.
[ULLW99] Ullman, Jeffrey y Jennifer Widom, Introducción a los sistemas de base de
datos, Pearson, México, 1999.
Traducción de un libro reciente sobre diseño de bases de datos
relacionales y lenguaje SQL. En 470 páginas de un nivel intermedio-avan-
zado, los autores explican el proceso de creación de aplicaciones de ba-
ses de datos y tratan temas de álgebra relacional, la interfaz entre SQL y
un lenguaje anfitrión y los lenguajes de consulta orientados a objetos.
[WALP91] Wallich, Paul, "Silicon Babies", en Scientific American, diciembre, 1991.
Artículo de difusión sobre los principales proyectos de inteligencia
artificial hasta el momento: robots no industriales, representación del
conocimiento, "insectoides" y otros.
Existe traducción al español.
[WINP94] Winston, Patrick, inteligencia artificial, tercera edición, Addison-Wesley,
México, 1994.
Traducción del libro considerado como estándar en el campo de la
inteligencia artificial. El autor también es ampliamente conocido por sus
trabajos con el lenguaje de programación LISP (entre los que se cuenta
un excelente libro sobre el tema), y en esta edición intenta cubrir lo nue-
vo sobre visión y principios de adquisición de conocimiento. El prefacio
inicia diciendo: "El campo de la inteligencia artificial ha cambiado enor-
memente desde la primera edición de este libro", y el lector debe tomar
en cuenta que se refiere tan sólo a un período de menos de quince años.
[WINT84] Winograd, Terry, "Computer Software for Working with Language", en
Scientific American, septiembre, 1984.
En este artículo se muestra cómo una computadora puede explorar
el significado de una frase de lenguaje natural, entrando a los temas de
procesamiento de textos, análisis sintáctico y semántico y problemas
asociados. El artículo concluye con la observación de que "el software
para simular el entendimiento completo del lenguaje simplemente no
está en consideración". Existe traducción al español.
Teoría
matemática de
la computación

Alan M. Turing (1912-1954)

Temas y áreas de conocimiento del capítulo


Área general de conocimientos del capítulo:
2.4 Teoría matemática de la computación
Sección 6.1 Introducción (subárea 2.4.2, MA31)
Sección 6.2 El concepto de algoritmo: la máquina de Turing (ídem,
y subárea 2.4.3, MA33)
Sección 6.3 Lenguajes formales y autómatas (subárea 2.4.1,
MA28-30)
Sección 6.4 Anexo: Visión histórica de la lógica matemática
(subárea 2.3.1, MA21)
Sección 6.5 Anexo: Visión de un libro fundamental, la obra de
George Boole (subárea 3.2.1, AC12)
Sección 6.6 Anexo: Elementos de lógica proposicional (subárea
2.3.1, MA21)
264 Capítulo 6 Teoría matemática de la computación

6.i INTRODUCCIÓN

Dado que va a pasar no sé qué


no sé cuándo, ¿qué medidas ha-
brá que tomar?

Jean Tardieu
"Problemas y trabajos prácticos",
en la revista El Cuento, núm. 13,
México, junio de 1965.

La idea central de la computabilidad (término matemático para nombrar los estudios


sobre teoría de la computación) parte de las ideas filosóficas expuestas al inicio del capítulo
1, y consiste en encontrar la representación adecuada para la descripción de un problema
o de un fenómeno, y ahora se dedicará todo un capítulo a ello.
Es evidente que siempre será posible describir algún aspecto de la realidad por medio
de cierto lenguaje: basta con encontrar las combinaciones adecuadas de símbolos para
representar lo que se tiene en mente. El concepto de símbolo es, pues, fundamental en
cuanto a la habilidad para describir que por excelencia tenemos los humanos. Buena parte
de nuestros procesos mentales y psicológicos se reducen a descripciones que hacemos
respecto de algún aspecto de la realidad que nos rodea, para lo cual se requieren asocia-
ciones entre conceptos y elementos del lenguaje (símbolos) en su sentido más amplio. Es
decir, utilizando nuestra maravillosa capacidad de armar mundos mediante palabras, crea-
mos descripciones que posteriormente re-crearemos, en otro momento y en otro lugar.
Pero una vez resuelto, aunque sea en principio, el problema de poder describir el mun-
do, hay que definir la manera de confirmar si la descripción es completa: ¿cómo asegurar
que una descripción pueda ser reproducida por un tercero, para que éste llegue al mismo
lugar de partida?
Este es otro problema: representar el fenómeno descrito. La comunicación efectiva
tiene lugar cuando se describe un problema o fenómeno determinado ante un receptor, y éste
representa la descripción que se emitió y vuelve al objeto original. Si se logra cerrar el ciclo
descripción-representación, entonces se está hablando de un conocimiento transmisible.

Figura 43 ¿Todas las


descripciones son
representables?

Tal vez este análisis parezca extraño para quien esté acostumbrado a pensar en una
computadora como una gran y veloz calculadora. Un poco más adelante se verá cómo
una máquina de esta clase es en realidad un modelo general, que permite cerrar el círculo
arriba enunciado y puede por ende perfectamente simular una calculadora (o cualquier
Sección 6.1 Introducción 265

otra cosa). Lo importante ahora es comprender que la computadora es mucho más que una
calculadora compleja, pues está basada en una idea matemática mucho más potente, la del
modelado.
Un modelo es una especificación, generalmente en términos de un lenguaje matemá-
tico, de los pasos necesarios para reproducir, aquí y ahora, un subconjunto determinado E l concepto
de la realidad descrito previamente. Es más, un modelo parte siempre de la descripción de de modelo
lo que se representará.
Surge una pregunta: ¿Todo aquello que es descriptible será representable? Es decir,
¿se podrá siempre pasar de la descripción de un proceso a su representación? Intuitivamente,
parece que sí; sólo hay que asociar acciones a símbolos de la descripción y se estará
"actuando" la descripción dada. Pero —nos preguntamos—, ¿esta representación simula-
rá completamente lo que fue descrito? En apariencia, la exactitud de la simulación de-
pende de la exactitud de la descripción: cuanto más adecuada sea la descripción del proceso,
tanto mejor será el resultado que emula lo real.
Descubriremos más adelante que esta idea es incorrecta; esto es, existen ciertos proce-
sos que pueden ser descritos con un grado ilimitado de precisión, pero cuya representa-
ción fracasa; no llega de regreso al punto de partida.
Tal vez si se replantea el problema se alcance una mejor comprensión. Supóngase que
se fabrica un aparato para producir descripciones en términos de cadenas de símbolos.
Supóngase además la existencia de una máquina que se comporta de la siguiente manera:
dada una descripción cualquiera, la analiza durante un tiempo finito y después emite su
dictamen, que consistirá en un sí o un no. El sí querrá decir que lo que se describe es
representable, y el no indicará lo contrario. Es decir, la respuesta positiva significa, por
ejemplo, que el problema que está siendo actuado tiene una solución efectiva (y por tanto
luego se podrá calcular o encontrar), mientras que la respuesta negativa implica que el
problema no tiene representación, lo cual significa que la solución no existe.
Un problema de esta clase es, por ejemplo: dado un número, extraer su raíz cuadrada.
El aparato descriptor produce una cadena de símbolos que, una vez en nuestra máquina, El problema
provoca que ésta dictamine si la descripción es o no representable. De esta forma, la má- de la decisión
quina funciona como un procedimiento de decisión: decide, en un tiempo finito, si la
descripción es representable o no y, por tanto, si el problema asociado con ella tiene o no
solución.
Ahora viene una pregunta mucho más comprometedora, ¿existe una máquina así? (el
hecho de que se pueda idear no significa que exista). Nos estamos acercando ya al problema
central de la teoría de la computabilidad: encontrar formas de representar descripciones
de procesos, de manera tal que siempre se pueda decir sí (existe), o no.
Se dice que un problema es computable cuando existe una de estas máquinas de
decisión para él. Entonces la pregunta podría plantearse como sigue: ¿todos los procesos
son computables?
La respuesta, asombrosamente, es no.
Para demostrarlo, habría que encontrar al menos un proceso así. O sea, encontrar una
descripción tal que cuando sea alimentada a la máquina de la decidibilidad, la ponga en un
estado curioso: no dice sí, pero tampoco dice no. O sea, ¡no puede decidir!
Este resultado, que parece contradecirse con la intuición, se debe al matemático in-
glés Alan Mathison Turing, quien en el proceso de encontrar esa máquina descubrió pro- Li m ita ntes
piedades insospechadas acerca del mundo de los conceptos lógico-matemáticos. en el sistema de
Como resultado de las investigaciones de Turing ahora se conocen algunas limitantes, pensamiento formal
inherentes a nuestro sistema de pensamiento, de los mecanismos mentales para averiguar
la estructura formal del mundo. Estos estudios inauguran la teoría matemática de la compu-
tación, de la cual las computadoras son tan sólo un aspecto, el más visible sin duda.
Pero volvamos a nuestro tema de estudio, porque en realidad comenzamos a hablar
de los algoritmos. Un algoritmo es un método formal y sistemático de representar la des-
266 Capítulo 6 Teoría matemática de la computación

cripción de un proceso. Además, dijimos que no todos los procesos terminan, es decir, no
para todos los casos se puede decidir si cierta descripción es representable o no, por lo que
conviene explorar estas ideas con mayor detenimiento.
Los algoritmos son la forma de operar de las descripciones, y la palabra se deriva del
término árabe al-hvárizmi, sobrenombre del matemático Mohámed ben Musa (c. 780),
que define un "conjunto ordenado y finito de operaciones que permite hallar la solución
de un problema", según la definición del diccionario.
A partir de esta sección se comenzará a trabajar con el concepto de algoritmo en
términos formales, aunque la creación y uso de algoritmos quedará para los posteriores
capítulos de programación.

6.2 EL CONCEPTO DE ALGORITMO:


LA MÁQUINA DE TURING

En su estudio, Turing propone una forma de representar un proceso a partir de su descrip-


ción. El modelo matemático propuesto (conocido ahora como máquina de Turingt) es
una especificación formal de los pasos primitivos necesarios para representar una des-
cripción en términos totalmente explícitos y claros, sin hacer la menor referencia a con-
ceptos u operaciones ambiguos o sobreentendidos.
Como veremos, mediante un conjunto de funciones matemáticas simples se indica el
comportamiento completo del proceso que está siendo representado, mostrando detalla-
damente los estados por los que se atraviesa para lograrlo. Se parte de un estado inicial y se
recorre un conjunto de estados intermedios hasta llegar al estado final, que marca entonces
el final de la computación. Desde otro punto de vista, mediante la especificación de una
máquina de Turing se define (en forma constructiva o construccionista) lo que un proceso
es en realidad, y eso también tiene implicaciones filosóficas que luego aclararemos.
Su definición formal consta de los siguientes elementos:
- Una cinta de longitud infinita, dividida en celdas.
(Cada celda puede contener un símbolo)
- Un diccionario de símbolos predefinido.
(De aquí se toman los símbolos para las celdas)
– Un control finito, que tiene la capacidad de examinar algún símbolo de alguna
celda y tomar una decisión.
(La decisión depende del símbolo observado y del estado en que se encuentre el
control finito en ese momento)

El control se llama finito precisamente porque puede, para un momento determinado,


estar en uno de varios estados posibles, habiendo tan sólo un número finito de ellos. Se puede
pensar en un estado como una configuración de una máquina digital que, según se dijo en
el capítulo 1, tiene la capacidad de adoptar un estado u otro pero ninguno intermedio.
Si se supone un diccionario de símbolos [s 1 , s2, sn], podemos pensar en codificar
un proceso (describirlo) por medio de ellos, para luego escribir estos símbolos —uno a
uno— en celdas de la cinta de la máquina de Turing, que entonces se vería así:

(t) Esta "máquina" no debe confundirse con un aparato físico o mecánico. Se trata de una construc-
ción matemática, y no tiene nada que ver con motores, transistores o dispositivos de ningún tipo.
Para un estudio de estos temas, en general avanzados, véase el artículo [HOPJ84] y las refe-
rencias [BROG93] y [HOPU93].
Sección 6.2 El concepto de algoritmo: la máquina de Turing 267

S1 So S9 So S76 S7 S7 S9 S 12 S44 —•

Control
finito Figura 44 Máquina
U U de Turing

En esta figura, el control finito de la máquina de Turing apunta a la primera celda de


la cinta; esto es, observa el primero de los símbolos que describe el proceso a representar,
a la vez que adopta un cierto estado, conocido como estado inicial.
Ahora la máquina podrá reaccionar en formas preestablecidas ante estímulos recibi-
dos del mundo exterior (que, para la máquina, estará representado por la cinta).
Definiremos un estímulo como la conjunción de dos sucesos:

A) Que el control finito se encuentre en cierto estado e l justo cuando


B) en la celda que está siendo observada hay un símbolo s 1 .

Ante este estímulo la máquina tendrá una reacción, que consistirá en tres acciones:
Modo de
1) Pasará a un nuevo estado (aunque este nuevo estado puede perfectamente ser igual funcionamiento
al que tenía antes, lo que es equivalente a que no cambie); de una máquina
2) escribirá un nuevo símbolo en el lugar del recién leído (que también puede ser el
de Turing
mismo que antes) y,
3) moverá el control finito una celda a la derecha (D) o una celda a la izquierda (I).

Ahora es posible escribir, para una máquina en particular, un conjunto de reacciones


que sucederán cuando en la cinta se presente un conjunto de estímulos:

ESTÍMULOS REACCIONES
Estado Símbolo Estado Símbolo Movimiento
actual actual nuevo nuevo
e 15 s38 e 21 S 42 I
e 21 s42 e 21 S
II
D
e09 S I4 e 42 S
I4
D
eo9 s 13 e 15 5 82 I

En esta tabla la primera entrada se lee: "si cuando la máquina se encuentra en el estado
15 el control se halla observando el símbolo 38, pasará entonces al estado 21, escribirá en
esa misma celda el símbolo 42, y se moverá una celda a la izquierda".
De manera similar se entienden las demás entradas de la tabla. Obsérvese que en el
segundo renglón la máquina no cambia de estado (porque, encontrándose en el estado 21,
pasa al estado 21, o sea, al mismo). Igualmente, en el tercer renglón la máquina deja el
símbolo observado sin modificar, pues vuelve a escribir el que ya estaba.
Con un poco de imaginación podemos darnos cuenta de que es posible "programar"
la máquina para que se comporte de tal forma que logre algún fin previsto. Tal fin no será
otro más que, precisamente, representar (darle vida o actuar) la descripción de un proceso
previamente codificado en la cinta.
268 Capítulo 6 Teoría matemática de la computación

El resultado dejado por la máquina al término de su actuación será un conjunto de


celdas de la cinta con la codificación de la solución encontrada. Por último, es necesario
definir cierto estado (o varios de ellos) como estado final, de modo que, cuando el control
finito entre en él (o en alguno de ellos), se detenga, dando así por terminada la computación
del proceso.
Desde una perspectiva más formal, los estímulos y reacciones de la máquina de Turing
La máquina de Turing en realidad no son sino los conjuntos que forman el dominio y el codominio de una función.
como conjunto Recuérdese que una función es un caso particular de relación entre dos conjuntos, en
de funciones la cual a elementos del primero (llamado dominio) se asocian elementos únicos del se-
gundo (llamado codominio o dominio inverso).
La particularidad de las funciones en matemáticas consiste en que todo elemento del
dominio tendrá siempre asociado un, y sólo un, elemento del codominio, con lo cual se
garantiza que no habrá sorpresas ni casos inesperados de asociaciones que en un momen-
to lleven a un estado y en otro momento lleven a algún estado diferente, pues sólo puede
existir uno de ellos como destino.
Es decir, las funciones son "bien portadas", en el sentido de que están definidas en
forma precisa, predecible e inmutable, y los resultados generados no están sujetos a variaciones
estadísticas o a indeterminismos. Por ejemplo, la función cuadrática es una relación entre el
conjunto de números enteros con signo (dominio) y el conjunto de los enteros positivos (co-
dominio), en donde se establece una correspondencia de dos a uno (excepto para el cero,
en donde es de uno a uno o bien se considera como su propio negativo), pues el cuadrado de
un número entero negativo o positivo es el mismo, como se puede apreciar a continuación.

Figura 45 Ejemplo de la
función f(x) = x2 DOMINIO CODOMINIO

Vista así, la tabla anterior de la máquina de Turing puede también representarse me-
diante la siguiente función entre el dominio (formado por pares de elementos: estado, sím-
bolo) y el codominio (formado por tercios de elementos: estado, símbolo, movimiento):
f(e 15, s38) (e21, S42' I)
f(e21 , s42) —> (e21 , s 11 , D)
f(e39, s14) (e42, s14, D)
f(e09, s 13) (e15, s82, I)
O sea, está definida por la función
f(estado actual, símbolo actual) —> (estado nuevo, símbolo nuevo, mov.)
Sección 6.2 El concepto de algoritmo: la máquina de Turing 269

Un primer ejemplo: la suma


Como primer caso de modelo, construiremos una máquina de Turing para simular el proceso
de la suma de dos números enteros, con lo cual a la vez se definirá lo que constituye una suma,
porque no existe una forma más primitiva o elemental de describirla. Primero escribiremos
los números empleando la notación más simple posible, para luego mostrar cómo operar
sobre ellos. Usando como ejemplo los números 3 y 4, la notación unaria elemental podría ser
III Y IIII
con lo que luego se tendrá que enseñar en forma explícita cómo unirlos, para entonces
obtener el resultado: 1 1 1 1 1 1 1. El modelo matemático que describe cómo obtener esto
para todos los posibles casos (y no tan sólo para el 1 1 1 y el 1 1 1 1) se llama, precisamente, la
máquina de Turing para sumar (es decir, el algoritmo de la suma), y consiste en el conjun-
to de acciones que la máquina toma cuando detecta cada uno de los símbolos que consti-
tuyen la representación unaria de cada número, de tal forma que logra fusionarlos.
Para ello escribiremos los números en una secuencia de celdas individuales, separán-
dolos mediante algún símbolo especial, como muestra la figura (B es el símbolo para una
celda en blanco):
1 1 1+ B...

Además, habrá que definir un índice, que apunta hacia alguna de las celdas y puede
leer su contenido. Con esta información, y el conocimiento del estado actual, se efectúa una
acción, que consiste en escribir un símbolo en esa celda recién leída, cambiar de estado, y
moverse a la celda contigua a la izquierda o a la derecha.
Estas acciones son el conjunto de funciones que describen el comportamiento de la
máquina de Turing cuando realiza el algoritmo, partiendo del símbolo en la primera celda
y del estado inicial. Es decir, existe una tabla que contiene las funciones, que a su vez
describen los pasos del algoritmo: dependiendo del estado actual, y del símbolo al que
apunte el índice del control finito, se aplica la función correspondiente de entre las conte-
nidas en la tabla, y se continúa así hasta llegar a un estado final.
Para nuestro ejemplo de suma se requieren los siguientes conjuntos:
Símbolos = { 1, +, B }
Estados = { e0, el , e2 } e0 es el estado inicial
e 1 es un estado intermedio
e 2 es el estado final
Y la tabla de funciones es
(ea, 1) —> (ea, 1, D)
(e0, +) —> (e0, I, D)
(e0, B) —> (e l , B, I)
(e 1 , 1) -3 (e2, B, I)

Nuevamente, la primera función se lee como "si el índice apunta a una celda que
contiene el símbolo 1 cuando el estado actual es e0, entonces habrá que pasarse al estado e 0
(esdcir,quanlmso)ecribíl1nsaced(ir,jlom
estaba) y mover el índice una celda hacia la derecha".
Aplicando estas cuatro funciones (que en sí son el algoritmo de la suma) al ejemplo,
y comenzando en el estado inicial con el índice apuntando hacia la primera celda, se
obtiene, luego de once pasos, el resultado:
III II I
B B ..
270 Capítulo 6 Teoría matemática de la computación

porque se tuvo que pasar por todas estas aplicaciones de las funciones contenidas en la
tabla de esta máquina de Turing:

Dominio Codominio
(Estímulos) (Reacciones)
(eo,I ) —> (e0, I , D)
(eo, ) —> (5,1, D)
(eo,I ) —> (ea , I , D)
(eo,I ) —> (eo, I , D)
(eo,+) —> (e0, I , D)
(eo, ) —> (eo, I , D)
(eo,I ) —> (e0 , I , D)
(eo,I ) --> (eo, I , D)
(eo, ) --> (eo, I , D)
(e B) —> (ei , B, I)
(e1, ) —> (e2, B, I)

Cuando se llega al estado final, el índice apunta al último símbolo no blanco.


Naturalmente que a nadie se le ocurriría emplear este método para sumar porque es
muy primitivo, y resulta mucho más cómodo utilizar la notación decimal y el método de
sumar que todos aprendimos en la primaria (o al menos el que se aprendía hasta antes
de la era de las calculadoras baratas); pero no deja de ser interesante reconocer que así es
como los niños suman con los dedos, y es también la base de funcionamiento del ábaco.
¿No indicará esto que realmente así se suma, y que el método usual no es sino un
refinamiento abreviado de lo que en realidad es una suma? Aquí no se hizo referencia a
consideraciones matemáticas ni teóricas y, además, no existe impedimento alguno para
que así sea. Los conceptos de número y de suma son muy simples, como se muestra en el
artículo "The Origins of Number Concepts", de Charles J. Brainerd, publicado en la re-
vista Scientific American, marzo de 1973.
¿Y qué es en realidad un procedimiento para obtener un resultado en matemáticas?
La conclusión no puede ser otra que la siguiente: el procedimiento para efectuar una
operación está definido, en sus términos más elementales, por una máquina de Turing que
llega a un estado final al término de la computación del proceso. Es decir, para cada
procedimiento matemático (o aritmético, o lógico) se puede escribir siempre la especifi-
cación de una máquina de Turing que indica cómo efectuarlo en forma algorítmica.

Otro caso: reconocimiento de cadenas


En el siguiente nuevo ejemplo se describe la tabla de cierta máquina de Turing, que llamare-
mos MT1 ; tiene siete estados (e e6) y maneja seis símbolos (0, 1, X, Y, Z, B)

Dominio Codominio
(Estímulos) (Reacciones)
f(e0, O) —> (ei , X, D)

f(e0, 1) —> (e6, 1, D)


Para esta máquina,
f(ei , 0) —> (e1 , 0, D) los estados finales
son e5 y e6, y el
lel , Y) —> (e1 , Y, D) estado inicial es eo
Sección 6.2 El concepto de algoritmo: la máquina de Turing 271

fie l , 1) —> (e2, Y, I) Originalmente la


cinta contiene tan
f(e2, Y) —> (e2, Y, I) sólo ceros, unos y
blancos (0, 1, B), en
f(e2, X) —> (e3, X, D) alguna configuración
cualquiera, que puede
f(e2,O) —> (e4, 0, I) cambiar de un caso a
otro, o de un ejemplo
f(e3,Y) -4 (e3, Y, D) a otro.

f(e3, B) (e5, Z, I) Las letras X, Y, Z


son símbolos
f(e3 , 1) —> (e6, 1, D) auxiliares empleados
por el modelo para su
f(e4, 0) -4 (e4, 0, I) funcionamiento

f(e4, X) —> (e0, X, D)

Veamos lo que sucede cuando el control finito tiene enfrente una cinta con los siguien-
tes símbolos y se encuentra observando el primero.

ol 011 1 B

El lector puede comprobar que se presentará la siguiente secuencia de estados y de símbo-


los en la cinta. Se muestran los estímulos (estado actual , cinta actual) y a su derecha las accio-
nes. Hay un asterisco debajo del símbolo que el control finito observa en cada momento:

Estado actual Cinta actual Cinta modificada


1) e0 0011BB X011BB

2) el X011BB X011BB
*
3) el XO11BB X O Y1 B B
*
4) e2 X0Y1BB X O Y1 B B
*
5) e4 XOY1BB X O Y1 B B

6) e0 X0Y1BB X XY 1 B B
*
7) el XXY 1 B B XXY 1 B B
*
8) el XXY 1 B B XXYY B B
*
9) e2 XXYYB B XXYY B B

10) e2 XXYYB B XXYY B B


*
11) e3 X XYY B B XXYY B B
*
12) e3 XXYYB B XXYY B B

13) e3 XXYYB B XXYY Z B


*
14) e5 XXYYZ B fin
272 Capítulo 6 Teoría matemática de la computación

Examinemos ahora cómo se comporta cuando se le presenta esta nueva cinta:

0I 1 1 B

Estado actual Cinta actual Cinta modificada


1) e0 O 1 1 BB X1 1 BB

2) el X11BB XY 1 BB
*
3) e2 XY1 B B XY 1 BB
*
4) e3 XY1BB XY1BB
*
5) e3 XY1BB X Y,1 B B
*
6) e6 XY1BB fin

Analicemos los resultados obtenidos. En el primer caso, MT 1 llegó al estado final e5 y,


en el segundo, al estado final e 6. Si ahora se conecta el estado e 5 con el sí y el estado e6
acepta la primera cadena (0011) y no acepta laconel,spudirqamán
segunda (011). El lector puede comprobar que la máquina acepta todas las cadenas de los
tipos O" 1" (n >= 1): aquellas formadas de al menos un cero seguido del mismo número de
unos, y que no acepta cadenas que no sigan esta regla de formación. Tal vez fuera necesa-
rio insertar un estado de "trampa" (equivalente a e 6) para que la máquina rechace otras
combinaciones. Así, para cualquier pareja estado actual-símbolo actual no definida ya en
la parte de estímulos de la tabla, simplemente se asocia una reacción que lleve a la máqui-
na a este nuevo estado final.
Si se considera que existe una infinidad de cadenas formadas por ceros y unos, puede
imaginarse la potencia del modelo de reconocimiento recién descrito. En efecto, permite
atacar y resolver un problema de tamaño infinito¡ con recursos finitos. No sólo eso; tam-
bién, como desde un punto de vista formal es posible considerar como un lenguaje a un
conjunto de símbolos (en este caso únicamente unos y ceros) y una regla de formación (en
este caso, que deben ser n ceros seguidos de n unos), resulta que la máquina de Turing
acepta (o reconoce) un lenguaje, y puede entonces considerarse como un esquema de
manejo de lenguajes formales.
La máquina de Turing representa el concepto de modelo llevado a su expresión más
básica o primitiva, pues —definiendo adecuadamente sus elementos, tabla y símbolos—,
se puede representar cualquier proceso descriptible.
En este punto cabe volver a preguntar: ¿existirá siempre una máquina de Turing que
se detenga (llegue a un estado final) para cualquier proceso adecuadamente descrito y
codificado?
Procesos computables La respuesta sigue siendo no. Ahora que ya es posible definir con más propiedad los
conceptos de algoritmo y de computabilidad, se dice que un proceso es computable o
tiene solución algorítmica cuando puede ser representado por medio de una máqui-
na de Turing que llega, en algún momento, a un estado final.
Si la máquina de Turing llega a un estado final con un sí, se estará haciendo una
correspondencia entre ella y el modelo de decisión. Pero cuando la máquina no llega a
este estado final pueden suceder dos cosas: que llegue a un estado de donde ya no salga, o
que sencillamente nunca se pueda saber si terminará o no con la computación.

(t) Consúltese las referencias [MINM67] y [MME87] para dos visiones sobre el concepto de infinitud.
Sección 6.2 El concepto de algoritmo: la máquina de Turing 273

Indecidibilidad
Para el primer caso bastará con hacer una equivalencia entre este estado sin salida y el no
del modelo de decisión; pero para el segundo habrá que decidir entre seguir esperando o Procesos no
no el resultado. En 1936, Turing demostró matemáticamente que existen procesos para computables
los cuales la máquina nunca terminará con un sí y nunca terminará con un no. En este
caso se podría esperar toda la eternidad para ver si la máquina se detiene o no, sin poder
llegar a saber si se detendrá en el siguiente instantet. Para ese caso, se dice sencillamente
que el problema no es computable, o bien, que no es posible decidir, en un tiempo finito,
si el proceso es representable algorítmicamente.
Los problemas de este tipo reciben el nombre de problemas indecidibles o "proble-
mas no solucionables en forma algorítmica", y el simple hecho de haberlos descubierto
representa una prueba de las enormes capacidades del método matemático para explorar
la realidad formal del mundo, pues se está hablando de verdaderos fantasmas que es posi-
ble describir pero nunca representar por completo para todos los casos. Sin embargo, no
debe confundirse este importante concepto con el trivial caso de no conocer la respuesta a
un problema: si no se conoce la solución de algo, puede simplemente deberse a la carencia
de datos suficientes o de tiempo o esfuerzo dedicado a averiguarlo; pero el hecho de que
un problema sea indecidible va mucho más allá, porque es equivalente a demostrar que no
existe ni puede existir solución posible, independientemente de voluntades y deseos.
Turing no detuvo aquí sus investigaciones, sino que en la búsqueda de los problemas
indecidibles generalizó el concepto de la máquina, como se verá ahora.
Supóngase que se construye la tabla de una máquina de Turing (que llamaremos MT 2)
capaz, por ejemplo, de extraer la raíz cuadrada de un número adecuadamente codificado
en la cinta, dejando el resultado del proceso en otra sección de la misma.
Es claro que esta máquina sólo tiene esa función, y no es capaz de sumar dos núme-
ros, ni de hacer ninguna otra cosa diferente de la original.
Bien, ahora hagamos un esfuerzo y observemos mentalmente la tabla de MT 2; ¿acaso
no consiste en una serie de símbolos que conservan cierto orden? ¿Sería posible codificarla
usando los símbolos de otro diccionario más complejo? Si se realizara esto, se podrían
colocar estos datos (la tabla de MT 2) codificados en la cinta de otra máquina de Turing.
Hagámoslo. Se tendrá así una nueva máquina (que llamaremos MT U), similar a las ante-
riores, y que tiene codificada en su cinta la tabla de otra máquina de Turing. Ahora cons-
truimos la tabla de MTu , o sea, la tabla que guíe a esta nueva máquina.
La nueva tabla tendrá instrucciones (parejas estímulo-reacción) que le indiquen cómo
seguir las instrucciones de la tabla de MT 2 codificada. Dichas parejas estímulo-reacción
irán haciendo que MTu se comporte exactamente de la misma forma en que se comporta-
ría MT2 , puesto que es precisamente su tabla la que está codificada en la cinta. Lo mismo
se aplicaría, por supuesto, para cualquier otra máquina cuya tabla estuviera en su lugar en
la cinta.
¡Se ha logrado una máquina universal!, pues imita o simula el comportamiento de
cualquier otra que esté codificada en su cinta. Pensemos un momento en la importancia Una máquina
de lo obtenido: se tiene un modelo de modelos. universal
La máquina universal de Turing (MTu) es, por excelencia, el modelo teórico de la
computabilidad; basta con codificar cualquier máquina particular de Turing en su cinta
para que sea entonces simulada y, por ende, pueda resolver ese problema particular
algorítmicamentet t.

(t) Esto parece una mala broma, pues siempre queda la duda de si no se llegará a un estado final un
instante después de haber abandonado la inacabable espera.
(t1) Aunque, claro, esto no es mágico: la máquina particular aún debe codificarse.
274 Capítulo 6 Teoría matemática de la computación

Es ahora cuando Turing se hace la siguiente pregunta: ¿podría la máquina universal


determinar si la máquina —particular— que está siendo simulada se va a detener (en un
estado final) o no? Esto es importante, porque implicaría predecir si algún procedimiento
convenientemente descrito y especificado terminará o no.
Supóngase que sí se puede detener: que existe una MI .] que determina si cualquier
otra (que llamamos MT0 ) se va a detener o no; es decir, termina con un sí si MT0 se
detiene, y con un no si MTo no se detiene. Entonces, también lo podrá hacer para la codi-
ficación de sí misma (es decir, podrá determinar si MT 1 se detendrá para cualquier caso
particular o no).
Ahora se construye una nueva máquina, MT 2, que se detiene si MTo no lo hace, y vice-
versa. Esto se logra haciendo que MT2 entre en un ciclo infinito cuando MTo se detiene (me-
diante un par de estados trampa, con los cuales la operación de la máquina oscilará entre uno
y otro, de tal forma que el primer estado la lleve al segundo, y éste nuevamente al primero).
¿Qué sucede si MT2 trabaja sobre la codificación de sí misma? Pues que se detendrá
Un problema si MT2 no se detiene, y no se detendrá si, y sólo si, se detiene. O sea, una contradicción total,
indecidible lo cual significa que esa máquina no puede existir; y a la vez equivale a decir que el pro-
blema bajo estudio es indecidiblet.
En resumen, los pasos fueron:
1. Se supone que se puede construir MT 1 , para determinar si MTo se detiene o no y
que, por tanto, también puede determinar si ella misma lo hace, cuando trabaja
sobre su propia codificación.
2. Se construye MT2, que termina con un sí cuando MT o no se detiene, y con un no
cuando MT0 se detiene. Esto se logra mediante un par de estados especiales de
trampa que causan entrada en un ciclo infinito cuando MT o llega a un estado final.
3. Cuando MT2 trabaja sobre su propia codificación se llega a una contradicción, lo
cual significa que la suposición del punto 1 es inválida.
Aunque ciertamente los problemas indecidibles desafían la intuición, se cometería un
error al suponer que se trata de meros asuntos especializados y esotéricos de matemáticas;
muy por el contrario, buena parte de los asuntos relacionados con la teoría de gramáticas
y lenguajes (y por tanto de traductores) están amenazados por lo indecidible, y esta es una
de la razones formales por las cuales al final del capítulo 5 alertamos en contra de las
visiones inocentes sobre la inteligencia artificial, porque aun el máximo poder electrónico
imaginable en una computadora será por completo inútil ante esta clase de problemas.
El trabajo de Alan Turing abrió muchísimas puertas en la investigación de la computabi-
Equivalencia entre lidad y demostró la existencia de la indecidibilidad y de un modelo genérico de la compu-
algoritmo y máquina tación. A tal grado se reconoce la máquina de Turing como modelo de computabilidad
que se acepta generalmente —pero es indemostrable, porque obligaría a salirse de ese
de Turing
marco de referencia— la siguiente propuesta, conocida como "la hipótesis de Turing": si
.-• existe una máquina de Turing para representar un problema, entonces éste tiene solución
algorítmica, y su dual: si un problema tiene solución algorítmica, es porque existe una
máquina de Turing para representarla.
En efecto, lo anterior hace equivalentes las nociones de algoritmo y de máquina de
Turing.
El concepto de indecidibilidad tiene profundas implicaciones filosóficas y de teoría
del conocimiento, porque efectivamente marca límites en los mecanismos formales de

(t) Este análisis del llamado "problema del alto de la máquina de Turing" (The Halting Problem)
sigue a lo expuesto en [MINM67, págs. 146-149]. El artículo original de Turing se llamó "On
computable numbers, with an application to the Entscheidungsproblem"; el último término alemán
se refiere al problema de la decidibilidad.
Sección 6.2 El concepto de algoritmo: la máquina de Turing 275

modelaje de la realidad: exhibe procesos que tienen una descripción acotada, pero que no
pueden ser representados en tiempo finito. Más adelante se explorará de nuevo esta in-
quietante idea.
Desde esta nueva perspectiva, el conocimiento formalizable (es decir, aquél basado
en los principios de la ciencia) puede entonces dividirse en tres grandes grupos: lo desco- Una clasificación
nocido, lo conocido y lo "inconocible". El objetivo del quehacer científico consiste en del conocimiento
avanzar del primer grupo al segundo, bordeando la insondable área de aquello que no se
puede conocer (porque es indecidible).
Ya formalizado el concepto de algoritmo, se ha convertido en fundamental en mate-
máticas y en filosofía de las ciencias formales, donde se emplea, junto con la máquina de
Turing, en demostraciones y planteamientos de teoremas. Un anexo al final de este capí-
tulo contiene notas sobre la historia de la lógica matemática y algunas consideraciones de
carácter más amplio, para aquellos lectores —seguramente todos— interesados en la filo-
sofía de las matemáticas. Además, se recomienda la referencia [RUSB88].
Gracias a la máquina de Turing se dispone de un método para definir en términos
rigurosos (sin ambigüedades o interpretaciones subjetivas) el concepto de procedimiento
efectivo: una descripción sobre la cual existe la garantía de que su representación termi-
nará en un tiempo finito.
En términos prácticos y desde una perspectiva lógica (y no física ni de ingeniería), el
quehacer de la computación se refiere al diseño y elaboración de algoritmos eficientes para
modelar situaciones o resolver problemas en la forma más rápida y económica posible.

Complejidad de algoritmos
Por lo pronto bastará con saber que no todos los algoritmos (decidibles) son iguales, porque
pueden clasificarse en familias, según su complejidad. El campo de estudios formales Clasificación
conocido como complejidad de algoritmos (subárea 6.1.3 dentro de los Modelos Curricu- de algoritmos
lares) analiza las formas de comportamiento de los programas en dos grandes áreas: tiem-
po que tardan en terminar, y cantidad de espacio (en memoria) que requieren para efectuar
su trabajo, aunque aquí nos interesa más bien la primera.
En términos generales, el tiempo que un algoritmo toma para ejecutar (suponiéndolo
bien escrito) puede clasificarse en varias categorías, dependiendo de cómo se comporta
cuando trabaja con cantidades crecientes de datos.
Un algoritmo se dice de complejidad lineal cuando el tiempo que tarda en procesar
crece en forma linealmente proporcional con el número de sus datos. Es decir, cuando la
función que describe su comportamiento es del tipo

t = fiCn), donde C es una constante y n y t son variables simples que denotan


la cantidad de datos y el tiempo, respectivamente.

Por ejemplo, si un cierto proceso de complejidad lineal tarda 15 unidades de tiempo


(milisegundos, tal vez, aunque podrían igualmente ser segundos o semanas) en procesar
100 datos —para encontrar alguno en particular o posiblemente para obtener su prome-
dio—, entonces tardará 150 unidades para procesar 1,000 datos, y así sucesivamente.
La constante C puede ser un valor numérico o a su vez una función de tipo logarítmico,
que crece más lento que una constante. Si n (la cantidad de datos) se dobla, log(n) no se
dobla, sino aumenta sólo un poco. Para que log(n) llegue al doble, n debe convertirse en
n2, lo cual vuelve a la función t = log(n) mucho más conveniente que t = Cn. Claro que el
verdadero problema consiste en encontrar la forma de convertir un algoritmo de compleji-
dad lineal a complejidad logarítmica, y eso sí que resulta difícil, cuando no imposible.
Para el caso de buscar un número específico entre un conjunto de números, un algorit-
mo simple conocido como "búsqueda secuencial" efectúa del orden de (n + 1)/2 = n/2 + 1/2
276 Capítulo 6 Teoría matemática de la computación

comparaciones, mientras que el algoritmo mejorado llamado "búsqueda binaria" efectúa


del orden de lg(n) comparaciones t, lo cual lo vuelve mucho más eficiente.
He aquí una comparación entre los tiempos de procesamiento de los dos algoritmos,
para diversos valores de n:
n (n+1)/2 lg(n)
10 5.5 3
100 50.5 6
1,000 500.5 9
10,000 5,000.5 13
100,000 50,000.5 16
1,000,000 500,000.5 19

Nadie en su sano juicio emplearía la versión lineal cuando pudiera usar la versión
mejorada, pero no siempre es posible porque esta última requiere que los datos estén previa-
mente clasificados, aunque estas consideraciones quedan ya fuera del alcance de nuestro
libro, porque son temas de estructuras de datos.
La conclusión por lo pronto es: sería bueno que todos los algoritmos fueran lineales
o, mejor aún, de complejidad logarítmica. Las cosas, sin embargo, no suelen ser así.
Por el contrario, muchos métodos exhiben la propiedad de ser de complejidad polino-
mial, porque la función que gobierna su tiempo de proceso está descrita por un polinomio,
lo cual en general resulta ser peor que el caso lineal. Es decir, cuando
t = f(C in + C2n2 + + Cknk), donde C 1 , C2, ..., Ck son constantes (pueden ser
cero) y, como antes, n y t denotan la cantidad de
datos y el tiempo, respectivamente.

Por ejemplo, un algoritmo sencillo de clasificación conocido como "método de la


burbuja" toma un tiempo proporcional a n' y otro, shellsort, consume n3', por lo cual es
más eficiente, como aparece en la tabla de comparación:
n nz n3/2

10 100 30
100 10,000 1,000
1,000 1,000,000 31,000
10,000 cien millones 1,000,000
100,000 diez mil millones 31,600,000
1,000,000 un billón mil millones

Claro que se prefiere un algoritmo lineal a uno polinomial, pero no siempre existe
para un problema dado. No sólo eso, sino que una gran cantidad de situaciones no pueden
ser modeladas ni siquiera por métodos de complejidad polinomial, y toman un tiempo
exponencial, porque su comportamiento en el tiempo está descrito por una función en
donde la cantidad de datos actúa como exponente:
t = f(C"), donde C es una constante y n es la cantidad de datos.

(f ) En este caso se emplean logaritmos de base 2, aunque las diferencias numéricas con respecto al
caso de los logaritmos de base 10 o los logaritmos naturales de base e no resultan significativas
cuando el valor de n es relativamente alto. Algunos de estos primeros ejemplos numéricos aparecen
en Algoritmos en C+ +, de Robert Sedgewick, Addison Wesley, México, 1995. Se trata de un libro
avanzado, fuera de nuestro alcance por lo pronto.
Sección 6.3 Lenguajes formales y autómatas 277

Estos algoritmos son absolutamente imprácticos, y uno de los objetivos del campo de
las ciencias de la computación conocido como análisis de algoritmos consiste, justamente,
en tratar de reducirlos de complejidad mediante un entendimiento profundo de su natura-
leza. Un algoritmo exponencial siempre requerirá más tiempo del disponible, excepto
para mínimas cantidades de datos, y por ello los problemas de ese tipo deben resolverse
con aproximaciones o mediante simulación de algunos casos representativos. El clásico
ejemplo exponencial lo constituye el "problema del agente viajero", en donde debe dise-
ñarse una ruta óptima para que un vendedor recorra un conjunto de ciudades empleando la
menor cantidad de recursos posibles, aunque existen muchos otros. La tabla muestra las
formas de crecimiento de una función lineal, una polinomial (cuadrática), una logarítmica
(con logaritmos de base 2) y una exponencial, todas con n datos:
n n' lg(n) 2"

10 100 3 1,024
1 00 10,000 6 1.27 X 10"
1 , 000 1,000,000 9 1.07 X 1030°
1 0,000 cien millones 13 demasiado grande
1 00 , 000 diez mil millones 16 demasiado grande
1, 000 , 000 un billónt 19 demasiado grande

A partir del segundo renglón las cosas se ponen un poco mal: 1.27 x 10 3° microsegundos
(millonésimas de segundo), por ejemplo, son aproximadamente 4.02 x 10 14 siglos: millo-
nes de veces más que la edad del universo.
Aun si las computadoras del futuro fueran cientos de miles de millones de veces más
rápidas que las actuales, nada podrían hacer frente a un caso así, por lo cual el mero plantea-
miento de la existencia de problemas de este tipo resulta intelectualmente amenazadortt.
Como conclusión podría decirse que el estudio de los algoritmos responde tanto a
necesidades prácticas (optimización de métodos polinomiales) como a la inacabable fas-
cinación por explorar las fronteras del conocimiento.

6.3 LENGUAJES FORMALES Y AUTÓMATAS

Antes de hablar de los algoritmos se había descrito cómo la máquina de Turing puede
reconocer cadenas pertenecientes a un lenguaje, y ahora se darán algunas definiciones
básicas sobre lenguajes formales y sus reconocedores, que reciben el nombre genérico de
autómatas.
El estudio formal de los lenguajes se debe, entre otros investigadores, al ya mencionado
Noam Chomsky, lingüista que en 1956 publicó un estudio considerado como pionero en
el campo conocido como lingüística matemáticat t t.
En [CHON57] Chomsky explica
El objetivo fundamental en el análisis lingüístico de un lenguaje es separar las
frases gramaticalmente correctas de las que no lo son, y estudiar la estructura
de las correctas.

(t) Debe tenerse cuidado cuando se traduce la palabra billion del inglés, porque en español no
significa "billón", sino "mil millones". Igualmente, el trillion del inglés (un millón de millones)
resulta ser "billón" en español.
(tt) Consúltense, por ejemplo, los interesantes artículos LEWP78], [STOC791 y [TRAW94].
(ttt) Como se indicó en el capítulo 5, Chomsky es además un importante crítico de la política
exterior estadounidense, y representa una corriente de opinión de la izquierda liberal de su país. Ha
publicado varios libros sobre política exterior y sobre lo que él llama "la responsabilidad de los
intelectuales" en el quehacer político.
278 Capítulo 6 Teoría matemática de la computación

Y más adelante propone


La investigación sintáctica de un lenguaje tiene como objetivo la construcción
de una gramática, que generará las sentencias del lenguaje.

Es decir, una gramática es el mecanismo mediante el cual se producen las frases que
constituyen un lenguaje. Desde un punto de vista, un lenguaje formal es un sistema alge-
braico y, como tal, está sujeto a un tratamiento matemático riguroso, a lo cual se dedica la
teoría de lenguajes y autómatas (subárea 2.4.1 de los Modelos Curriculares).
Las siguientes definiciones básicas permitirán hacer un elemental estudio sobre estos
Definiciones temas, de central importancia para la teoría de la computabilidad, su aplicación a los
preliminares lenguajes de programación, los compiladores y, en general, para la programación de siste-
mas avanzada.
Un alfabeto o vocabulario es un conjunto finito de símbolos, y una frase o sentencia
es una cadena de longitud finita compuesta de símbolos del alfabeto. Para propósitos de
demostración de teoremas y otros estudios, existe una frase especial que consta de cero
elementos, llamada cadena vacía que se representará mediante el símbolo A.
Si V es un alfabeto, entonces la notación V* (léase "la cerradura de V") denota al
conjunto de todas las frases compuestas de símbolos de V, incluyendo la cadena vacía. En
general, la cerradura representa la operación de repetición. Por ejemplo, si V es un alfabe-
to que contiene dos símbolos, O y 1,
V = { 0,1 }

entonces, la cerradura de V será un conjunto infinito .


V*. {A, 0, 1, 00, 10, 01, 11, 000, 100, 010, 001, 110, 101, 011, 111, 000 ... }
Además, a un conjunto cualquiera de frases se le conoce como lenguaje.
Surgen entonces varias preguntas: ¿Cómo se representan las frases de un lenguaje y,
más aún, si éste es infinito (es decir, si contiene un número infinito de frases)? ¿Existe una
representación finita para cada lenguaje? ¿Qué se puede decir de la estructura de los len-
guajes que sí admiten una representación finita?
Como se verá a continuación, las representaciones finitas de los lenguajes son esque-
mas generativos, es decir; son un modelo dinámico para generar frases de un lenguaje, y
se llaman gramáticas formales.
Una gramática G se define como un conjunto de cuatro elementos:
—un vocabulario, llamado no terminal
—un vocabulario, \ft, llamado terminal
—un conjunto P de reglas de producción o de reescritura
—un símbolo especial de inicio, S, que por definición pertenece al vocabulario no terminal
Así, G = < V., V t, P, S >
El corazón de la gramática es el conjunto P de reglas de producción o de reescritura,
que define cómo obtener nuevas cadenas de símbolos a partir de otras previast. Cada
regla consta de dos miembros, conectados mediante una flecha:
a —› b

(t) O sea que las reglas de producción en realidad no son otra cosa que macrodefiniciones, que
especifican cómo reemplazar un texto por otro. Recuérdese lo ya estudiado en la sección 5.3, sobre
macroprocesamiento.
Sección 6.3 Lenguajes formales y autómatas 279

El miembro izquierdo está formado por al menos un elemento tomado de los alfabe-
tos terminal y no terminal o, en términos más generales, por alguna cadena no vacía tomada
de la cerradura de V. y V,. El miembro derecho tiene las mismas características; pero
puede incluso ser la cadena vacía.
Empleando notación elemental de conjuntos:

an {V L.)V,}*—{A }
be {Vn.uV,}*

Mediante las reglas de producción de una gramática se pueden obtener cadenas de


símbolos a partir del símbolo inicial. Por tanto, el símbolo inicial debe ser el miembro
izquierdo de al menos una regla dentro del conjunto P.
El procedimiento mediante el cual se producen cadenas de símbolos (frases de un Cómo se obtienen
lenguaje) es el siguiente: frases con una
gramática.
1. Se escoge una regla de producción que contenga a S como miembro izquierdo.
2. Se aplica la regla, es decir, se sustituye el símbolo S por la cadena b del lado
derecho de esa regla.
3. Ahora pueden suceder dos cosas: que esa cadena b esté formada exclusivamente
por elementos del vocabulario terminal V,, o que no sea así. En el primer caso se
ha obtenido ya una frase (que, por tanto, forma parte del lenguaje producido por la
gramática), y el procedimiento termina. Es posible volver a iniciar comenzando por
el paso 1, para intentar obtener otras frases terminales diferentes. Por otra parte, si
la cadena b contiene al menos un elemento no terminal, entonces el procedimiento
de generación aún no termina, y habrá que escoger alguna regla dentro del conjunto
que contenga ese elemento no terminal como parte de su miembro izquierdo, para
entonces aplicarla y volver a hacer la consideración de este paso 3. Esto se repite
hasta obtener una cadena compuesta exclusivamente de elementos terminales. Si
no se puede llegar a un fin, entonces se dice que la gramática no produce nada.

La razón de ser de una gramática es producir cadenas terminales, que entonces for-
man parte del lenguaje producido por ella.
Un ejemplo aclarará esto. Considérese la siguiente gramática:

G= ( {S}, {0,1},P,S )

en donde P consta de las siguientes reglas:

1. S —> 0S1
2. S --> 01

Se puede aplicar inicialmente la regla 1 o bien la 2, porque ambas contienen a S como


miembro izquierdo. Si se aplica la regla 2 se obtiene

S 01 (La doble flecha representa la aplicación de alguna regla.)

y el proceso termina allí, ya que la cadena 01 contiene únicamente elementos terminales.


Si se aplicara inicialmente la regla 1, sin embargo, se obtendría lo siguiente:

S OS1

y como el lado derecho de esta aplicación contiene al menos un elemento no terminal, se


vuelve a aplicar una regla (la 2), para obtener
280 Capítulo 6 Teoría matemática de la computación

0S1 = 0011 (Lo subrayado es lo que se acaba de sustituir.)

que ya es una cadena terminal y, por tanto, perteneciente al lenguaje producido por esta
gramática.
El proceso completo fue, entonces,

S O S 1 = 0011

El lector puede comprobar que la cadena 000111 también pertenece al lenguaje pro-
ducido por esta gramática, porque

S 0S1 00S11 000111

pero que la cadena 00111 no puede ser producida por esta gramática, como tampoco
alguna que comience con 1.
Ahora es posible intentar contestar las preguntas planteadas al inicio de este apartado,
diciendo que no obstante que esta gramática produce un número infinito de frases (porque
se puede escoger la regla 1 tantas veces como se desee), todas ellas tienen la misma estruc-
tura gramatical (conocida en lingüística como estructura profunda): un grupo de ceros
seguido del mismo número de unos, y que además existe un procedimiento para obtenerlas.
Como se dijo, el conjunto de todas las frases terminales que se pueden obtener con una
gramática es el lenguaje producido por ella, lo cual se representa formalmente como:

L(G)={wiweV,*&S*w}

Esto se lee así: "el lenguaje L producido por una gramática G es el conjunto de todas
Definición formal las cadenas w tales que w pertenece a la cerradura del vocabulario terminal (o sea, que w
de un lenguaje consta sólo de terminales) y que w fue además obtenida mediante la aplicación repetida
de reglas de producción (lo cual se indica mostrando la cerradura de la aplicación de
alguna regla), habiendo comenzado con el símbolo inicial". En otras palabras, el lenguaje
producido por una gramática es el conjunto de todas las cadenas terminales válidas
derivables de ella.
En general, averiguar cuál lenguaje produce una gramática no es una tarea trivial,
porque debe determinarse formalmente la estructura común a todas sus frases. Existe, por
supuesto, toda un álgebra para manejar gramáticas y lenguajes pero ya no hay espacio
aquí para describirla. Las referencias [ BROG93], [COHD91] y [ HOPU93] se dedican a estos
temas que, en general, son de nivel avanzado.

Jerarquización de gramáticas
Una vez que se cuenta con un procedimiento para producir frases dotadas de una estructu-
ra afín (o sea, una gramática), es posible estudiar a fondo sus propiedades matemáticas,
con el fin de intentar determinar de antemano las características que se pueden esperar de
los lenguajes obtenidos. Para ello, Chomsky clasificó las gramáticas (y, por ende, los len-
guajes que generan) en varias familias, enmarcadas en una jerarquía de cuatro tipos. En
esta jerarquía, las gramáticas de tipo O son las más generales e incluyen como caso par-
ticular a las de tipo 1, que a su vez tienen las propiedades de las que engloban —de tipo
2—, para llegar al extremo más limitado, las gramáticas de tipo 3.
En una gramática de tipo O no hay restricciones para definir los miembros izquierdo
Tipos de gramáticas y derecho de las reglas de producción, excepto, claro, que deberán estar formadas por
elementos de los vocabularios ya definidos.
Una gramática es de tipo 1 si se exige que la longitud del miembro derecho de toda
regla de producción sea mayor o igual que la longitud del izquierdo. Con esto se impide
Sección 6.3 Lenguajes formales y autómatas 281

que las cadenas obtenidas en el transcurso de la aplicación de las reglas sean de longitud
decreciente, y se impide asimismo el caso extremo de que mediante una gramática de tipo
1 puedan desaparecer cadenas de símbolos.
Esto tiene implicaciones teóricas importantes. Las gramáticas de tipo 1 también reci-
ben el nombre de "sensibles al contexto", porque del lado izquierdo puede haber más de
un elemento, lo cual implica que un símbolo puede reemplazarse en el contexto de otros,
como se explicará en un ejemplo.
Una gramática es de tipo 2 cuando se exige que el miembro izquierdo de toda regla
de producción sea un único elemento no terminal, y a la vez se impide que el lado derecho
esté vacío. Es fácil observar cómo estas nuevas limitaciones cumplen además la restricción
pedida a las gramáticas anteriores: que la longitud del lado derecho sea al menos igual a la
del lado izquierdo. Como del lado izquierdo de las reglas en estas gramáticas de tipo 2
sólo puede haber un elemento no terminal, se puede sustituir un solo símbolo no terminal
a la vez, independientemente de lo que lo rodee; por esta razón, a estas gramáticas se les
conoce también como "independientes del contexto" (context free).
Por último, si a una gramática de tipo 2 se le restringe aún más en sus reglas de produc-
ción, regularizándolas para que sólo sean de alguna de dos formas bien definidas, enton-
ces se convierte en una gramática de tipo 3, también llamada "gramática regular".
Una gramática es de tipo 3 cuando las dos formas permitidas para las reglas son: 1) un
no terminal del lado izquierdo produce un terminal del lado derecho y, 2) un no terminal
del lado izquierdo produce del lado derecho un terminal seguido de un no terminal.
Es decir, cuando todas sus reglas son de alguna de estas dos formas:

A —> a
A —> aA

para elementos no terminales (A) y terminales (a) cualesquiera.


Antes de mencionar los resultados obtenidos con esta jerarquización, mostraremos
algunas gramáticas.
Por ejemplo, la gramática con reglas

1. S --> 0S1
2. S —> 01

mostrada anteriormente es de tipo 2, porque tiene un solo elemento no terminal del lado
izquierdo de cada regla y porque sus lados derechos no son nulos.
Esta nueva gramática es de tipo 1, porque la única restricción en las reglas es que el
lado derecho sea mayor o igual que el izquierdo:

1. S Abc
2. Ab —> aAbB (Las mayúsculas son elementos no terminales y
3. Bb bB las minúsculas son terminales.)
4. Bc -3 bcc
5. A —> a

Con ella se pueden obtener cadenas compuestas por las letras a,b,c en ese orden, y
con la misma cantidad de cada una.
Por ejemplo, así se genera la cadena "aabbcc":

S = Abc aAbBc aabBc aabbcc

aplicando sucesivamente las reglas 1, 2, 5 y 4. Obsérvese cómo al aplicar la regla 2 se usó


el contexto formado por la secuencia Ab.
282 Capítulo 6 Teoría matemática de la computación

Este es un ejemplo de una gramática regular o de tipo 3, que produce palabras forma-
das por cualquier número de letras o dígitos, pero que comienzan con una letra:

1. S (La 1 es un terminal que representa


2.S—>/A una letra cualquiera; la d es un
3.A1 terminal que representa un dígito
4.A—>d cualquiera, y la A es un símbolo
5.A —> /A no terminal auxiliar.)
6. A —> dA

Por ejemplo, la secuencia de derivación de la cadena "alfa27" es:

S aA alA = alfA = alfaA alfa2A = alfa27

aplicando las reglas 2, 5, 5, 5, 6 y 4.


Como se dijo, el estudio de las gramáticas y los lenguajes formales es un bello y muy
amplio campo de análisis e investigación, y entre los sorprendentes resultados que se han
Propiedades obtenido resalta el hecho de que las gramáticas de tipo O y 1 exhiben un buen número de
indecidibles propiedades indecidibles. Por ejemplo, para una gramática de tipo O no se puede deter-
minar algorítmicamente si una frase dada es producto de ella o no; es decir, no se puede
saber si una máquina de Turing diseñada para reconocer esas frases se detendrá o no
durante el transcurso de esa computación. Igualmente, es indecidible el procedimiento
para averiguar si dos gramáticas de tipo O dadas producen o no el mismo lenguaje. Más
aún, el número de propiedades indecidibles de las gramáticas es mayor que el de las que sí
lo son; por eso el hecho de restringirlas mediante la jerarquización de Chomsky es útil,
pues produce construcciones más "dóciles", cuyas características sí son determinables
algorítmicamente. El precio pagado, por supuesto, es que una gramática de tipo 3 o de
tipo 2 produce un lenguaje más limitado y menos rico que una de tipo 1 ó O.
Con todo lo analizado hasta ahora debe estar clara la existencia de dos procesos fun-
damentales en teoría de gramáticas y lenguajes formales: la producción de las frases de
un lenguaje a partir de una gramática, y el reconocimiento de frases como pertenecientes
al lenguaje producido por una gramática. Aparentemente, el proceso de producir una frase
debe ser el inverso de reconocerla, sobre todo si se visualiza la producción de una
frase como la generación de una descripción, y su reconocimiento como la representa-
ción. De hecho, así es y, como se ha dicho ya, para cada tipo de gramática existe un recono-
cedor particular, conocido con el nombre genérico de autómata. Hay también, entonces, una
jerarquización de autómatas, mostrada a continuación.

Gramática y lenguaje Reconocedor


que genera
Tipo O Máquina de Turing
Tipo 1 Autómata lineal
(Sensible al contexto)
Tipo 2 Autómata de pilat
(Independiente del contexto)
Tipo 3 Autómata finito
(Regular)

(t) Recibe este nombre debido a que emplea una estructura de datos conocida como pila (stock,
en inglés).
Sección 6.3 Lenguajes formales y autómatas 283

Queda fuera de los alcances de este curso introductorio dar una descripción de estos
autómatas, aunque ya se ha dedicado una sección al más poderoso y general de ellos,
capaz de simular a todos: la máquina de Turing.
Sin embargo, como se ha visto, aun este último autómata/reconocedor por excelencia
tiene limitantes, pues existe toda una clase de procesos que se pueden describir mas no
representar algorftmicamente; éstos son los procesos indecidibles, que efectivamente
marcan una frontera entre lo que es computable y lo que no lo es.
La teoría de la computabilidad ha permitido descubrir muchas de las propiedades de
los lenguajes y sus reconocedores, e hizo posible el diseño de los lenguajes de programa-
ción y sus correspondientes autómatas encargados de reconocerlos y traducirlos a expre-
siones más simples —los compiladores, descritos en el capítulo 5—. Las gramáticas
que describen los lenguajes de programación usuales son de tipo 2, y el analizador sintáctico
de un compilador es un autómata de pila. De la misma forma, el analizador lexicográfico de
un compilador es un autómata finito, porque la gramática empleada en la definición léxica
de los lenguajes de programación es de tipo 3 (de hecho, el ejemplo de la gramática de
tipo 3 recién expuesta muestra cómo se pueden obtener los nombres simbólicos de las
variables para un lenguaje tipo Pascal o C).
En términos más formales, se dice que un lenguaje cuyas frases pueden ser generadas
por un procedimiento es recursivamente enumerable, mientras que se le llama recursivot
si existe un algoritmo para reconocerlo.
Que un lenguaje sea recursivamente enumerable significa que se puede producir me-
diante una gramática; que sea recursivo significa que existe para él un algoritmo de recono-
cimiento: una máquina de Turing que llega a un estado final (un sí o un no) cuando analiza
las frases que lo componen.
Terminamos este capítulo introductorio sobre computabilidad enunciando el resultado
final (intuitivamente contradictorio, porque la indecidibilidad no parece ser muy espon-
tánea): no todos los lenguajes recursivamente enumerables son recursivos.
En otras palabras, nuestro mecanismo mental de conocimiento sistematizable tiene límites
inherentes, pues el hecho de que existan y se puedan construir descripciones para las cuales
no existen ni se pueden construir representaciones finitas tiene profundas implicaciones
matemáticas, de teoría del conocimiento y filosóficast t. Tal vez a esto se refiere el filósofo
y lógico Ludwig Wittgenstein, cuya obra —mencionada más adelante— concluye con la
aparentemente simple frase: "De lo que no se puede hablar, mejor es callarse" lt

(t) El empleo del término recursivo en este contexto es independiente del que tiene al referirse a
lenguajes de programación; es importante no confundir estos dos usos. El primero, aquí empleado,
toma su nombre de la teoría de funciones recursivas de las matemáticas, mientras que el segundo, el
más común, se refiere a la capacidad que ofrece un lenguaje de programación de manejar módulos
y rutinas que se llaman a sí mismos.
(t -t) De hecho, el propósito último expresado por el gran matemático Kurt Gódel —también mencio-
nado más adelante, por su demostración de la existencia de proposiciones indecidibles en la arit-
mética y los demás sistemas formales— era completar una prueba formal de la existencia de Dios
como ejemplo de lo absoluto, siguiendo así los intentos iniciales de Leibnitz, de quien igualmente
se hablará en este capítulo.
(t tt) Las ideas sobre lo indecidible, debidas a Gódel, Turing, Church y otros, muestran cómo la
distancia que nos separa de lo infinito a su vez es infinita, y tienen la virtud de convertirnos en un
tanto más humildes cuando de conocerlo todo se trata. Un caso similar lo constituye el derrumba-
miento de los aparentemente ilimitados poderes de la mecánica newtoniana para describir el uni-
verso: la segunda ley de Newton (1642-1727), por ejemplo, indica que aplicando una fuerza suficiente
se podrá obtener cualquier aceleración y por ende viajar a cualquier velocidad deseada (F = ma).
Muy por el contrario, el inalcanzable límite c (la velocidad de la luz) es el abismo que la nueva
física de Einstein (1879-1955) mostró como respuesta ante nuestros fáusticos anhelos de poder.
284 Capítulo 6 Teoría matemática de la computación

Los siguientes cursos de computación suelen estar dedicados a integrar estos con-
ceptos teóricos a formas prácticas de ser programadas en una computadora porque, sobra
decirlo, estos resultados de 1936 son anteriores al nacimiento de las computadoras elec-
trónicas digitalest. La segunda parte del libro presenta un método para escribir algoritmos,
que cumplan además ciertos criterios humanos, y no sólo formales.

6.4 ANEXO: VISIÓN HISTÓRICA DE LA LÓGICA


MATEMÁTICA"

Todas las proposiciones de la lógica dicen lo mismo. Es decir, nada.


Ludwig Wittgenstein

Lógica tradicional
El arte de la lógica nace en la Grecia de los siglos iv y v a.C. como un desarrollo de los
esfuerzos de grupos de filósofos y retóricos que pugnaban por refutarse mutuamente a
través de discursos cada vez más elaborados. Buscaban en particular la manera de encon-
trar las fallas en los razonamientos de sus adversarios. Uno de los métodos usados era el
de aceptar el punto de vista de sus contrarios y luego demostrar que implicaba consecuen-
cias absurdas.
Primero Sócrates, y luego su discípulo Platón (428-347 a.C.), comienzan el estudio
de estos problemas con un método definido, consistente en buscar las características
generales implícitas en los razonamientos.
Platón, en el diálogo "Eutifrón o de la santidad", pregunta "¿Lo santo es amado por
los dioses porque es santo, o es santo porque es amado por ellos?", dando a entender que
Sócrates conocía perfectamente bien la diferencia entre una condición suficiente y una
condición necesaria (en [ PLAT73 ]).
Pero no será sino hasta Aristóteles cuando la lógica nace como un método especiali-
zado que servirá como instrumento de la filosofía. Aristóteles es creador de un sistema
lógico llamado silogística, y hace también un estudio del concepto de definición. Se da
cuenta de que la definición debe estar dada en términos anteriores al objeto definido,
observando así la necesidad de partir de algunos términos indefinidos para construir la
serie. Hace notar que una definición dice qué es una cosa, pero no que esa cosa existe, por
lo que se vuelve necesario, además, demostrar su existencia.
Con respecto a la lógica, Aristóteles da a entender que se deriva de las matemáticas.
Sus principios básicos son la ley de contradicción (una proposición no puede ser falsa y
verdadera a la vez) y la ley del tercero excluido (una proposición debe ser o bien verdadera

¿Más ejemplos?: Luego de expediciones científicas por el mundo, Charles Darwin (1809-1882)
postuló que, lejos de ser los príncipes de una creación terminada, somos partícipes de un inacaba-
ble proceso evolutivo compartido con los demás seres vivos. Mediante estudios teóricos y años de
trato con pacientes, Sigmund Freud (1856-1939) indicó que buena parte de nuestros actos están
anclados en razones y motivaciones ajenas a lo racional y consciente. A medida que avanza el
conocimiento científico sobre nosotros y el mundo que nos rodea, aumenta también nuestro asom-
bro por haber sido capaces de descubrir sus límites. Tal vez Sócrates tenía razón.
(t) Como dato interesante cabe mencionar que Turing se dedicó, años después de estas investigacio-
nes, y poco antes de suicidarse, al desarrollo de las primeras computadoras electrónicas.
(tt) Recomendamos los deliciosos libros [PERM94], como amena fuente y guía sobre las principa-
Charles Darwin (1809-1882) les ideas matemáticas, o [DUNW901, para un fantástico viaje por algunos de sus teoremas centrales.
Sección 6.4 Anexo: Visión histórica de la lógica matemática 285

o bien falsa). Con base en esos principios es posible construir un método de pruebas e
inferencias deductivas. También introduce el uso de variables en el estudio de la lógica.
Aristóteles usa la lógica como un instrumento (Organon) y le da el nombre de analí-
tica. Su estudio se divide en varias partes:

- Categorías (estudio de las condiciones generales del pensamiento)


- Analíticos primeros (estudio del silogismo)
- Analíticos segundos (estudio de la demostración)
- Tópicos y refutaciones de los sofistas (en donde se expone un método de argu-
mentación)

Debe mencionarse que la lógica de Aristóteles básicamente no contiene errores y ha


sido reaxiomatizada para mostrar que es consistente y decidiblet.

Después de Aristóteles
En principio, el sistema de Aristóteles se mantuvo inconmovible por más de 2000 años,
hasta el nacimiento de la lógica moderna en el siglo xix.
La lógica fundada por Aristóteles es una lógica de términos basada en la silogística.
La lógica moderna es una lógica de proposiciones que presupone a la escolástica o
aristotélica.
Los precursores de la lógica de proposiciones se pueden encontrar poco después de la
muerte del maestro (322 a.C.). Para los filósofos Teofrasto de Ereso y Crisipo de Soles,
posteriores a Aristóteles, la lógica tiene dos valores: una proposición cualquiera o es verda-
dera o es falsa. Y ésta es la definición que dan de proposición. Reconocían proposiciones
simples y compuestas. Las segundas resultan de combinaciones de proposiciones diferen-
tes, o de dos ocurrencias de la misma; esta combinación se logra por medio de conectivas.
También sabían negar proposiciones anteponiéndoles una partícula negativa; y formaban
una función de verdad con el resultado, diciendo que no-A es verdadera cuando A es falsa,
y viceversa. Tenían además conocimientos acerca de la implicación, la conjunción y la
disyunción exclusiva.
Mucho tiempo después se encontrará la lógica aristotélica dentro de la cultura árabe,
entre los siglos vin y xin, y en la lógica de la Edad Media.
Es interesante abrir un paréntesis para considerar la lógica hindú. En la India florecieron
varias escuelas lógicas enmarcadas dentro de la filosofía hindú. Su estudio puede dividir-
se en cinco partes:

- Gramática (estudio de las reglas lógicas de formación de sánscrito)


- Mimamsa (dedicada a problemas de interpretación de textos)
- Vaisésika y antigua Nyaya (sistemas de filosofía natural)
- Lógica budista (se encuentran aquí algunos fundamentos de lógica formal; esta
escuela comienza a excluir consideraciones ontológicas y psicológicas del estu-
dio de la lógica)
- Nueva Nyaya (fase final de la lógica hindú, que continúa hasta la fecha)

Dentro de la gramática se puede encontrar una preocupación por un criterio de eco-


nomía que requiere la eliminación de elementos superfluos. Se construye así un lenguaje
técnico, con abreviaciones y conceptos elementales, usado de acuerdo con ciertas reglas
de composición y sustitución. El creador de la Tierra, con
cuatro cabezas, que
simbolizan los cuatro vedas
(escrituras sagradas), los
(f) El lógico polaco Jan Lukasiewicz publicó en 1951 el libro A ristotle's Syllogistic from the Standpoint cuatro yogas (eras) y
of Modem Formal Logic (Oxford University Press, Nueva York), en donde expone esos resultados. las cuatro vamas (castas).
286 Capítulo 6 Teoría matemática de la computación

Precursores de la lógica moderna


La lógica matemática moderna nace en
1847 con la publicación de los trabajos de
los matemáticos ingleses De Morgan y
Boole, pero ya antes Leibnitz, Euler, Lambert
y Bolzano habían realizado los estudios
que prepararon el camino[.
Leibnitz suponía que un concepto se
puede descomponer en elementos simples,
tal como un entero es separable en sus fac-
tores primos, y sugería la posibilidad de
formar un lenguaje universal estructurado
lógicamente y capaz de servir como el ins-
trumento de análisis científico. Tenía todo
un programa de distinciones y funciones
de los diversos elementos del lenguaje, y
sentó las bases del concepto de función
proposicional. Tenía la intención de fun-
dar un cálculo lógico, capaz de expresar
ideas de una manera lógica y sin ambigüe-
dades. También hace una distinción funda-
mental entre antecedente y sujeto, y entre
consecuente y predicado, pasando así al
moderno punto de vista de proposiciones,
Wilhelm Leibnitz (1646-17115) en lugar de usarlos como términos en el
sentido aristotélico.

(t) Gottfried Wilhelm Leibnitz, filósofo y matemático alemán, fue fundador, en paralelo con Newton,
del cálculo infinitesimal, aunque hasta el final de su vida mantuvo con éste una agria polémica
sobre los "derechos" de la invención. Representante del racionalismo, para Leibnitz el criterio de
verdad del conocimiento no consiste en su adecuación a la realidad, sino en su intrínseco carácter
necesario. Con fundamento en los principios de razón suficiente y de identidad, formula la noción
de la mónada, entidad infinitesimal psicofísica, única, indestructible y dinámica, de la cual está
formada la realidad última; desde la creación, las mónadas están perfectamente sincronizadas, en
una armonía que les permite reflejar la realidad cambiante sin ser afectadas por las demás mónadas:
así es como la diversidad se explica mediante lo unitario. Es autor de un sistema lógico dedicado a
la mecanización del pensamiento y a la creación de un lenguaje universal (Characteristica
universalis: "para que la humanidad pueda tener un nuevo tipo de instrumento que aumente el valor
de la razón más allá de lo que cualquier instrumento óptico haya ayudado al poder de la visión"),
además de inventor —en Europa— del sistema binario.
Leonhard Euler, matemático suizo extraordinariamente fértil en descubrimientos y estudios.
Es uno de los autores más prolíficos de la historia de las matemáticas (su obra consta de 72 volúme-
nes), y quedó ciego a partir de 1771, aunque siguió trabajando mediante dictados. Alumno de
Johann Bernoulli, Euler inicia la época del análisis matemático y de la geometría analítica en tres
dimensiones. Es autor de la extraordinaria fórmula e'n + 1 = O , que relaciona las constantes más
importantes del conocimiento formal.
Johann Heinrich Lambert (1728-1777), físico, matemático y astrónomo alemán. Elaboró una
teoría del conocimiento, con influencia de la silogística, dividida en cuatro partes: sobre las leyes
formales del pensamiento; sobre los elementos simples del conocimiento; sobre la distinción entre
lo verdadero y lo falso; sobre las causas del error.
Bernhard Bolzano (1781-1848), filósofo y matemático austríaco. Consideraba a las proposi-
Leonhard Euler (1707-1783) ciones como poseedoras en sí de una realidad lógica desvinculada del ser pensante.
Sección 6.4 Anexo: Visión histórica de la lógica matemática 287

Euler contribuye a la lógica con unos diagramas que eran ilustraciones geométricas
de los silogismos; aunque estrictamente hablando no fueron de su invención, él fue quien
les dio un uso generalizado.
Lamben dedicó algunos ensayos a la empresa de crear un cálculo de la lógica, y tomó
un punto de vista diferente del de Aristóteles para tratar problemas relacionados con la
lógica. Utilizó las operaciones de multiplicación, división, suma y resta dentro de la lógi-
ca, aunque no tuvo mucho éxito en sus logros. Años después, Boole repetiría exitosamente
algunos de estos intentos.
Bolzano consideraba la lógica como una teoría acerca de la ciencia. Utilizó un lenguaje
semiformal de su invención para las investigaciones sobre lógica. Desde su punto de vista,
ésta trabaja con términos y proposiciones, que constituyen sus entidades fundamentales.
Una de sus ideas era asignar valores de validez a proposiciones obtenidas de sustituir
términos distintos en una proposición dada, para llegar así a una especie de tabla de ver-
dad que decía si la proposición era universalmente válida, universalmente contraválida, o
consistente.

Lógica del período de Boole


Los sistemas de lógica moderna son producto de la segunda mitad del siglo xix, fuera ya
de la influencia directa de Aristóteles. Aquí se encuentran varios autores importantes.
Augustus De Morgan (1806-1871), quien además desarrolló trabajos de álgebra y se
interesó por el aspecto educativo y pedagógico de las matemáticas, logró estudiar los silo-
gismos en su forma más general, como series de combinaciones de relaciones, hasta mos-
trar lo que llamaba la "forma pura" de un juicio.
En 1847 apareció el libro de George
Boole (1815-1864), Mathematical Ana-
lysis of Logic, destinado a abrir una nueva
época en el conocimiento y estudio de la
lógica. En esta obra, y en la posterior y
definitiva An Investigation on the Laws of
thought on which are founded the Mathe-
matical Theories of Logic and Probabi-
lities, probaba que las leyes del álgebra
pueden ser expresadas formalmente sin
necesidad de alguna interpretación particu-
lar. En ambos estudios representa lo que
llama "operaciones necesarias para el pen-
samiento" por medio de un álgebra sim-
bólica. Boole usa las letras x y y como
"símbolos electivos" para clases formadas
y seleccionadas de un universo de cosas.
Utiliza el símbolo 1 para denotar el uni-
verso o universo del discurso. 111) Lin IMMO. 00.11. IttlEt.
George Boole (1815-1864)
Toma de De Morgan el recurso de denotar la clase complementaria como 1-x. Mues
tra también el uso y las limitaciones de las operaciones de suma y multiplicación en el
álgebra de clases y sus propiedades distributiva y conmutativa.
El mismo Boole se da cuenta de que su sistema no se restringe a una interpretación de
clases, sino que también es válido para una interpretación más limitada de los valores
de las variables; por ejemplo, O y 1. Otra interpretación revela por vez primera la afinidad
entre la lógica de clases y el cálculo de proposiciones.
Será tarea de sus sucesores depurar el sistema para extraer algunos elementos extra-
ños (como las operaciones algebraicas de división y sustracción) que había introducido,
pero el sistema de la lógica moderna estaba ya creado.
288 Capítulo 6 Teoría matemática de la computación

El lógico y economista inglés William Stanley Jevons (1835-1882) es de los primeros


en acometer la tarea de revisar el álgebra recién creada y hace cambios y mejoras impor-
tantes que hoy día siguen inalterados. Crea un sistema (ya abandonado) de simplificación
de expresiones mediante combinación de variables y de búsqueda de inconsistencias en-
tre ellas. Inventa un ábaco lógico para resolver estos problemas; esta fue una de las primeras
máquinas que usaron variables lógicas.
El matemático inglés John Venn (1834-1923) hizo intentos por encontrar significa-
ción a las operaciones algebraicas de división y sustracción en el sistema de Boole, consi-
guiendo un mayor conocimiento de la teoría, no una utilización práctica de ellas. Es creador
de los diagramas que llevan su nombre y son representaciones gráficas de los procesos
algebraicos introducidos por Boole e ilustrados mecánicamente en el alfabeto de Jevons.
Logra así obtener los resultados buscados por este último al hacer sus combinaciones de
variables, pero de manera más sencilla y clara.
Lewis Carroll (1832-1898), cuyo verdadero nombre era Charles L. Dogson, escribió
Alicia en el país de las maravillas y A través del espejo. Matemático inglés, fue autor de
la importante observación acerca de la inferencia que dice: una ley que permite obtener
conclusiones de las premisas no puede ser tratada a su vez como premisa, pues se genera-
ría una regresión infinita. Recientemente se descubrió un libro suyo que lo hace aparecer
como un importante autor; hay una referencia a este libro en el artículo "Lewis Caroll's
Lost Book on Logic" de W. W. Bartley, publicado en la revista Scientific American, julio
de 1972.

Lógica moderna
Gottlob Frege (1848-1925), lógico y matemático alemán, uno de los más importantes
autores de la fundamentación de la aritmética, hizo el análisis lógico de la demostración
por inducción matemática. Fue contemporáneo del gran matemático italiano Giuseppe
Peano (1858-1932), y muchos de los conceptos y símbolos de ambos autores serán emplea-
dos para los desarrollos por venir.
Alfred North Whitehead (1861-1947) y Bertrand Russell (1872-1970), filósofos y
PRINCIPIA MATHEMATICA
matemáticos británicos, escriben entre 1910 y 1913 una de las obras máximas de la era
moderna, Principia Mathematica [WHIR67], donde tratan de demostrar que las matemá-
u71/60 Hoxn. WHITEHPAO, AL.
rt ticas están fundamentadas en la lógica y, a la vez, sistematizan todo el conocimiento de
lógica hasta esa fecha, bajo un enfoque conocido como logicismo, que desarrolla la lógica
.11.RAND PUS515.1.1,
partiendo de algunos axiomas y términos indefinidos, como proposición elemental, fun-
ción proposicional, negación de una proposición, etc., usando intensivamente la notación
desarrollada por Peano. Según los autores, la ventaja de la notación formal sobre el len-
VOLUME I
guaje común es porque "las ideas aquí empleadas son más abstractas que las normal-
mente consideradas en el lenguaje". La obra está contenida en tres volúmenes, y el
esfuerzo de crearla les ocasionó, en palabras del mismo Russell, "daño cerebral irrever-
sible". La definición del número 1 y sus propiedades, por ejemplo, ocupa las proposi-
Cvnbridu
a∎ rtlx Uninnrty Port, ciones *52 y *53, con un total de 12 páginas en nuestra edición abreviada. Esta es una
■ 9..
pequeña parte, para indicar que a es una clase unitaria si y sólo si no es nula y todos sus
miembros son idénticos:

*52.16. i—:.a€ 1 . m : 3 ! a : x, y € a. D,,,,.x=y

Sin embargo, la posición logicista de la obra ha sido criticada, y además el sistema


nunca fue completado y tiene partes oscuras pero, sin lugar a dudas, representa un enorme
avance en la ciencia de la lógica y el conocimiento.
Como una propuesta diferente del logicismo se encuentra la escuela formalista, de la
cual el matemático alemán David Hilbert (1862-1943) fue el exponente más importante.
Este autor es el principal axiomatizador de la geometría euclidiana y precursor, por tanto, de
Sección 6.4 Anexo: Visión histórica de la lógica matemática 289

las geometrías no euclidianas. En el capí-


tulo 1 se mencionó que John von Neumann
realizó su posdoctorado con Hilbert.
Parte fundamental de su tesis fue la
creación de una teoría de la prueba, cono-
cida también como metamatemática, cuyo
estudio profundo ha llevado a descubrir
la estructura interna y las limitaciones in-
herentes al sistema axiomático, del que se
hablará más adelante.
Después de Hilbert, en un sistema axio-
mático formal los elementos primitivos
(símbolos, términos) carecen de conteni-
do esencial explícito y sólo se usan como
piezas en un juego totalmente definido por
sus axiomas y sus reglas de inferencia.
Hilbert crea un "programa" que se ha
hecho famoso en la historia de las mate-
máticast. El objetivo era acabar definiti-
vamente con los problemas relativos a los David Hilbert (1862-1943)
fundamentos de esta ciencia. El programa estaba dividido en tres etapas: determinación
de los símbolos por emplear; formación de sucesiones finitas de ellos, que serán las ex-
presiones formales, y formación de sucesiones finitas de expresiones formales. Se crean

(f) En el Congreso Internacional de Matemáticos de París, en 1900, Hilbert presentó una conferen-
cia sobre "los problemas futuros de las matemáticas", con 23 problemas y áreas de investigación,
de los cuales a la fecha algunos sólo se han resuelto parcialmente; además se ha demostrado que
varios otros son indecidibles. Se pueden encontrar muchas referencias en la dirección Internet
http: / /mathworld.wolf ram com y la lista completa se halla en la dirección
http://alephO.clarku.edu/ - djoyce/hilbert/problems.html.
Esta es la lista de los problemas (junto con algunas de sus soluciones hasta la fecha):
1. Problema del número cardinal del continuo de Cantor. (Problema abierto y parcialmente
resuelto, conocido ahora como "la hipótesis del continuo".)
2. La compatibilidad de los axiomas aritméticos. (Son inconsistentes, como muestra el teo-
rema de Giidel.)
3. La igualdad de volúmenes de dos tetraedros de bases y alturas iguales. (No necesaria-
mente es así.)
4. El problema de la línea recta como la distancia más corta entre dos puntos. (Resuelto.)
5. El concepto de Lie sobre un grupo continuo de transformaciones, sin suponer la diferen-
ciabilidad de las funciones que lo definen. (Resuelto, entre otros, por von Neumann.)
6. Tratamiento matemático de los axiomas de la física. (Problema abierto.)
7. Irracionalidad y trascendencia de ciertos números. (Resuelto en 1934 para el caso de los
números algebraicos.)
8. Problemas de números primos. (La hipótesis de Riemann sigue abierta.)
9. Prueba de la ley más general de reciprocidad en campos de números. (Abierto.)
10. Posibilidad de resolución de ecuaciones diofantinas (demostrado como indecidible; véase
el artículo "Hilbert's Problem", de Martin Davis y Reuben Hersh, en Scientific Ame-
rican, noviembre, 1973.)
11. Formas cuadráticas con cualquier coeficiente numérico algebraico. (Abierto.)
12. Extensión del teorema de Kronecker sobre campos abelianos a cualquier dominio de
racionalidad. (En proceso.)
13. Imposibilidad de solución de la ecuación general de séptimo grado mediante funciones
de dos argumentos. (Abierto.)
290 Capítulo 6 Teoría matemática de la computación

así fórmulas (sucesiones de símbolos que guardan ciertas reglas) y teoremas del sistema,
que permiten obtener sucesiones de fórmulas de modo que cada una sea o bien una infe-
rencia lógica de fórmulas precedentes o bien un axioma. Esas sucesiones de fórmulas
constituirán así la demostración formal de la última fórmula de la sucesión completa.
Esta elaboración de un sistema formal ya estaba incluida en los Principia, pero la
diferencia reside en que en este caso se trata de un sistema axiomático existencial o for-
mal, mientras que en el anterior el sistema es material, en el sentido de que acude a ciertos
elementos lógicos como base de sustentación. Otros autores de la corriente formalista de
las matemáticas son Ackermann, Gentzen y von Neumann.
Es importante mencionar también la escuela intuicionista, cuyo principal exponente
fue el matemático holandés Jan Brouwer. Aquí se afirma que las matemáticas son el estu-
dio de cierto tipo de construcciones mentales, y la intuición de lo que esa construcción
representa no se puede reducir a otros conceptos más primitivos. El intuicionismo no
Jan Brouwer (1881-1966) considera a la lógica como fundamental para la creación de un sistema de las matemáticas,
y algunas distinciones que hace entre la forma de utilizar sus principios llevan al rechazo
del principio del tercero excluido cuando se tratan conjuntos con un número infinito de
elementos, pues se podría entonces llegar a considerar proposiciones posiblemente
indecidibles, y además no resulta posible "construirlos". Para el intuicionismo, en fin, las
ideas matemáticas son independientes de la vestidura ofrecida por el lenguaje o la lógica,
pues son más ricas que esos elementos externos. Entre los autores de esta escuela desta-
can, además, Kronecker, Poincaré y Weyl .
Arend Heyting (1898-1980) formuló en 1930 un sistema intuicionista formalizado
para la aritmética, y hay otros autores de esta corriente que incluso llegan a no admitir la
negación, por ejemplo. En una de sus conferencias, Hilbert tachó al intuicionismo como
"traición a la ciencia", pero debe reconocerse que, aun no siendo muy popular, el cálculo
intuicionista es consistente y, en cierto sentido, toda la matemática moderna comparte (a
sabiendas o no) algunos de los conceptos de esta escuela. Recomendamos el artículo
[ROTB97] como mínima fuente de documentación, además de los libros BOUN721, [DOUA74]
y [ KLIM72] , que son compendios muy amenos sobre filosofía de las matemáticas.
Al matemático polaco nacionalizado estadounidense Emil Post (1897-1954) se debe,
además de estudios centrales sobre teoría de computabilidad, la creación de una técnica
Jules Henri Poincaré de tablas de verdad empleada para el análisis de las definiciones de consistencia y com-
(1854-1912) pletitud del cálculo proposicional.

14. Prueba de finitud de ciertos sistemas completos de funciones de invariantes algebraicos.


(Abierto.)
15. Fundamentación rigurosa del cálculo enumerativo. (Abierto.)
16. El problema de la topología de curvas y superficies algebraicas. (Sigue abierto, aunque el
último teorema de Fermat fue resuelto en 1995.)
17. Expresión de formas de funciones racionales enteras definidas mediante cuadrados. (Abierto.)
18. Construcción de espacios a partir de poliedros congruentes. (Resuelto en 1997, para el
caso del "problema de empacar esferas".)
19. ¿Las soluciones de problemas regulares en el cálculo de variaciones son siempre necesa-
riamente analíticas? (Abierto.)
20. El problema general de los valores acotados. (Abierto.)
21. Prueba de la existencia de ecuaciones diferenciales lineales con grupos monodrómicos.
(Resuelto parcialmente en 1989.)
22. Uniformización de relaciones analíticas mediante funciones automórficas. (Abierto.)
23. Desarrollos adicionales de los métodos del cálculo de variaciones. (Abierto.)
El siguiente Congreso Internacional de Matemáticos (sin distinción de géneros, claro) —or-
ganizado por la Unión Matemática Internacional— será en Pekín, China, en agosto de 2002. Su
dirección Internet es http://www.cms.org.cn/icm01.htm.
Sección 6.4 Anexo: Visión histórica de la lógica matemática 291

Otro autor que descubrió paralela-


mente el método de las tablas de verdad
fue el filósofo y lógico austríaco Ludwig
Wittgenstein, considerado como uno de los
pensadores más importantes del siglo XX.
En su obra central, Tractatus Logi-
co-Philosophicust , reduce la lógica de
predicados y las matemáticas a un sistema
de cálculo proposicional. Su preocupación
acerca de un lenguaje ideal lo lleva luego
a realizar estudios de lógica y matemáti-
cas considerándolas fenómenos naturales
del lenguaje (durante lo que se conoce
como su "segunda etapa"). Parte de los
principios del simbolismo y de las relacio-
nes necesarias entre las palabras y las cosas
en cualquier lenguaje para mostrar cómo la
lógica y la filosofía tradicionales cometen Ludwig Wittgenstein
equívocos por el deficiente uso del lengua- (1889-1951)
je. En la primera parte de su estudio comienza la construcción de un lenguaje lógico a
partir de lo que llama el "hecho atómico", aquel que no tiene partes que sean hechos. Una
proposición será, entonces, una función de verdad de proposiciones atómicas. Este
constructivismo que parte de los hechos atómicos coloca a Wittgenstein cerca de las posi-
ciones intuicionistas en la filosofía de las matemáticas.
Rudolph Camap (1891-1970), filósofo y lógico alemán del llamado "Círculo de Viena"
(formado en tomo a la filosofía de Wittgenstein), extendió las técnicas de la lógica moder-
na a la epistemología, la física y la filosofía de la ciencia.
Alonzo Church (1903-1995), estadounidense, autor de un importante texto de lógica,
fue un investigador en problemas de la computabilidad y creador de un esquema formal
equivalente al de la máquina de Turing.
Es importante mencionar la escuela de lógicos polacos, entre quienes destacan Alfred
Tarski (1901-1983) y Jan Lukasiewiczt t (1878-1956), autores de sistemas lógicos forma-
lizados.
Existen muchos otros autores que han contribuido a hacer de la lógica una ciencia
viva y abierta a la investigación, y se pueden mencionar los nombres de Frank P. Ramsey
(1903-1930), revisor y crítico de los Principia, y particularmente de la teoría de los tipos
allí contenida (aunque sólo vivió 27 años); Leopold Lowenheim (1878-1957) y Thoralf
Skolem (1887-1963), autores en el cálculo de predicados y teoría de modelos; Gerhard Gent-
zen (1909-1945), creador de un método para derivación de resultados sobre indecidibili-
dad y de una prueba de consistencia; Willard van Orman Quine (n. 1908), autor de libros
sobre lógica, conocido también por un método de minimización de funciones, y Henry M.
Sheffer, creador de una conectiva del cálculo lógico (NAND) que lleva su nombre.
La figura más importante de la lógica matemática moderna sin duda es el matemático
austríaco, nacionalizado estadounidense, Kurt Godel (1906-1978), cuyos trabajos sobre
teoría de conjuntos, teorema de incompletitud y teorema de la imposibilidad de formali-

(t) Traducida al español (con el mismo título) por Alianza Editorial, Col. Alianza Universidad,
núm. 50, Madrid, 1973.
(t t) Lukasiewicz, ya mencionado como sistematizador de la lógica aristotélica, es el creador de la
"notación polaca" sufija y prefija para la aritmética, empleada con mucho éxito en las calculadoras
programables avanzadas.
292 Capítulo 6 Teoría matemática de la computación

zación de una prueba de consistencia para


el sistema de la aritmética pusieron lími-
tes a los métodos axiomáticos; límites que
muy pocos matemáticos sospechaban
que pudieran existir. Su principal teore-
ma, que lleva el título " Sobre sentencias
formalmente indecidibles de Principia
Mathematica y sistemas afines", de 1931
(conocido simplemente como el teorema
de Gódel), prueba que los sistemas forma-
les de las matemáticas son incompletos,
puesto que puede encontrarse en ellos sen-
tencias imposibles de deducir a partir de
los axiomas (ni ellas ni sus negaciones, lo
cual les da justamente el carácter de indeci-
dibles); además, Gódel demostró que no
puede probarse la consistencia de un siste-
ma formal, y esto echó por tierra el progra-
ma de Hilbert, al mostrar que es imposible
construir sistemas formales completos y con-
Kurt Gódel (1906-1978)
sistentes. En la referencia [ HEMN691 se in-
cluye una explicación de las principales características de la demostración, y en [ GODK81 ]
aparece el teorema completo; además, en [WANH95] se presentan extensamente sus datos
biográficos.
Así, la lógica matemática puede dividirse en dos etapas irreconciliables: antes de
Gódel (cuando se esperaba algún día culminar con el programa de Hilbert) y después
de su trabajo, donde ya se sabe que esto no puede ser.
Estos resultados de 1931 se verían correspondidos con los obtenidos por Turing cinco
arios después, al mostrar que también existen límites inherentes a la computabilidad y, por
tanto, a nuestra capacidad (hasta entonces considerada ilimitada) de análisis matemático
y formal.

Algunas definiciones
La lógica de predicados trata de expresiones, su validez y su forma. Al estudiar la estruc-
tura interna de las expresiones tiene que vérselas con los cuantificadores y con las conectivas
sentenciales. Trata, entonces, del análisis de expresiones simples y de la teoría de las
propiedades lógicas de los cuantificadores. Tiene una porción elemental, llamada lógica
de predicados de primer orden; ésta es la lógica elemental moderna. Se llama "de primer
orden" porque los cuantificadores se aplican solamente a elementos individuales y no a
clases de elementos.
Dentro de la lógica de predicados se encuentra la llamada lógica sentencial, que trata
de expresiones simples y de los compuestos que se pueden hacer con ellas. Es en esta
parte de la lógica donde se emplean las tablas de verdad y las funciones de verdad. Se
entiende la noción de verdad lógica en el estrecho sentido permitido por las tautologías
(proposiciones que siempre son ciertas) dentro del sistema de tablas de verdad. Como
existe un método algorítmico para determinar las tautologías (por medio de las tablas de
verdad), entonces la lógica sentencial es decidible. Un argumento en lógica sentencial es
válido si la condicional que corresponde a ese argumento es una tautología.
Sin embargo, la lógica elemental generalizada (que toma en cuenta también a los
cuantificadores y sus conectivas) es indecidible: no existe un procedimiento de decisión
para la validez de los argumentos en la lógica de primer orden. Con las limitaciones pro-
pias a su indecidibilidad, un argumento es válido si su correspondiente condicional es un
Sección 6.5 Anexo: Visión de un libro fundamental, la obra de George Boole 293

teorema del cálculo de primer orden; o sea, si es una fórmula bien formada, válida en la
lógica de primer orden.
Vienen después las nociones modernas de sintaxis y semántica y sus derivados, como
consistencia, interpretación, satisfacibilidad, consecuencia, validez, etc. La sintaxis con-
sidera ciertas relaciones entre símbolos, sin acudir a sus significados, mientras que la
semántica considera las relaciones entre los símbolos y los objetos a los cuales se aplican.
Describir la sintaxis de un lenguaje formal L es especificar sus símbolos y reglas de
formación.
Describir sintácticamente un sistema formal S es especificar, además, su aparato de-
ductivo.
Dotar a L de una semántica es darle una interpretación que asigna significados a sus
símbolos y expresiones.
Si para una interpretación dada / del lenguaje L los axiomas de un sistema formal S
(en términos de L) son válidos, entonces se dice que la interpretación / constituye un
modelo para S.
Todo esto es la base para la llamada metalógica, que responde en parte a una antigua
pregunta de Platón sobre la existencia de una ciencia de la ciencia (diálogo "Carmides o
de la templanza" [ PLAT73] ).

En general, se dice que una teoría formal es decidible si existe un algoritmo que
permita averiguar si una fórmula cualquiera de la teoría es formalmente demostrable o no.
Los axiomas de un sistema formalizado se llaman completos si todo enunciado verdadero
expresable en el sistema es formalmente derivable de ese conjunto de axiomas Por últi-
mo, un sistema axiomático se llama consistente si no implica enunciados contradictorios.
Dentro de la lógica llamada no elemental se encuentran los cálculos de predicados de
segundo orden en adelante, las teorías de clases, la teoría de funciones recursivas, y la
teoría de modelos. Los cálculos de orden superior al primero son necesarios para cubrir
los campos de cuantificación sobre clases y sobre clases de clases. Se construyen aña-
diendo al lenguaje formal de la lógica de primer orden notaciones para distinguir entre
tipos de variables y órdenes de estos tipos y, además, variando las leyes de formación y el
aparato deductivo del sistema.
También hay estudios sobre lógicas multivalentes; aquéllas donde los valores de ver-
dad de las variables lógicas no se restringen a dos (verdadero, falso), sino que se permiten
valores adicionales. Estos tipos de lógica cumplen objetivos muy específicos, y algunas
son desarrolladas sobre pedido; éste es el caso de una lógica polivalente empleada en la
física cuántica, donde son necesarios varios niveles de verdad, correspondientes a algu-
nos grados de libertad particulares en el estudio especializado del campo subatómico.
át1 INVESTIGATION

6.5 ANEXO: VISIÓN DE UN LIBRO FUNDAMENTAL, THE LAWS OF THOUGHT,

LA OBRA DE GEORGE BOOLE momo

THS 11ÁTILERATIO/L MEMO OF


PROBABILIIIII.
Como información histórica resulta interesante presentar un resumen del libro de Boole
que lleva el enorme título de Una investigación de las leyes del pensamiento en que están
fundamentadas las teorías matemáticas de la lógica y de las probabilidades, escrito en
1854. Las referencias son de la edición facsimilar (en inglés) publicada por la editorial 01101:113 BooLF, LL,D.

Dover, Nueva York, 1958.


Comienza por dar una definición de lo que debe entenderse por signo:

Un signo es una marca arbitraria, con una interpretación fija, y es susceptible WALTOP • /A IMMIILRY,
• Vi 9999 Y

de combinarse con otros signos, sujetándose a ciertas leyes fijas supeditadas a .01/ILLÁN kW CP

sus mutuas interpretaciones.


294 Capítulo 6 Teoría matemática de la computación

Y luego lo explica de esta manera:

Proposición I: Todas las operaciones del lenguaje como instrumento del razona-
miento pueden ser dirigidas por un sistema de signos compuesto por los siguien-
tes elementos:

1. Símbolos literales, como x, y y c, que representan cosas sujetas a nuestras


concepciones.
2. Signos de operación, como +, x, para aquellas operaciones de la mente
por medio de las cuales las concepciones de las cosas se combinan para for-
mar nuevos conceptos implicando los mismos elementos.
3. El signo de identidad, =.

Estos símbolos de lógica se usan sujetos a leyes definitivas, parcialmente concor-


dando y parcialmente difiriendo de las leyes para los símbolos correspondientes
en la ciencia del álgebra.
La ley expresada por xy = yx puede caracterizarse diciendo que los símbo-
los literales x, y, z, son conmutativos, como los símbolos del álgebra. Y como
la combinación de dos símbolos literales en la forma xy expresa la totalidad
de esa clase de objetos para los cuales los nombres o cualidades representados
por x y y son aplicables conjuntamente, se sigue que si los dos símbolos tienen
exactamente la misma significación, su combinación no expresa más que lo que
cualquiera de los dos tomados por separado diría. En tal caso deberíamos
entonces tener

xy = x

pero como se supone que y tiene el mismo significado de x, podemos reempla-


zarla en la ecuación por x, y tener

XX = X

Ahora bien, en el álgebra común, la combinación xx se representa más breve-


mente como x 2 .

De acuerdo con esta notación, entonces, la anterior ecuación adquiere la forma

X2 = x

que es, en realidad, la expresión de una segunda ley general acerca de aquellos
símbolos por medio de los cuales representamos simbólicamente nombres, cua-
lidades o descripciones. (Pág. 31)
Signos de aquellas operaciones mentales por medio de las cuales reunimos
partes en un todo, o separamos un todo en sus partes. [...] Estrictamente, las
palabras y y o, interpuestas entre los términos descriptivos de dos o más clases
de objetos, implican que esas clases son diferentes, de tal manera que ningún
miembro de una se encuentra en la otra. En éste y en todos los respectos, las pala-
bras y y o son análogas al signo + en álgebra; y sus leyes son idénticas. [...] Pero
no podemos concebir posible recolectar partes en un todo, sin concebir como posible
también el separar las partes del todo. Esta operación se expresa en lenguaje
común con el signo excepto. Como hemos expresado la operación de agregar con
el símbolo +, así también podremos expresar la operación antes descrita con el
símbolo —. (Pág. 33)
Sección 6.6 Anexo: Elementos de lógica proposicional 295

Más adelante dice

Proposición II: Determinar el valor lógico y la significación de los símbolos O y 1.


El símbolo O, tal como se usa en álgebra, satisface la siguiente regla formal
O . y = O, o bien Oy = O,
para cualquier número que y pueda representar. Esta ley formal puede ser obe-
decida en el sistema de la lógica; debemos asignar al símbolo O una interpreta-
ción tal que la representada porOy pueda ser idéntica con la clase representada
por O, sin importar cuál sea la clase y. Una breve consideración mostrará que
esta condición se satisface si el símbolo O representa nada. De acuerdo con una
definición anterior, podemos hacer de nada una clase. De hecho, nada y univer-
so son los dos límites de la extensión de clases, puesto que son los límites de las
posibles interpretaciones de los nombres en general, ninguno de los cuales pue-
de relacionar menos individuos de los que incluye nada, o más de los que incluye
universo.
Proposición III: Si x representa cualquier clase de objetos, entonces 1-x
representará la clase contraria o suplementaria de objetos. Por ejemplo, la cla-
se que incluye aquellos objetos que no están comprendidos dentro de la clase x
(Págs. 47-48).

Inmediatamente después analiza el principio de contradicción, que afirma la impo-


sibilidad de que algún ente posea una cualidad y al mismo tiempo no la posea; e indica
que esto es una consecuencia de aquella ley fundamental del pensamiento cuya expre-
sión es x2 = x.
Posteriormente pasa al análisis de las proposiciones y su división de acuerdo con los
cuantificadores. Las separa en dos grandes grupos: primarias o concretas y secundarias o
abstractas.
En el siguiente capítulo, trata las funciones lógicas del tipo f(x), donde x es un símbo-
lo en el sentido previamente definido, y en los otros capítulos habla de la eliminación, de
la reducción, de métodos para abreviar funciones, de sus aplicaciones etc. La base ha sido
puesta ya y la lógica entra en una etapa nueva, luego de más de 2000 años de existencia.

6.6 ANEXO: ELEMENTOS DE LÓGICA


PROPOSICIONAL

Aquí se hará una breve introducción al tema específico del cálculo (o lógica) proposicional, (((Va) Bx A Fa)
que es una interpretación del álgebra desarrollada por George Boole. Para ello se darán
inicialmente varias definiciones. (Vy) Gya)
Una variable es un término que no posee significado por sí mismo. Cuando en una
expresión que contiene variables se sustituyen éstas por constantes (términos con significa-
do preciso) determinadas, se convierte en una proposición, o función proposicional, con
la característica de tener uno y sólo uno de los valores permitidos por el sistema lógico en
cuestión. Como se trata de un álgebra con dos elementos constantes, entonces una propo-
sición puede tomar únicamente dos valores: verdadero o falso, pero no ambos a vez. Es
necesario mencionar también (explícita o implícitamente) a los cuantificadores, que con-
vierten una expresión con variables en una proposición.
Si p denota una proposición, entonces hay dos casos posibles:

1) p es verdadera
2) p es falsa
296 Capítulo 6 Teoría matemática de la computación

y si p es verdadera entonces no es falsa, y viceversa. Esos son los valores de verdad de la


variable p.
En el ejemplo anterior, p fue utilizada como variable y también como proposición, de
una manera general.
Se define la negación de p, denotada -p, como aquella proposición que es verdadera
cuando p es falsa, y viceversa.
Claramente, el operador "la negación de" (--) es unario; esto es, su campo de acción
está limitado a una sola fórmula proposicional (que bien puede ser una variable aislada,
como en el ejemplo).
Se mencionó ya una notación tabular muy común, llamada tabla de verdad para ejem-
plificar en forma compacta todos los posibles casos en que se utiliza tal o cual operador.
La tabla de verdad para la negación es la siguiente:

V
F V

Se define la conjunción de dos variables o proposiciones, p y q denotada p n q, como


aquella que es verdadera cuando ambas, p y q, son verdaderas; y falsa en caso contrario.
En el entorno de los lenguajes de programación, la conjunción se conoce como la opera-
ción lógica AND.
La disyunción de p y q se denota p v q y se define como falsa cuando ambas, p y q,
son falsas; y verdadera en caso contrario. Hay que notar que se utiliza la versión completa
de la disyunción, es decir, p y q significa "p o q, o ambas". Esto se conoce como "o
inclusivo", que en latín se llama vel, de donde proviene el símbolo v. En el entorno de los
lenguajes de programación, la conjunción se conoce como la operación lógica OR.
Las tablas de verdad de los operadores binarios de conjunción y disyunción son

Conjunción Disyunción
Pq pAq pq pvq
VV V VV V
VF F VF V
FV F FV V
FF F FF F

Desde otro punto de vista, se está hablando de aplicaciones (mapeos) del producto carte-
sianot S X S en S, donde S es el conjunto dotado de dos elementos: (V,F) o bien (1,0).
S X S tiene cuatro elementos: (V, V), (V, F), (F, V) y (F, F). Habrá entonces 2 4 . 16
aplicaciones de S X S en S, porque cada uno de los cuatro elementos de S X S se aplica a los
dos elementos de S, y a cada una de las aplicaciones se asigna uno de los valores, F o V.
Entre las 16 posibles aplicaciones se encuentran las mencionadas hasta ahora, junto
con varias otras muy útiles; hay algunas, finalmente, que son poco empleadas.
Existen 10 conectivos binarios que, sumados a las dos variables (en dos casos espe-
ciales llamados tautología y contradicción) y a las dos variables negadas, dan el total
de 16 funciones.

(f) Prácticamente todos los textos de álgebra y matemáticas incluyen la definición del producto car-
tesiano (así nombrado en honor del gran filósofo y matemático francés René Descartes). Recomenda-
René Descartes (1596 1650)
- mos especialmente [RICR83].
Sección 6.6 Anexo: Elementos de lógica proposicional 297

Una de ellas es la conocida con el nombre NOR (not or), también llamada operador de
Peirce, en honor del lógico y filósofo estadounidense Charles Sanders Peirce (1839-1914),
denotada como p q, y que se lee "ni p ni q"; es de particular importancia en los circuitos
lógicos empleados para el diseño de computadoras.
La función NAND (not and), también llamada operador de Sheffer, se denota como
p 1q y se lee "no es el caso que p y q"; es de gran importancia también en diseño de
circuitos lógicos.
La implicación material de p y q, denotada como p —> q, es otra de esas 16 funcio-
nes, y se lee "si p entonces q". De ella se derivan varias concepciones, tales como las de
condición necesaria y condición suficiente.
Por ejemplo, en la frase "si nieva entonces me da frío", en realidad se está haciendo la
implicación de un antecedente p ("nieva") y de un consecuente q "me da frío". Se dice
que es condición suficiente que nieve para que me dé frío, pero no es necesario que nieve
para tenerlo; puedo tener frío por alguna otra causa. Es decir, la notación p —> q también
indica que "p es condición suficiente para q".
Desde el otro lado, también resulta cierto que "q es condición necesaria para p".
La unión de las dos declaraciones anteriores resultaría en algo como "si nieva me da
frío"' y "si me da frío quiere decir que nieva", o más simplemente como "me da frío si, y
sólo si, nieva". Se dice entonces que p es condición necesaria y suficiente para q. Ésta es
la bicondicional o equivalencia, p H q, abreviada como "ssi" (si y sólo si) o iff: if and
only if, en inglés. En condiciones normales la proposición "me da frío ssi nieva" es falsa,
porque ya hace frío aún antes de comenzar a nevar, pero la proposición "hoy es martes ssi
ayer fue lunes" sí es siempre cierta.
Esto lleva a dos aplicaciones más de particular trascendencia: la tautología y la con-
tradicción.
La tautología es siempre verdadera, sin importar las particularidades o estado lógico
de sus componentes, mientras que la contradicción es siempre falsa, para toda combina-
ción de sus componentes. En la obra del gran lógico Ludwig Wittgenstein ya mencionada,
Tractatus Logico Philosophicus, se lee:

(4.464) La verdad de la tautología es cierta; la de las proposiciones, posible; la


de las contradicciones, imposible.
(5.143) La contradicción se oculta, por así decirlo, fuera de todas las proposicio-
nes; la tautología, dentro.

Es interesante saber, además, que bastan algunas funciones de todas las posibles para
poder definir las restantes. Por ejemplo, a partir de las funciones I y - es posible definir
todas las restantes.
En la práctica es necesario trabajar y construir funciones de varias variables relacio-
nadas entre sí mediante cadenas de operadores lógicos. Surge entonces el problema de
saber —dada una fórmula cualquiera— si está construida correctamente; si puede tener
algún sentido. Esto ya se trató cuando se abordó el tema de la computabilidad.
El estudio del cálculo proposicional continúa con los diversos métodos para defini-
ción y simplificación de funciones compuestas, y sirve como base conceptual para la
creación de circuitos electrónicos que se comportan de acuerdo con las tablas de verdad
especificadas, empleados como elementos constructivos en el diseño de las computadoras.
En el anexo 5.10 se mencionó un lenguaje de programación (Prolog) cuya filosofía
está basada en el cálculo proposicional, con el cual es relativamente fácil construir y
probar expresiones lógicas complejas, cosa que se dificulta un tanto con los demás len-
guajes. En el artículo [ KOSI93] se describe un tipo especial de lógica, llamada "difusa"
(fuzzy logic), que utiliza variaciones continuas entre los dos estados de verdad del cálculo
proposicional y se está volviendo de uso práctico en aplicaciones computacionales.
298 Capítulo 6 Teoría matemática de la computación

EJERCICIOS

1. Turing propuso una "prueba" para determinar si una computadora es inteligente o


no, consistente en establecer un diálogo, empleando un teclado, con un interlocu-
tor invisible que no se sabe si es una máquina o una persona. Si mediante este
procedimiento no resulta posible averiguar si quien contesta es uno de nosotros,
entonces allí hay inteligencia. Su definición de esta prueba se puede encontrar en
la referencia [ PYLZ75 citada en el capítulo 1.
],

Organice una discusión documentada sobre esto. Considere, entre otros, el


programa ELIZA, de 1964, que simula una sesión psicoanalítica que ha logrado
engañar a muchos, aunque su autor se sorprende por ello, pues el programa es muy
primitivo y esquemático; existe mucho material sobre ELIZA (en la revista Scientific
American, en [PYLZ75] y en casi cualquier otro lugar donde se hable de los inicios
de la inteligencia artificial); se recomienda el libro de su creador, Joseph Weizenbaum,
Computer Power and Human Reason, W.H. Freeman, San Francisco, 1976, ade-
más de las referencias citadas en la sección 5.8 de este libro.
2. Diseñe una gramática que produzca palindromas binarios. (Un palindroma binario
es una cadena que se lee indistintamente de izquierda a derecha o al revés, formada
por símbolos de dos tipos únicamente. Por ejemplo, 010 , 0101010 y 00000100000
son palindromas binarios, así como 0, 1, 11, 00, etc., que son casos triviales.)
3. La demostración de que una gramática G produce un lenguaje L tiene dos partes.
En la primera se muestra que esa gramática sólo puede producir cadenas con la
estructura del lenguaje L que se propone, y en la segunda se debe demostrar que,
dada una cadena perteneciente al lenguaje L, ésta debe ser producto de la gramáti-
ca G. Es decir, hay que demostrar que la gramática produce ese lenguaje y demos-
trar también que el lenguaje debe ser producto de esa gramática, o sea, que están
hechos el uno para el otro.
Demuestre que la gramática ya mencionada G = ({S }, {0, 1 }, P, S), donde P
consta de las reglas
1. S --> 0S1
2. S --> 01
produce el lenguaje Onln (n >= 1), es decir, cadenas formadas de un grupo de ceros
seguido del mismo número de unos, y nada más.
4. Demuestre que la gramática obtenida en el problema 2 produce todos los palin-
dromas binarios, y nada más.
5. ¿De qué tipo, dentro de la jerarquización de Chomsky, será la gramática de los
llamados lenguajes naturales (español, inglés, etc.)? ¿Por qué?
6. El problema de la traducción automática de un idioma a otro ha sido siempre de
mucho interés práctico, porque sería muy deseable disponer de un programa
de computadora que recibiera como entrada textos en un idioma y produjera como
resultado ese mismo texto, ya traducido. Sin embargo, esto no ha sido posible, y
existen razones poderosas, no sólo técnicas, para impedirlo, al menos si se habla de
traducciones completas y de calidad.
Haga una investigación sobre el tema y considere, por un lado, la referencia
[GRAF72], que habla acerca de la imposibilidad de lograr traductores completos y,
por otro, la disponibilidad de sistemas profesionales de traducción (por ejemplo,
los de la universidad de Utah y otros para computadoras personales), además del
ya citado número de enero de 1996 de la revista Communications of the ACM,
dedicado a la comprensión del lenguaje natural. ¿No es esto una contradicción?
Palabras y conceptos clave 299

7. Consideraciones sobre el concepto de negación en lógica: si llevo monedas en la


bolsa puedo decir que "tengo dinero", pero ¿cuál de las siguientes dos aseveracio-
nes es la formalmente correcta para el caso de que deba diez pesos y por qué?:
A) "No tengo dinero"
B) "Tengo menos diez pesos en la bolsa"
8. ¿Es lo mismo terminar un proceso de decisión con la respuesta "no", que no termi-
nar un proceso? Aclare.
9. En el texto se dijo que un problema indecidible no es aquel cuya representación no
termina, sino más bien uno del cual se pueda demostrar que no tiene representa-
ción en términos algorítmicos. ¿Se aplicará esto al siguiente programa en ensam-
blador que nunca termina?:
OTRAVEZ: BR OTRAVEZ
10. (Para lectores interesados en filosofía) El hecho de que existan limitaciones en las
capacidades de la máquina universal de Turing tiene implicaciones profundas, que
van más allá de las estrictamente computacionales y entran de lleno en el terreno
de la filosofía de las matemáticas y de la ciencia en general. En 1931 Gódel publi-
có su famoso teorema, que demuestra la imposibilidad de conseguir un cálculo
suficientemente rico en el que todos los enunciados y teoremas sean decidibles
dentro del sistema, lo cual contribuyó al derrumbe del concepto de los esquemas
axiomáticos como pilares sobre los que pueden construirse sistemas de conoci-
miento completos desde un punto de vista matemático.
Haga un estudio comparativo de las consecuencias que para la teoría del cono-
cimiento tienen las limitaciones expuestas por la máquina de Turing y por el teore-
ma de Giidel.

PALABRAS Y CONCEPTOS CLAVE

En esta sección se agrupan las palabras y conceptos de importancia estudiados


en el capitulo, para que el lector pueda hacer una autoevaluación consistente en
describir con cierta precisión el significado de cada término, y no sentirse satisfe-
cho sin haberlo logrado. Se muestran en el orden en que se describieron o se
mencionaron.
COMPUTABILIDAD
MODELO
ALGORITMO
MÁQUINA DE TURING
COMPUTACIÓN DE UN PROCESO
FUNCIÓN
ESTADOS
PROCESO COMPUTABLE
PROBLEMA INDECIDIBLE
MÁQUINA UNIVERSAL
HALTING PROBLEM
PROCEDIMIENTO EFECTIVO
COMPLEJIDAD
LENGUAJE FORMAL
ALFABETO
FRASE
CADENA VACÍA
300 Capítulo 6 Teoría matemática de la computación

LENGUAJE
GRAMÁTICA FORMAL
ESTRUCTURA DE UN LENGUAJE
JERARQUÍA DE CHOMSKY
GRAMÁTICA REGULAR
GRAMÁTICA SENSIBLE AL CONTEXTO
GRAMÁTICA INDEPENDIENTE DEL CONTEXTO
AUTÓMATA/RECONOCEDOR
LÓGICA MATEMÁTICA
CÁLCULO PROPOSICIONAL
CONTRADICCIÓN
TAUTOLOGÍA

REFERENCIAS PARA
[BOUN72] Bourbaki, Nicolás, Elementos de historia de las matemáticas, Alianza
EL CAPÍTULO 6
Editorial, Col. Alianza Universidad, núm. 18, Madrid, 1972.
Un anónimo grupo de matemáticos franceses decidió recopilar con
detalle la historia de las matemáticas y describirla desde una perspecti-
va formal. El autor Bourbaki en realidad no existe, es un seudónimo co-
lectivo. Se han publicado ya varios volúmenes, y este libro es la traducción
de un compendio de los primeros resultados.
[BROG93] Brookshear, Glenn, Teoría de la computación, Addison-Wesley, México, 1993.
Uno de los muy pocos libros sobre computabilidad traducido al espa-
ñol. En un conciso y especializado volumen, el autor presenta los princi-
pales conceptos de la teoría matemática de la computación y analiza con
bastante detalle los lenguajes formales, los autómatas y reconocedores,
la máquina de Turing y los temas fundamentales sobre computabilidad y
complejidad de algoritmos.
[CHON57] Chomsky, Noam, Syntactic Structures, Mouton & Co., La Haya, Holanda,
1957.
Una de las primeras obras de este prolífico autor de lingüística, lin-
güística matemática y ciencias sociales. Este libro es "un intento de
construir una teoría formalizada general de las estructuras lingüísticas, y
de analizar su fundamentación". Propone aquí su método de análisis
basado en la estructura de las frases y las reglas de gramática transfor-
macional.
Existe traducción al español.
[COHD91] Cohen, Daniel, Introduction to Computer Theory, Wiley, Nueva York, 1991.
Edición revisada de uno de los pocos libros para principiantes dedi-
cado por completo a la teoría de la computación. Está dividido en tres
partes: teoría de autómatas, teoría de autómatas de pila (un tipo espe-
cial de autómatas, más generales), y "teoría de Turing". Bonito texto que
resulta una completa y accesible fuente de teoremas y resultados de
computabilidad.
[DOUA74] Dou, Alberto, Fundamentos de la matemática, Nueva Colección Labor,
núm. 117, Barcelona, 1974.
Traducción de un interesante y accesible libro sobre filosofía mate-
mática. En él se describen algunas de las ideas que se han desarrollado
acerca del porqué de las matemáticas, y se hace un recuento de las
principales características de las escuelas de pensamiento matemático.
[DUNW90] Dunham, William, Journey Through Genius: The Great Theorems of
Mathematics, Wiley & Sons, New York, 1990.
Extraordinario y bello libro de divulgación matemática. Ofrece una
visión sobre doce teoremas fundamentales en donde combina los aspec-
Referencias para el capítulo 6 301

tos formales de la demostración con anécdotas y referencias históricas y


sociales sobre sus creadores. Trata desde un teorema del año 440 a.C.
(cuadratura de figuras geométricas por Hipócrates) hasta el teorema de
Cantor sobre los transfinitos, de 1891, pasando por Euclides, Arquímedes,
Newton, los Bernoulli y Euler.
[GODK81] Gódel, Kurt, Obras completas, Alianza Editorial, Col. Alianza Universidad,
núm. 286, Madrid, 1981.
Recopilación y traducción en un solo volumen de la obra completa
de Gódel, que incluye sus teoremas fundamentales y muchos otros tra-
bajos, todos ellos cortos, absolutamente densos y de altísimo nivel ma-
temático. Se incluye un comentario de los traductores como introducción
a cada trabajo.
[GRAF72] Gracia, Francisco (ed.), Presentación del lenguaje, Taurus, núm. 89, Ma-
drid, 1972.
Compendio de traducciones de artículos sobre lingüística y aspec-
tos de teoría de lenguajes. Contiene un largo artículo de Chomsky, y uno
titulado "Una demostración de la impracticabilidad de traducciones com-
pletamente automáticas y de alta calidad", escrito en 1960 por el inves-
tigador Yehoshua Bar-Hillel, pionero en la formalización de la lingüística.
[HEMN89] Hempel, C., E. Nagel, J. Newman, et al., Matemática, verdad y realidad,
Grijalbo, Barcelona, 1969.
Traducción de un libro que recoge artículos descriptivos e introduc-
torios sobre la función y la filosofía de las matemáticas escritos por va-
rios autores, entre ellos los conocidos filósofos de la ciencia Carl Hempel
y Ernest Nagel. Se incluye una accesible explicación completa sobre el
teorema de Gódel.
[H0PJ84] Hoperoft, John, "Turing Machines", en Scientific American, septiembre, 1984.
Interesante artículo sobre la máquina de Turing y los problemas de
la computabilidad y los sistemas formales, tratado en forma introductoria
por este autor, autoridad en el campo, y coautor también de numerosos
textos sobre teoría de la computación y algoritmos, varios de los cuales
se citan en este libro.
Existe traducción al español.
[HOPU93] Hoperoft, John y Jeffrey Ullman, Introducción a la teoría de autómatas,
lenguajes y computación, CECSA, México, 1993.
Traducción de una obra de 1979, que comienza diciendo: "Hace diez
años los autores produjeron un libro que cubría el material conocido so-
bre lenguajes formales, teoría de autómatas y complejidad computacional.
Visto retrospectivamente, sólo algunos resultados importantes se omi-
tieron en sus 237 páginas. Al escribir un nuevo libro sobre estos temas
nos encontramos con que el campo se ha ampliado en tantas nuevas
direcciones que es imposible tratarlas a todas de manera exhaustiva".
[KLIM72] Kline, Morris, Mathematical Thought from Ancient to Modem Times, Oxford
University Press, Nueva York, 1972.
Concisa enciclopedia en la que se hace un recuento del saber ma-
temático de la humanidad; tarea enorme que este reconocido autor de
matemáticas lleva a cabo de una forma agradable y muy legible.
[KOSI93] Kosko, Bart y S. lsaka, "Fuzzy Logic", en Scientific American, julio, 1993.
Artículo sobre un tipo especial de lógica, llamada "difusa", empleada
para diseñar algoritmos con un comportamiento más cercano a las impreci-
siones y complejidades del mundo real que lo permitido directamente
por el álgebra booleana.
Existe traducción al español.
[LEWP78] Lewis, Harry y Christos Papadimitriou, "The Efficiency of Algorithms", en
Scientific American, enero, 1978.
302 Capítulo 6 Teoría matemática de la computación

Artículo dedicado a describir y explorar en forma sencilla la comple-


jidad de los algoritmos computacionales y algunas de sus implicaciones
matemáticas profundas, como las hasta la fecha desconocidas relacio-
nes entre la familia de complejidad polinomial, P, y la de complejidad
polinomial no determinística, NP.
Existe traducción al español.
[MA0E87] Maor, Eli, To lnfinity and Beyond: A Cultural History of the Infinite, Princeton
University Press, New Jersey, 1987.
Muy interesante y bien documentado libro de divulgación escrito por
un doctor en matemáticas israelí. Dividido en cuatro partes: El infinito
matemático, el infinito geométrico, el infinito estético y el infinito cosmo-
lógico, en casi 300 páginas ilustra las principales ideas y conceptos so-
bre lo infinito en matemáticas, e incluye ilustraciones del artista holandés
Maurits Escher (1898-1972), quien se autodenominaba ignorante en
matemáticas.
[MINM67] Minsky, Marvin, Cómputation: Finite and Infinite Machines, Prentice-Hall,
New Jersey, 1967.
Libro considerado ya como un clásico en la literatura de alto nivel
sobre computación y teoría de la computabilidad. Maneja desde estudios
de corte filosófico hasta especificaciones de la teoría de las funciones
recursivas, pasando por los autómatas y las máquinas de Turing. Minsky
es también una de las autoridades sobre inteligencia artificial.
[PERM94] Perero, Mariano, Historia e historias de matemáticas, Grupo Editorial
Iberoamérica, México, 1994.
Encantador libro de difusión matemática. En menos de 200 páginas
presenta una muy accesible y amena fuente de información histórica
sobre las grandes figuras históricas y los principales aspectos de las
ciencias matemáticas.
[PLAT73] Platón, Diálogos, Porrúa, México, 1973.
Quien lea algunos de los clásicos diálogos de Platón comprenderá
por qué se incluyen en la bibliografía del capítulo donde se tratan temas
de computabilidad y lógica, pues resulta evidente que buena parte de los
fundamentos de nuestros métodos de conocimiento se encuentran ex-
puestos en esta obra de hace más de 20 siglos.
[RICR83) Richardson, Moses y Leonard Richardson, Fundamentos de matemáti-
cas, CECSA, México, 1983.
Traducción de un excelente texto de 1973 sobre los conceptos bási-
cos de las ciencias matemáticas, tratados en un nivel preuniversitario.
Ofrece una visión amplia, interesante y bien fundamentada sobre lógica,
Platón (428-347 a. C.) álgebra, trigonometría, principios del cálculo, probabilidad y estadística y
metamatemáticas. Ojalá todos los libros introductorios de matemáticas
fueran así.
[R0TB97] Rotman, Brian, "The Truth About Counting", The Sciences, revista oficial
de la Academia de Ciencias de Nueva York, noviembre/ diciembre de 1997.
Interesante y esclarecedor artículo que contiene una muy accesible
explicación sobre algunos de los temas de las matemáticas de la escue-
la intuicionista, referidos al problema de contar y de los números infini-
tos, transfinitos y "surreales" (estos últimos tratados por Donald Knuth
en su "noveleta matemática" Números surreales, ya citada en el capítu-
lo 5).
[RUSB88] Russell, Bertrand, Introducción a la filosofía matemática, Ediciones Paidós,
Barcelona, 1988.
Traducción de un libro escrito por el gran lógico y filósofo inglés en
1919, mientras purgaba una condena en prisión por sus actividades pa-
cifistas y opuestas a la Primera Guerra Mundial. Trata sobre teoría bási-
Referencias para el capítulo 6 303

ca de números, relaciones, funciones, lógica y temas de la fundamentación


de las ciencias matemáticas, sin entrar en demasiados tecnicismos ni
exigir un alto nivel formal. Aunque no es un libro fácil, por su calidad y
profundidad merece pertenecer a la biblioteca de todo aquel interesado
en la belleza de las matemáticas.
[STOC79] Stockmeyer, Larry y Ashok Chandra, "Intrinsically Difficult Problems", en
Scientific American, mayo, 1979.
Impactante artículo sobre complejidad de algoritmos cuya frase
introductoria dice: "Para ser resueltos, algunos tipos de problemas compu-
tacionales requieren que una computadora tan grande como el universo
funcione durante tanto tiempo como el universo tiene de existir. Sin em-
bargo, en principio sí tienen solución". El lector comete un error si pien-
sa que se refieren a complejísimos y ocultos problemas, porque uno de
los ejemplos empleados es un inocente juego de mesa.
Existe traducción al español.
[TRAW94) Traub, Joseph y H. Wozniakowski, "Breaking Intractability", en Scientific
American, enero, 1994.
Artículo que explora la posibilidad de aplicar métodos matemáticos
para disminuir la complejidad computacional de los problemas y poderles
dar una solución práctica por computadora. Analiza además la existencia
de problemas científicos sin respuesta posible (porque la cantidad de
dimensiones a considerar para su estudio aumenta en forma exponencial),
y propone la idea de establecer un "teorema de Gódel físico".
Hay traducción al español.
[WANH95] Wang, Hao, Reflections on Kurt Gódel, The MIT Press, Massachusetts,
1995.
Cuarta reimpresión de un libro escrito en 1987 por una de las pocas
personas cercanas al gran lógico austriaco (1906-1978). Gódel llegó a
Estados Unidos en 1940 y se instaló en el Instituto de Estudios Avanza-
dos de Princeton, de donde prácticamente no salió después. Además de
una exhaustiva referencia sobre los trabajos, conferencias y apuntes
de Gódel, el libro abunda en sus intereses sobre matemáticas, cosmología
y relatividad, sus preocupaciones filosóficas (a las que dedicó los últi-
mos 30 años de su vida, estudiando a Kant, Leibnitz y Husserl), su pro-
funda amistad con Einstein y sus múltiples excentricidades.
[WHIR67] Whitehead, Alfred North y Bertrand Russell, Principia Mathematica, edi-
ción abreviada hasta la proposición *56, Cambridge University Press,
1967.
Resumen del libro I de la monumental obra que "marcó una época
en la historia del pensamiento especulativo", al deducir todas las propo-
siciones fundamentales de la lógica y las matemáticas a partir de un
pequeño número de premisas lógicas e ideas primitivas, dando así a la
lógica el lugar de honor como generadora de todo el pensamiento mate-
mático. La versión abreviada tiene 410 páginas, y buena parte de ellas
no contienen casi ninguna palabra, sino únicamente proposiciones nume-
radas, codificadas con una notación simbólica formal y rigurosa. Este libro
(así como la poesía, según una afortunada frase del escritor guatemalte-
co Luis Cardoza y Aragón) "es una prueba concreta de la existencia del
hombre".

Potrebbero piacerti anche