Empezando con Forth... a pesar de los redactores de las revistas

Descripción del contenido de la página

Revisión de unos programas escritos Abersoft Forth aparecidos en el número 2 de la revista Todospectrum.

Etiquetas:

En la página 64 del número 2 de la revista Todospectrum, de octubre de 1984, al final de la sección de programas, fueron publicados, bajo el título de Empezando con Forth, unos pequeños ejemplos en Forth, escritos para Abersoft Forth, enviados por Alfonso Martín.

Desgraciadamente el código fuente no anima mucho a interesarse por el Forth, porque está muy comprimido y la mayoría de los nombres de las palabras creadas por el autor no indican su función, lo que lo hace ilegible para el profano. Y además cualquiera que se animara a teclearlo literalmente en Abersoft Forth sin conocer el lenguaje, difícilmente podría lograr que funcionara porque el redactor de la revista, como es habitual, no respetó todos los espacios entre las palabras, imprescindibles en Forth. A todo ello hay que sumar algunas erratas en el código, sin duda también contribución del redactor de Todospectrum que, sin saber Forth, copió el código para publicarlo.

Estas imágenes muestran las páginas las páginas originales del artículo:

Primera página: (394.17 KiB) Segunda página: (368.11 KiB)

Como homenaje y agradecimiento al autor, que se tomó la molestia de escribir y enviar a Todospectrum código en Forth, copio a continuación el texto del artículo y el código fuente de los ejemplos con los espacios y los saltos de línea corregidos. Ofrezco también unas versiones modificadas y más legibles de los programas, que quizá puedan servir a alguien que esté empezando a aprender Forth y así cumplir la intención original de su autor.

Artículo original transcrito (sin código fuente)

Dicen que en este mundo tiene que haber de todo. Y el Forth parece estar llamado a "pegar fuerte" en el futuro. Alfonso Martín nos ha realizado una pequeña y básica demostración para irnos familiarizando con este lenguaje, utilizando el compilador de Abersoft.

El programa 1 define AL1, AL2 e INPUT. Las dos primeras sirven para visualizar el juego de caracteres del Spectrum. INPUT permite introducir datos numéricos.

El programa 2 ilustra la forma de crear sus propios condicionales. Para ello se definieron AL3, AL4, AL5 y AL6. Teclee AL3, a partir de la cual se hace referencia a las otras, e introduzca un dato. El programa le indicará si es menor, mayor o igual a 100.

Veamos ahora cómo construir un pequeño programa que nos dé el factorial de un número. El listado 3 define las variables F y D. En F se almacena el producto, y D es el contador. Introduzca FACT y el número del que desea obtener el factorial.

Finalmente el programa del listado 4 permite utilizar las posibilidades gráficas del Spectrum, indicando las coordenadas X, Y. Teclee PINTAR, el ordenador le pedirá los valores iniciales y finales (coordenadas) para trazar líneas.

Código fuente

Listado 1

Original

Sólo he añadido los espacios que faltaban, así como eliminado los cortes de línea forzados por la maquetación:

: AL2 CR ." JUEGO DE CARACTERES" CR ;
: AL1 6 PAPER 1 BORDER CLS AL2 255 23 DO I 23 - . 8 SPACES I EMIT CR LOOP ;
: INPUT PAD 1+ 64 EXPECT .0 PAD (NUMBER) DROP DROP . ;

Reescrito

Las dos primeras palabras son triviales; pero con nombres adecuados, bien formateadas y con algún comentario ganan mucho:

: .TITULO  ( -- )
  ." JUEGO DE CARACTERES"
  ;
: .CARACTERES  ( -- )
  6 PAPER  1 BORDER  CLS .TITULO CR
  255 23 DO
    CR I 23 - .  ( imprimir ordinal)
    8 SPACES
    I EMIT  ( imprimir signo)
  LOOP
  ;

He elimininado los CR en la primera palabra porque no tiene sentido imprimir un salto de línea tras haber borrado la pantalla (un pequeño despiste del autor). Con este pequeño cambio, la primera palabra hace una sola cosa: imprimir el título. Es la segunda palabra la que se ocupa de poner los saltos de línea donde corresponda.

La definición de INPUT es más interesante, y se usa en el resto de programas del artículo:

: INPUT  ( -- u )
  PAD 1+ 64 EXPECT  ( leer 64 caracteres del teclado en la dirección siguiente al inicio de PAD)
  0. PAD (NUMBER)  ( convertir en número de doble precisión el texto en PAD+1 y sumarlo a 0)
  2DROP  ( eliminar los resultados innecesarios)
  ;

INPUT tenía dos errores: la forma de forzar al intérprete a interpretar un número de doble precisión, que es lo que (NUMBER) espera en la pila, es escribir un punto en cualquier parte del mismo, salvo al principio. Por eso lo correcto es 0.. ¿Por qué a PAD se le suma uno la primera vez y no la segunda? Porque (NUMBER) interpreta el texto en la dirección siguiente a la que se le da; igual resultaría pasarle PAD a EXPECT sin modificación y restarle uno a PAD antes de pasárselo a (NUMBER). Por otra parte, la palabra . al final de (NUMBER) sobraba, pues imprimía el número que había que devolver como resultado. El uso de 2DROP en lugar de DROP DROP evidentemente no afecta en nada, es por ahorrar código; la función es la misma: eliminar de la pila la dirección del primer carácter no convertible del texto y el octeto superior del número de doble precisión, pues no lo necesitamos.

