Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
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
—
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.
Por ejemplo,
SUMA i 27
-
Op (valor)
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
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>
Entonces la instrucción
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:
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:
CARGA 20
SUMA 21
GUARDA 22
ALTO
20 20 30 21 02 22 70
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
(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
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.
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:
(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
ALFA: DATO 1
ARRIBA: IN
GUARDA ALFA
COMP-i 0
BR= ABAJO
BR ARRIBA
ABAJO: OUT
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.
DIEZ: EQU 10
NUMERO: EQU 915
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:
FACTOR: EQU 6
ORIGEN 1000
NÚMEROS: LISTA 7,21,14,6,99
ZETA: DATO 3
ALFA: VALOR FACTOR * 5
(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
COMP NÚMEROS + 2
produce el código
40 1002
GUARDA ZETA + 1
02 1006
LONG: EQU 5
ORIGEN 1000
ARR1 : DATO LONG
ORIGEN 1000
ARR2 : LISTA 700,710,720,730,740
190 Capítulo 5 Software de base
Aquí, por ejemplo, la celda ARR2 + 3 contiene el valor 730, por lo que con las
instrucciones
CARGA ARR2 + 3
OUT
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).
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:
1014: 20 1005
1016: 32
1017: 02 1005
1019: 20 1006
1021: 32
1022: 02 1006
1024: 41 05
1026: 48 1011
1028: 70
Haremos un último ejemplo antes de escribir el diseño final del traductor ensamblador
y dar por concluida esta larga sección.
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
(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.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
<nombre>: MACRO •
renglón 1 del cuerpo
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
CARGA A
SUMA B
GUARDA C
(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
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
1
2
3
4
5
6
7
B1
B2
B3
8
9
B1
B2
B3
Al
A2
A3
A4
10
11
12
ADICIÓN: MACRO(3)
CARGA ?1
SUMA ?2
GUARDA ?3
FIN_MACRO
PROM3: MACRO(4)
CARGA 71
SUMA ?2
SUMA ?3
DI V - i 3
GUARDA ?4
FIN_MACRO
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
Macroensamblador
11111111111111111111111~
.u.uuuuuuuIuI-
U111111111111111111~
111111MMIIII~
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:
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
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.
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) (500)
Minicargador Cargador
5,500
Traductor T 1 (macroensamblador)
25,500 33,900
Programa objeto P 1 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,
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
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).
(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
(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
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:
O más escuetamente,
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"
<ORACIÓN>
Un primer análisis
sintáctico
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:
(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
"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>
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:
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:
Por otro lado, del poeta Raúl Bañuelos (Editorial Universidad de Guadalajara, 1989) leemos:
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.
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
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, -)
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, - )
1.17,1•77,
ANALIZADOR LEXICOGRÁFICO
, DICCIONARIO
L DE
ANALIZADOR SINTÁCTICO SÍMBOLOS
ANALIZADOR SEMÁNTICO
• El
Figura 36 Estructura
funcional de un compilador
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
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
(t) ¿Hará falta recordar al lector que no todas las computadoras son personales?
Sección 5.5 Compiladores 219
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:
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
Figura. 39 Un modelo
de estudio para los sistemas
operativos
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.
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
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.
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.
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
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 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].
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
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
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
(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
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
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
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.
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
Inteligencia
artificial
NIVEL 5
Sistemas
operativos
NIVEL 4
NIVEL 3
NIVEL 2
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:
(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
(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
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-
-
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.
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-
-
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
-
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.
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.
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
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.
[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".
6.i INTRODUCCIÓN
Jean Tardieu
"Problemas y trabajos prácticos",
en la revista El Cuento, núm. 13,
México, junio de 1965.
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.
(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
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).
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
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
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)
Dominio Codominio
(Estímulos) (Reacciones)
f(e0, O) —> (ei , 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
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
0I 1 1 B
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
(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
(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
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.
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.
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
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 }
(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,}*
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 )
1. S —> 0S1
2. S --> 01
S OS1
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
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
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":
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:
(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.
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:
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:
(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 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:
(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.
(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
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
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
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:
xy = x
XX = X
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
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
V
F V
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:
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
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