Por una conjunción copulativa; viajando en el tiempo
En lo que va de mes he trabajado en dos de mis proyectos: el módulo de redifusión en formato Atom 1.0 para Simplilo; y el formato de datos de La legionela del pisto.
Es un ejercicio mental que me gusta: repartir mi tiempo entre proyectos principalmente «útiles» y proyectos principalmente «deliciosos», como es el caso de los dos mencionados. Me gusta pasar de un lenguaje complejo, potente, actual y muy popular como PHP, que se ejecuta en un servidor de páginas hipertextuales, a un lenguaje sencillo, limitado, «antiguo» y olvidado (pero encantadoramente creativo y cómodo) como Beta BASIC, que se ejecuta en una ZX Spectrum. Es un ejercicio que me ayuda a no perder la perspectiva, a no olvidar los orígenes.
Trabajar en 2010 con una ZX Spectrum 128, una computadora de 1985, aunque sea por medio de un emulador como Fuse, es hacer un delicioso viaje en el tiempo, a una época en que un octeto de más o de menos tenía toda la importancia de sus ocho bitios, ninguno menospreciable; una época en que la limitación de recursos no era sentida como tal; una época en que cualquier descubrimiento era un nuevo mundo al alcance de la mano. Ahora los nuevos mundos informáticos me resultan agobiantes por demasiados; suelen ser complejísimos y casi siempre son inabarcables por una sola persona. Por eso me gusta viajar en el tiempo y pasear sin prisa por aquella otra época, para mí entrañable.
Siempre me ha gustado leer código fuente. Pero a menudo leo código de programadores expertos escrito de cualquier forma, tanto en su aspecto como en su funcionamiento. He llegado a la conclusión de que la potencia de las computadoras actuales induce a la pereza, a no afinar los algoritmos, a no ahorrar memoria, a no ahorrar tiempo de proceso, a no fijarse en los detalles. A veces me pregunto qué podría lograrse sumando la potencia de las máquinas y lenguajes actuales con el genio y la creatividad de tantos programadores de hace un cuarto de siglo, que dieron vida a muchas pequeñas maravillas en unas pocas docenas de kibioctetos de memoria y con tres y pico megaciclos de procesador.
Pensaba en todo ello mientras estos días trabajaba en varios algoritmos para La legionela del pisto. Por ejemplo, el dilema de una simple conjunción copulativa:Es un pequeño pero elegante signo de inteligencia, por parte del programa, imprimir una simple conjunción «y» ante el último elemento de una lista. Entre Hay salidas hacia norte, sur, oeste.
y Hay salidas hacia norte, sur y oeste.
hay un pequeño mundo algorítmico. En el primer caso, distinguir cuándo hay que imprimir coma y cuándo punto es muy fácil (coma ante cada elemento, empezando con el segundo, y punto al terminar la lista); pero, en el segundo caso, saber cuándo imprimir la conjunción «y» no es tan fácil: es necesario saber por anticipado cuántos elementos tiene la lista. Esto sería trivial en un lenguaje moderno (incluso interpretado, como PHP), ejecutándose en una computadora actual; pero es un cierto desafío en Beta BASIC en una ZX Spectrum. Hacerlo de una u otra manera puede significar una diferencia significativa de tiempo de ejecución.
Muestro a continuación un par de ejemplos del código de La legionela del pisto que ilustran el problema de la conjunción copulativa. Son versiones ya descartadas, porque desde que las escribí he cambiado el formato de las matrices de datos, pero eso no afecta al fondo de la cuestión.
La versión sin conjunción copulativa simplemente construye una lista de salidas y la imprime o no según tenga algún elemento o esté vacía; también tiene en cuenta el número de elementos hallados para imprimir correctamente el plural de la palabra «salidas»:
1770 DEF PROC action_SALIDAS_fast
1780 LOCAL i,exit,exits,t$
LET exits=0,t$=""
1790 FOR i=fNorth TO fDown
1800 LET exit=!ent#(location,i),exit=exit-lockedDoor*(exit>lockedDoor)
IF exit THEN LET d$=!ent$(i, TO nameLength)
trim d$
LET d$=(", " AND exits)+d$,exits=exits+1
COPY d$ TO t$
1810 NEXT i
1820 IF NOT exits THEN tell "No hay salidas."
ELSE
tell "Hay salida"+("s" AND exits>1)+" hacia: "+t$+"."
1830 END PROC
La versión con conjunción copulativa debe hacer un recuento previo de los elementos de la lista, para saber cuál será el último a la hora de imprimirlos:
1670 DEF PROC action_SALIDAS_slow
1680 LOCAL i,exit,exits,s$,t$,listed,d$
LET exits=0,listed=0
1690 FOR i=fNorth TO fDown
LET exit=!ent#(location,i),exit=exit-lockedDoor*(exit>lockedDoor),exits=exits+(exit<>0)
NEXT i
1700 IF NOT exits THEN tell "No hay salidas."
GO TO 1760
1710 LET t$="Hay salida"+("s" AND exits>1)+" hacia "
1720 FOR i=fNorth TO fDown
1730 LET exit=!ent#(location,i),exit=exit-lockedDoor*(exit>lockedDoor)
IF exit AND exit<lockedDoor THEN LET d$=!ent$(i, TO nameLength)
trim d$
LET d$=(", " AND (listed AND (listed<>(exits-1))))+(" y " AND (listed=(exits-1) AND listed))+d$,listed=listed+1
COPY d$ TO t$
1740 NEXT i
1750 tell t$+"."
1760 END PROC
Un método más rápido que utilicé después consiste en aprovechar el primer recuento de elementos para conservar éstos en un formato intermedio y así acelerar el segundo bucle, el que construye el texto final a imprimir.
Durante los viajes en el tiempo, hasta una simple conjunción copulativa tiene su importancia y requiere cuidado y atención.