Para conocer el funcionamiento de las palabras EXPECT y (NUMBER) es recomendable consultar el manual de Abersoft Forth.

Listado 2

Original

Sólo he añadido los espacios que faltaban, así como eliminado los cortes de línea forzados por la maquetación.

Además corrijo una errata: en el código original, en la palabra AL3, hay una L que debería ser <. Parece que el redactor no revisó el texto.

: AL4 ." EL NO. ES MENOR DE 100" ;
: AL5 ." EL NO. ES MAYOR DE 100" ;
: AL6 ." EL NO. ES IGUAL A 100" ;
: AL3 INPUT 100 < IF AL4 ELSE 100 = IF AL6 ELSE AL5 THEN THEN ;

Pero además de esa errata, el código tiene un fallo: la palabra AL3 consume el valor devuelto por INPUT en la primera comparación, por lo que las siguientes comprobaciones no funcionarían. Para solucionarlo hay que añadir un DUP y un DROP como se verá a continuación.

Reescrito

La palabra principal, que he llamado COMPARA, está corregida. A pesar de que su código es muy sencillo, he preferido formatearla verticalmente y tabularla para facilitar su lectura a los principiantes.

: MENOR  ( -- )
  ." EL NO. ES MENOR DE 100"
  ;
: MAYOR  ( -- )
  ." EL NO. ES MAYOR DE 100"
  ;
: IGUAL  ( -- )
  ." EL NO. ES IGUAL A 100"
  ;
: COMPARA  ( -- )
  INPUT DUP 100 <
  IF
    DROP MENOR
  ELSE
    100 =
    IF
      IGUAL
    ELSE
      MAYOR
    THEN
  THEN
  ;

Listado 3

Original

Sólo he añadido los espacios que faltaban, así como eliminado los cortes de línea forzados por la maquetación:

0 VARIABLE F
0 VARIABLE D
: FACT1 1 F !  INPUT D !  D @ F ! ;
: FACT3 D @ 1 - D ! ;
: FACT FACT1 BEGIN F @ DUP D @ 1 - * F !  FACT3 D @ 1 = UNTIL F ? ;

Pero el código tiene un error: En la palabra FACT, la palabra DUP es innecesaria y deja la pila llena de resultados parciales. Además, la frase 1 F ! en la palabra FACT1 es también innecesaria y no tiene ningún efecto.

Reescrito

Aparte de corregir el error citado anteriormente y de renombrar las palabras, es posible simplificar el código decrementando el contador con la palabra +!; también la primera palabra, INIT, que pide y guarda el número, puede ser simplificada con un sencillo DUP:

0 VARIABLE RESULTADO
0 VARIABLE CONTADOR
: INIT  ( -- )
  INPUT DUP CONTADOR ! RESULTADO !
  ;
: FACT  ( -- )
  INIT
  BEGIN
    RESULTADO @ CONTADOR @ 1 - * RESULTADO !
    -1 CONTADOR +!
    CONTADOR @ 1 =
  UNTIL
  F ?
  ;

Pero, en lugar de mantener un contador en un bucle BEGIN - UNTIL, es más sencillo usar un bucle DO - LOOP. Y aun más sencillo es si recibimos el número en la pila y devolvemos el resultado en la pila, que es lo habitual en Forth y lo más versátil. Con todos esos cambios, el programa queda reducido a una sola palabra:

: FACT  ( u1 -- u2 )
  DUP 1 - 1 SWAP
  DO
    I *
  -1 +LOOP
  ;

En cualquier caso, los números de los que podemos calcular el factorial no son muchos, porque la aritmética es de enteros simples (de 16 bitios): un número mayor de 10 dará un resultado erróneo debido al desbordamiento. Para poder operar con más números habría que modificar el código para que usara enteros dobles (de 32 bitios), lo cual sería un ejercicio de programación interesante. Abersoft Forth no dispone de palabras para manejar números de coma flotante, que permitirían trabajar con un rango de números mucho mayor (aunque con mayor lentitud).

Listado 4

Original

Sólo he añadido los espacios que faltaban, así como eliminado los cortes de línea forzados por la maquetación:

0 VARIABLE XI
0 VARIABLE YI
0 VARIABLE XF
0 VARIABLE YF
: P1I ." X" INPUT XI !  ." Y" INPUT YI ! ;
: P1F ." X" INPUT XF !  ." Y" INPUT YF ! ;
: P1 ." INTRODUCIR VALORES" CR ." INICIALES" P1I CR ." FINALES" P1F ;
: PINTAR P1 CLS XI @ YI @ PLOT XF @ YF @ DRAW ;

Reescrito

Lo único que ha hecho falta corregir es la separación de los textos, para que no se juntaran en la pantalla:

0 VARIABLE XI
0 VARIABLE YI
0 VARIABLE XF
0 VARIABLE YF
: INICIALES  ( -- )
  ." X: " INPUT XI ! SPACE
  ." Y: " INPUT YI !
  ;
: FINALES  ( -- )
  ." X: " INPUT XF ! SPACE
  ." Y: " INPUT YF !
  ;
: VALORES  ( -- )
  ." INTRODUCIR VALORES"
  CR ." INICIALES: " INICIALES
  CR ." FINALES: " FINALES
  ;
: PINTAR  ( -- )
  CLS VALORES CLS
  XI @ YI @ PLOT
  XF @ YF @ DRAW
  ;