Anda di halaman 1dari 19

13/9/2018 www.disc.ua.es/~gil/rexx.

html

EL LENGUAJE REXX

1.- Introducción.
=================

REXX es un lenguaje de programación con unas características


sumamente interesantes. Desgraciadamente, y a pesar de haber
cumplido ya los veinte años, no es tan conocido como debiera,
aunque esto se está remediando rápidamente, con una comunidad
de programadores activa y creciente. Esta pequeña introducción
quiere también colaborar en este sentido, ya que, al parecer,
no existen cursos de REXX en español, ni documentación de
otro tipo, salvo el sistema de ayuda incluido con los Sistemas
Operativos de IBM.

¿Qué es lo que lo hace atractivo? Varias cosas. A lo largo de


los años, he desarrollado tres o cuatro pequeños lenguajes de
programación, y he adquirido la certeza de que la existencia
de tipos primitivos de datos es un error. Un programador no
debería de estar pendiente de si sus datos van a ser positivos
o negativos, y de si se van a mover en tal rango o en tal
otro. Un programador debería de pensar únicamente en términos
de *números* y *cadenas de caracteres*. Pero los humanos no
tenemos una idea intuitiva de los números, salvo para los
primeros enteros. 44016 no nos dice nada, pero no nos importa,
porque hemos aprendido a manejar la *cadena* "44016", y
sabemos combinarla con otras cadenas que también representan
números. En definitiva, un lenguaje de programación avanzado,
cercano a la forma en que pensamos los humanos, debería de
tratar únicamente con cadenas de caracteres. Unas
representarán palabras o frases, otras representarán números.
Esto es exactamente lo que hace REXX, y es su primera ventaja.

La segunda ventaja es una consecuencia directa de la primera:


puesto que REXX trabaja con cadenas de caracteres para
representar números, la aritmética es totalmente independiente
de la plataforma. No sólo eso, es configurable por el usuario.
Se acabaron los mensajes "underflow" u "overflow". Se acabaron
las representaciones aproximadas de números como "1.2E-34".
Con REXX, factorial de 158 es exactamente 158!.

Tercera ventaja: puesto que REXX es un lenguaje orientado a


cadenas, es de esperar que encontremos una completa remesa de
funciones que traten cadenas. Funciones de alto nivel que
ahorrarán mucho trabajo respecto a otros lenguajes.

Cuarta ventaja: REXX esta perfectamente integrado con su


entorno. De hecho, puede usarse como lenguaje de "script".
Pero un lenguaje de "script" más potente y fácil de usar.
Puede pasarse información de un programa REXX a otro y entre
programas REXX y programas escritos en otros lenguajes de
programación, como C o Java.

Quinto: el tratamiento que hace REXX de los arrays es original


y sumamente elegante. Tanto es así, que arrays de cualquier
dimensión y estructuras definidas por el usuario son cubiertos
por la misma sintaxis: clara y natural. Tanto, que los
`record' de Pascal y `struct' de C de pronto nos parecen
anticuados.

Por lo demas, REXX es un lenguaje estructurado en bloques, con


instrucciónes de control de flujo algo más elaboradas y
flexibles de lo que podemos encontrar en otros lenguajes.

Quizás sea también preciso decir que REXX no contiene


punteros. Un alivio para los primerizos y una pérdida para los
programadores más avezados, amantes de las estructuras de
datos complejas que con ellos pueden crearse.

http://www.disc.ua.es/~gil/rexx.html 1/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

Finalmente, tengo que anunciarte que REXX es un lenguaje


multiplataforma, unas diez veces más fácil de aprender que
Java. Escribe un programa en REXX y ejecútalo sin tocar un
punto ni una coma en cualquier maquina que te encuentres y en
cualquier sistema operativo. Interesante, ¿verdad ?. Si te
parece poco, existe una extensión de REXX que introduce
objetos, Object REXX, y otra extensión llamada NetREXX que
permite escribir aplicaciones para la RED y aprovechar todas
las ventajas de los entornos Java. Y hablando de entornos,
puedes programar REXX tanto en modo consola, usando tu editor
favorito, como en modo gráfico, usando los entornos gratuitos
que existen para este lenguaje. Y en cuanto al ejecutable,
igual: puedes crear con la misma facilidad aplicaciones
orientadas a consola y aplicaciones gráficas.

Por supuesto, también tiene algunas desventajas. La principal,


es que de momento no existen compiladores gratuitos para REXX.
Sin embargo, los intérpretes que existen funcionan muy bien,
pudiendo procesar, en un Pentium 500, del orden de 600.000
cláusulas por segundo. Juzga tú mismo.

2.- Para aprender REXX.


=======================

Bueno, lo primero que necesitas es un intérprete REXX. En


algunas distribuciones Linux puedes encontrar el intérprete de
Regina, y si miras en la dirección:

http://www2.hursley.ibm.com/rexx

encontrarás una lista de enlaces a implementaciones de REXX


gratuitas. BREXX, por ejemplo, está bien, así que aquí tienes
el enlace para descargar la version para DOS: ( que puedes
correr en cualquier Windows, por supuesto )

ftp://gwdg.de/pub/languages/rexx/brexx

Lo segundo que necesitas es un editor. Supongo que conoces


algún otro lenguaje de programación, de manera que haré una
exposición inversa a la habitual, es decir, de arriba a abajo.
Así que, a medida que vayas leyendo esta introducción, teclea
los pequeños programas y ejecútalos. Es la forma más rápida.
Por cierto, que me voy a limitar a la implementación que la
comunidad REXX llama `clásica'. No hablaré nada de ObjectREXX
ni de NetREXX.

Por otra parte, yo uso dos implementaciones de REXX,


principalmente, la primera, PC-DOS REXX, y la segunda, Regina
REXX, para Linux. Mi fuente más completa de información,
aparte algunos excelentes documentos que pueden conseguirse en
la RED, es el manual de PC-DOS REXX, de IBM. Es posible por
tanto que algún punto de los explicados en lo que sigue sea
específico de esta implementación. Si es así, mira en la
documentación que acompañará a tu intérprete.

3.- Estructura de un programa.


===========================

Como he dicho antes, voy a hacer una exposición inversa a lo


habitual, empezando por las estructuras mayores y descendiendo
a las más pequeñas. Pero como comprendo tu impaciencia, aquí
va el programa con que se comienza la exposición de cualquier
lenguaje desde hace 30 años:

/ * primer programa en REXX * /


di "Hola mundo!"

Edítalo, sálvalo con el nombre 1.r u otro cualquiera y


ejecútalo llamando al intérprete. Si éste se llamase `rexx',

http://www.disc.ua.es/~gil/rexx.html 2/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

escribe en tu línea de comandos `rexx 1.r'.

Un programa REXX comienza con un comentario y termina con la


última línea del archivo fuente, o la instrucción `EXIT':

/ * patrón de un programa * /
instrucciónes
salida

Dentro del bloque principal del programa puede haber llamadas


a subrutinas. Una subrutina es un segmento de código que puede
ser llamado desde más de un lugar en el programa principal.
Este fragmento de código puede residir en el mismo archivo que
el programa principal o en un archivo de disco distinto. Una
subrutina se invoca mediante la instrucción `CALL', por
ejemplo:

/ * patrón de un programa * /
instrucción 0
instrucción 1
...
LLAME a mirutina
instrucción n
instrucción n+1
...
SALIDA
mirutina:
instrucciones
REGRESO

Opcionalmente, después de RETURN puede ir una expresión. Si


éste es el caso, la expresión es evaluada, y el valor
resultante asignado a una variable especial llamada RESULT,
que puede ser usada en el programa principal. Si la expresión
no aparece, el valor que pudiese tener previamente asignado
RESULT es eliminado, de manera que su evaluación da como
resultado el nombre "RESULT".

En cuanto al paso de argumento, si es preciso, se colocan tras


el nombre de la rutina, separados por comas. Por ejemplo:

LLAME a mirutina a, b, c

Para que estos argumentos sean tomados efectivamente por la


subrutina, y pasados a variables formales con las que operar,
se usan las instrucciones `ARG' y `PARSE ARG'. La primera pasa
los argumentos a mayúscula, antes de entregarlos a la rutina,
mientras que la segunda los pasa exactamente. Bien, es la hora
de un ejemplo:

/ * programa principal * /
/* calculo del área del triángulo */
say "introduce la altura del triángulo: "
altura de tiro
say "introduce la base del triángulo: "
tire de la base
call area base, altura
say "el area del triángulo es : "
decir RESULTADO
salida
/* subrutina que calcula area */
zona:
parse arg a, b
devolver 0.5 * a * b

Otra forma de pasar argumentos a una subrutina es mediante la


función ARG(). En esta modalidad, se llama a la subrutina con
CALL, y los argumentos pueden ser expresiones que serán
computadas con ARG(). Por ejemplo:

http://www.disc.ua.es/~gil/rexx.html 3/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

/* ejemplo del uso de ARG() */


di "introducir en número:"
jalar
say "introduce un segundo número: "
tirar b
llamar a mirutina a + b, ab, a * b
decir RESULTADO
salida
mirutina:
f1 = arg (1)
f2 = arg (2)
f3 = arg (3)
return f1 * f2 * f3

En REXX existe el concepto de `función' además del concepto de


subrutina. A las funciones se las llama por su nombre, con
los argumentos entre paréntesis, y *deben* devolver siempre un
valor mediante RETURN, aunque sea la cadena nula. El valor
devuelto es usado para evaluar la expresión de la que la
llamada a la función forme parte. Por ejemplo:

/* ejemplo del uso de funciones en REXX */


di "introduce un número:"
jalar
di "introduce otro número:"
tirar b
say "tres veces el producto es: " 3*producto(a,b)
salida
producto:
parse arg a, b
devolver a * b

Resumiendo lo expuesto hasta aquí, la estructura al más alto


nivel de un programa REXX tiene la forma de la figura
siguiente:

Bloque principal Bloques de subrutinas


+ ----------------- + + -------------------- +
| llamar a ---------------> | subrutina a |
| . | + -------------------- +
| . |
| . | + -------------------- +
| llamar b ---------------> | subrutina b |
| . | + -------------------- +
| . |
| . | + -------------------- +
| llamar a c ---------------> | subrutina c |
| . | + -------------------- +
| . |
| . |
| . |
| |
| salir |
+ ----------------- +

Conviene en este punto hacer una reflexión. REXX sólo tiene un


tipo de datos, la cadena de caracteres, y por eso muchos
afirman que, en consecuencia, las variables no necesitan ser
declaradas. Este es un error común, ya que no hay nada que
impida que las variables no declaradas tengan tipo, ni nada
que impida que variables sin tipo deban ser declaradas. En
cualquier caso, en REXX no es necesario declarar variables, lo
que podría llevar a errores de difícil localización, sobre
todo en programas largos. Esencialmente, el problema consiste
en que todas las variables son globales. Para remediar esta
situación, REXX ofrece la posibilidad de ocultar las variables
de subrutinas al resto del programa, haciéndolas por tanto
locales. Si se desea hacer esto, bastará con escribir

http://www.disc.ua.es/~gil/rexx.html 4/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

`PROCEDURE' al inicio de la subrutina. Esta instrucción sólo


puede llamarse una vez, y debe ser la primera instrucción de
la rutina. Una variante de esta instrucción es `PROCEDURE
EXPOSE', que permite compartir con el programa principal una
porción de las variables de la rutina. Su sintaxis es simple:
a continuación de `PROCEDURE EXPOSE' se escriben los nombres
de las variables que vayan a ser compartidas. Esto incluye
aquellas que sean creadas dentro de la subrutina, que pasan a
estar disponibles para el programa principal.

Otra función interesante es SYMBOL(), que toma como único


argumento un nombre de variable entre dobles comillas ( para
distinguirlo de su valor ). SYMBOL() devuelve `BAD', `LIT' o
`VAR' según que el argumento no sea un símbolo válido, el
argumento sea un nombre válido de variable al que aún no se ha
asignado ningún valor ( o una constante numérica ) o que el
nombre haya sido usado ya como variable en el programa. Una
de las utilidades de SYMBOL() puede ser asegurarse de que una
variable ha sido iniciada antes de usarla.

4.- Estructuras de control y bloques.


======================================

REXX es un lenguaje estructurado de bloques. Los bloques


comienzan con `do' y terminan con `end'. Por ejemplo:

hacer
instrucción 1
instrucción 2
...
fin

que es similar a la pareja `begin'/`end' de Pascal o `{'/`}'


de C. Existen estructuras de control que permiten ejecutar
repetidas veces un bloque de instrucciones, y estructuras que
permiten elegir entre varios bloques, para ejecutar uno u otro
según se den o no ciertas condiciones.

4.1.- Estructuras repetitivas.


------------------------------

Verás que REXX destaca sobre otros lenguajes en la riqueza de


las estructuras repetitivas, que hacen muy difícil encontrar
un caso en que la formulación de un problema no siga el camino
que cualquiera consideraría `natural'. Veamos cuales son las
posibilidades:

i) Bloque repetitivo simple. Es el caso más sencillo.


Simplemente, se especifica el número de veces que se desea
ejecutar el bloque. Por ejemplo:

hacer 10
di "hola"
fin

ii) Uso de un contador para usar en el interior del bloque:

hago yo = 1 a 10
di "hola" i
fin

iii) Uso de contador y especificación del intervalo. Puede de


esta forma controlarse el incremento que experimenta el
contador en cada iteración:

hago yo = 1 a 10 por 2
di "hola" i
fin

http://www.disc.ua.es/~gil/rexx.html 5/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

iv) Uso de contador, especificacion del intervalo y número de


iteraciones:

do i = 1 a 10 por 2 por 2
di "hola" i
fin

Y en este caso, el bucle solo se ejecuta dos veces.

v) Repetición del bloque un número ilimitado de veces:

hacer para siempre


di "hola"
fin

vi) Uso de expresiones condicionales con `while'. Por ejemplo:

/* leer números mientras sean distintos de cuatro */


número=0
do while número = 4
pull número
fin

En esta construcción, la condición es comprobada al principio


del bucle.

vii) Uso de expresiones condicionales con `until'. Por ejemplo:

/* leer números hasta que se introduzca el 4 */


número=0
do hasta el número = 4
pull número
fin

En esta construcción, la condición es comprobada al final del


bucle. Esto significa que el bloque de instrucciones es
ejecutado al menos una vez.

viii) Variable de control y condición. Esta es una forma


realmente sofisticada de bucle repetitivo. Sirva este ejemplo:

/ *
No aceptar un "NO" por respuesta, salvo si el "NO"
se repite tres veces
* /
hacer 3 hasta respuesta = "NO"
pull respuesta
fin

En este ejemplo, el bucle se ejecuta hasta que la respuesta


sea distinta de "NO", o, caso de ser "NO", tres veces.
Ejecútalo y lo veras más claro. Otro ejemplo:

/ *
Leer 10 números, pero salir si se introduce la cadena vacía
* /
haz 10 hasta respuesta = ""
pull respuesta
fin

4.2.- Una piedra en el camino


------------------------------

A veces, el flujo normal del programa debe ser interrumpido.


Estrictamente, siempre es posible preservar la estructura de
bloques de un programa. Lo que ocurre es que a veces esto no
es práctico. Piensa en el caso en que has de explorar una
matriz tridimensional buscando que un elemento tenga un valor
determinado. Necesitarás tres bucles anidados, y cuando

http://www.disc.ua.es/~gil/rexx.html 6/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

encuentres el elemento que buscas, ¿qué haces ?. Puedes


guardarte los indices de la matriz y el valor, esperar a que
concluyan las iteraciones y después seguir la ejecución del
programa. Si la matriz es de 100 por 100 por 100, tiene un
millon de elementos, y si el que buscas es el primero, tienes
que explorar 999.999 antes de terminar, lo que es un notable
desperdicio. Mejor salir del bucle inmediatamente, y para
estos casos, incluso los lenguajes que abominan más
abiertamente de la instrucción `goto', como Pascal o C,
*tienen* la instrucción `goto'. Donde otros lenguajes usan
`goto', REXX usa `SIGNAL'. Su uso es muy simple. He aqui un
ejemplo donde se busca un determinado elemento en una matriz
de 100*100:

hago yo = 1 a 100
hacer j = 1 a 100
si aij = 3.14 entonces
drenaje de señal
fin
fin
salir:

Pero `SIGNAL' también puede usarse para colocar `trampas', de


manera que el programa interrumpa su ejecución normal y salte
a una rutina específica cuando se produzca una condición
determinada. La sintaxis es `SIGNAL ON condición', donde
`condición' puede ser:

ERROR: se produce el salto a la subrutina ERROR: cuando REXX


pasa una cadena al Sistema para que la ejecute y como
resultado el sistema devuelve un error. ( REXX puede pasar
cadenas al entorno, y por eso es un excelente lenguaje de
`script '. )

FAILURE: se produce el salto a la subrutina FAILURE: cuando


REXX pasa una cadena al Sistema para que la ejecute pero éste
encuentra un error grave y no la ejecuta.

NOTREADY: se produce el salto a la subrutina NOTREADY: cuando


hay algun error de Entrada/Salida.

NOVALUE: la condición de error consiste en que REXX encuentra


un nombre que podría ser el nombre de una variable pero ésta
en realidad no existe. Por ejemplo, este error puede
producirse al teclear incorrectamente el nombre de una
variable, y podría ser muy difícil de localizar a no ser por
esta facilidad de REXX.

SYNTAX: se ejecuta la subrutina llamada SYNTAX: cuando REXX


encuentra un error sintáctico mientras procesa un programa.

Cualquiera de estas trampas puede desactivarse mediante


`SIGNAL OFF nombre '.

Otra interesante posibilidad consiste en reiniciar el programa


caso de producirse un error. La sintaxis es similar pero
usando la instrucción `CALL'. Asi `CALL ON NOTREADY'
reiniciaría el programa si durante su ejecución se produjese
un error de Entrada/Salida.

Hay otras dos instrucciones útiles en según que casos. La


primera es `LEAVE', y permite salir inmediatamente de un
bucle. Podría usarse `SIGNAL', pero mientras que ésta provoca
que el programa continúe ejecutándose en cualquier punto que
se desee, `LEAVE' salta inmediatamente después del final del
bucle actual. Además, cuando hay más de un bucle anidado,
puede especificarse de cual de ellos se desea salir. No tiene
por que ser el más interno. Por ejemplo, el siguiente programa
imprime parejas (i,j), pero pasa a la `i' siguiente en cuanto

http://www.disc.ua.es/~gil/rexx.html 7/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

encuentra el valor j=3 y además i=2:

do yo = 1 a 5
hacer j = 1 a 5
si i = 2 & j = 3 entonces deja j
decir ij
fin
fin

Similar a la instrucción anterior es `ITERATE', que salta al


principio del bucle. Así, el programa anterior podría haberse
escrito en la forma:

do yo = 1 a 5
hacer j = 1 a 5
si i = 2 y j = 3, entonces repite i
decir ij
fin
fin

4.3.- Condicionales
--------------------

La construcción condicional en REXX es similar a la que se


encuentra en BASIC o Pascal. Por ejemplo, en el siguiente
programa se pide la introducción de un número, y se ejecuta un
bloque de instrucciones u otro según este número cumpla o no
una condición:

di "introduce un número:"
jalar
si a> 0 entonces
hacer
say "el número es mayor que cero"
say "adiós, hasta la próxima"
fin
más
hacer
say "el número es menor o igual que cero"
say "que pases buen día"
fin

Puede, en determinadas circunstancias, no ser preciso hacer


nada. Entonces, se usara la instrucción `NOP', que significa
`No OPeration'. Por ejemplo:

di "introduce un número:"
jalar
si a> 0 entonces
hacer
say "el número es mayor que cero"
say "adiós, hasta la próxima"
fin
más
nop

Naturalmente, los condicionales pueden anidarse entre si, de


manera que, eligiendo alternativamente entre dos opciones,
podemos optar por un número arbitrario de opciones. Pero para
eso es mejor la construcción `SELECT', cuya estructura es la
siguiente:

seleccionar
cuando condicion_0 entonces
hacer
instrucciones
fin
cuando condicion_1 entonces
hacer

http://www.disc.ua.es/~gil/rexx.html 8/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

instrucciones
fin
de otra manera
hacer
instrucciones
fin
fin

4.4.- Operadores condicionales


-------------------------------

Existen muchos operadores condicionales. Algunos de ellos ya


los he presentado en distintos fragmentos de código. Aquí va
la lista completa de ellos:

= igual
<menor
> alcalde
>= mayor o igual
<= menor o igual
<> distinto

Todos ellos pueden negarse con el operador `', teniendo, por


ejemplo: \= ( no igual ), \< ( no menor que ), etc. Estos
operadores comparan números y cadenas de caracteres. En el
segundo caso, los espacios en blanco al principio y al final
de las cadenas no se tienen en cuenta a la hora de hacer la
comparación. Existe una versión `fuerte' de estos operadores,
que hacen comparaciones estrictas de cadenas, es decir,
incluyendo los posibles espacios en blanco. Estos operadores
hijo:

== igual
<< menor
>> alcalde
>>= mayor o igual
<<= menor o igual

y sus correspondientes negaciones mediante `'. Otra diferencia


entre estos operadores y los anteriores esta en la forma
distinta en que tratan cadenas que representan números. Por
ejemplo, 2 y 2.0 no son la misma cadena, pero son el mismo
número. Ejecuta para probar el siguiente fragmento de código:

/* operadores de comparacion */
a = 2
b = 2.0
si a == b entonces diga "la misma cadena"
si a = b entonces diga "los mismos números"

5.- Símbolos compuestos.


========================

El tratamiento que hace REXX de lo que en Pascal son registros


y en C estructuras es muy interesante, porque, además, de una
forma unificada se tratan también las estructuras definidas
por el usuario y los arrays de datos. Por cierto, REXX lleva
la cuenta del tamaño de los arrays, ampliándolo si es preciso,
de manera que nunca tendremos mensajes del tipo `out of
distancia'.

Un símbolo compuesto es un símbolo que tiene una `raiz' y una


o varias extensiones, separadas por un punto. A la comprensión
por el ejemplo:

/* programa REXX que inicia un array de 20 números */


hago yo = 1 a 20
ai = 0
fin

http://www.disc.ua.es/~gil/rexx.html 9/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

/* programa REXX que inicia una matriz de 4*4 */


hago yo = 1 a 4
hacer j = 1 a 4
aij = 0
fin
fin

El uso de símbolos compuestos para formar registros con


información relacionada también es directo.

/* ilustración del uso de símbolos compuestos */


pull a.nombre
pull a.direccion
jalar un teléfono
tirar a.dni
say a.nombre
/* ilustracion de un array de estructuras */
hago yo = 1 a 10
pull ainombre
pull aidireccion
tirar aiedad
fin
hago yo = 1 a 10
decir aiedad
fin

6.- Aritmética
===============

6.1.- Aritmética básica con REXX


---------------------------------

La aritmética es una de las partes más gratificantes de REXX.


No hemos de preocuparnos de los rangos en que se moverán las
cantidades, ni de si llevarán o no signo, ni de los
moldeadores de tipo, ni de la imposibilidad de obtener
resultados exactos cuando los números son muy grandes. Por
ejemplo, trata de hallar factorial de 100 con C: no puedes.
Con REXX sí, míralo:

/* factorial grande */
f = 1
hago yo = 1 a 100
f = f * i
fin
decir f

Ejecútalo y obtendrás el resultado: 9.33262137E+157. Por


defecto, REXX usa nueve cifras significativas, pero puede
cambiarse a cualquier otra cantidad. Por ejemplo, si necesitas
doscientos dígitos, escribe `numeric digits 200' antes de
comenzar los cálculos, y vuelve a ejecutar el programa:

93326215443944152681699238856266700490715968264381621468592963
89521759999322991560894146397615651828625369792082722375825118
5210916864000000000000000000000000

Esto es factorial de 100!. Por lo demas, REXX se comporta con


la aritmética como es de esperar. El siguiente programa toma
tres números y hace algunas operaciones con ellos:

/* algunas operaciones */
jalar
tirar b
tirar c
di a a + b ab a / ba ** b (a + b) / (a + c) a% ca // c

además de los operadores habituales de suma, resta,

http://www.disc.ua.es/~gil/rexx.html 10/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

multiplicación y división, tenemos la exponenciación (**), la


división entera (%) y el resto (//).

6.2.- dígitos numéricos


--------------------

Como se ha dicho antes, REXX trabaja internamente con 9


dígitos significativos. Pero esta cantidad puede alterarse en
cualquier momento para satisfacer necesidades especiales de
cálculo. Por ejemplo, en cálculos con números enteros grandes
desearíamos representaciones exactas, sea cual sea el número
de dígitos que se requieran.

Naturalmente, usar un elevado número de dígitos significativos


tiene su precio en tiempo de cálculo. A veces, este precio no
compensa de la pérdida de prestaciones, mientras que otras es
imprescindible. Cada caso debera ser valorado, pero podemos
hacernos una idea con el siguiente programa que calcula el
tiempo necesario para realizar una operacion sencilla, con 9
dígitos y con 18 dígitos:

/* perdida de velocidad al aumentar número de dígitos */


a = 1
b = 7
t = tiempo ('E')
do i = 1 a 100000
c = a / b
fin
decir tiempo ('E')
t = tiempo ('R')
dígitos numéricos 18
do i = 1 a 100000
c = a / b
fin
decir tiempo ('E')

En mi máquina, el primer bucle se ejecuta en 3.40 segundos, y


el segundo en 4.50. Digamos que la función `time' tiene 8
formatos distintos, y que nosotros hemos usado las formas
time('E') y time('R'). La primera pone en marcha un
cronómetro, si no está ya corriendo, o da el tiempo
transcurrido, en caso contrario. La segunda pone el cronómetro
a cero y lo inicia. Por último, la función digits() devuelve
el valor que fue establecido con `numeric digits'.

6.3.- Fuzz numérico


------------------

Imaginemos que deseamos sumar 1/3 + 1/3 + 1/3.


Independientemente de cual sea la precisión elegida, nunca
obtendremos 1, sino algo como 0.999999999. O imaginemos que
obtenemos dos números, procedentes de dos cálculos distintos.
Uno de ellos puede ser 0.2324256 y el otro 0.2324356.
Obviamente no son iguales, ¿o si?. Depende de qué precisión
usemos para las comparaciones. Dependiendo de qué aplicación
estemos ejecutando, dos números como los anteriores pueden
ser, o no, considerados como iguales. Este detalle se controla
mediante `numeric fuzz n', donde n es un entero entre 0 y el
valor que éste usando `numeric digits' menos 1. Por defecto,
vale 0, lo que indica que no se descartará ningún dígito
durante la comparación. Un valor distinto de cero indica que
número de dígitos serán descartados cuando se realicen las
comparaciones. Considérese el siguiente ejemplo:

/* efecto de numeric fuzz */


numérico fuzz 0
di 1 + 1/3 + 1/3 + 1/3 = 2 / * '0', falso * /
fuzz numérico 1
say 1 + 1/3 + 1/3 + 1/3 = 2 /* '1' cierto */

http://www.disc.ua.es/~gil/rexx.html 11/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

6.4.- La función format()


-------------------------

Aunque no es estrictamente aritmética, incluyo en este punto


una descripción de la función format(), que controla la forma
en que se imprimirán en pantalla los números. En su forma más
simple, format() toma tres argumentos: el número que será
impreso, el número de espacios antes del punto decimal y el
número de espacios después del punto decimal. Si el número de
espacios antes del punto es insuficiente, se genera un error.
Si el número de espacios solicitados excede al necesario, se
añaden espacios en blanco. En cuanto al espacio tras el punto
decimal, si se ha solicitado un espacio mayor del necesario,
se añaden ceros hasta completar ese espacio, y si el espacio
es insuficiente, el número se redondea. Ejemplo:

/ * formato de uso () * /
a = 3.1415
decir formato (a, 1,1)
decir formato (a, 1,6)
decir formato (a, 4,4)

Es posible usar format() para obtener salidas numéricas en


formas de columna, que son más fáciles de leer. Si format()
necesita descartar algunos decimales, realizará un redondeo
convencional, pero si lo que deseamos es truncar, usaremos la
función trunc(), donde se especifica el número de cifras
decimales que se usarán. La diferencia entre format() y
trunc() se ve claramente en el siguiente fragmento de código:

/* diferencia entre trunc() y format()


a = 3.145
decir formato (a, 1,2)
decir trunc (a, 2)

7.- Entrada/Salida con REXX.


=========================

7.1.- Escribir/Leer líneas


--------------------------

Hay dos diferencias fundamentales entre REXX y otros lenguajes


de programación, que lo hacen muy agradable de usar. La
primera, es que el intérprete lleva cuenta de qué archivos hay
abiertos, y se ocupa de cerrarlos al terminar el programa si
es que no se hace explícitamente. Por tanto, no es preciso
abrir explícitamente un archivo para poder usarlo. La segunda,
es que en REXX los archivos son meras secuencias de bytes, con
una marca que indica el final de cada cadena y que no es más
que un byte específico. Por tanto, no hay archivos con tipo.
Se escriben y se leen caracteres individuales, o cadenas de
ellos. Conceptualmente es la misma idea que tiene el sistema
UNIX de lo que son archivos.

Comenzamos con un ejemplo sencillo, un programa que lee este


archivo y lo muestra en pantalla:

/* uso de archivos */
archivo = "rexx2.txt"
buffer = "-"
linein (archivo, 1.0)
do while buffer = ""
a = linein (archivo ,, 1)
decir un
fin

La función encargada de leer una línea de un archivo es


`linein()', que toma tres argumentos: el nombre del archivo,

http://www.disc.ua.es/~gil/rexx.html 12/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

la línea que va a leerse y el número de líneas que se desean


obtener. Si el archivo no esta abierto, la primera llamada a
linein() lo abre, y coloca a 1 la variable que lleva la cuenta
de qué línea es la siguiente que se va a leer. Así, una
llamada como linein(archivo,1,0) abre el archivo, inicia el
contador de líneas a 1 *pero no lee ninguna línea*, de ahí el
valor 0 en el tercer parámetro. En sucesivas lecturas, la
variable que indica la siguiente línea a leer se actualiza
automáticamente, de manera que no hay que especificarla.
Naturalmente, en cualquier momento puede especificarse qué
línea se desea leer. Si es la línea `n', basta con escribir
linein(archivo,n,1). Como segundo ejemplo, veamos un programa
que lee las líneas de este archivo y a continuación las
escribe en orden inverso:

/* leer y escribir en orden inverso */


contador = 0
buffer = "-"
archivo = "rexx2.txt"
linein (archivo, 1.0)
do contador = 1 por 1
a.contador=linein(archivo,,1)
si a.contador = "" luego se va
fin
del contador2 = contador a 1 by -1
di a.contador2
fin

Obsérvese por otra parte que un error de lectura, producido


por querer leer más alla de los límites del archivo, se
traduce en que linein() devuelve la cadena nula.

En cuanto a la escritura de archivos, se efectúa mediante la


función lineout(), que toma como parámetros el nombre del
archivo, la cadena que se desea escribir y la posición. El
valor devuelto es 0 si todo fue bien o 1, si hubo algún error.
Una primera llamada a lineout() abre el archivo, si no se
encontraba abierto, y coloca el puntero de escritura al final
del archivo, de manera que la siguiente lectura se añadirá al
archivo. Téngase en cuenta que si se especifica un lugar
concreto para escribir la cadena, y la cadena que ocupa ese
lugar tiene una longitud menor de la que pensamos escribir,
los datos del archivo pueden corromperse. En entornos DOS,
lineout() añade un carácter nueva línea y un carácter de
retorno de carro al final de cada línea. El siguiente
ejemplo lee este archivo y lo escribe en orden inverso en
el archivo llamado "inverso.txt":

/* leer un archivo y escribir en otro en orden inverso */


contador = 0
buffer = "-"
entrada = "rexx2.txt"
salida = "inverso.txt"
linein (entrada, 1,0)
do contador = 1 por 1
a.contador = LineIn (en ,, 1)
si a.contador = "" luego se va
fin
/* escribir en orden inverso */
del contador2 = contador a 1 by -1
call lineout salida, a.contador2
si RESULTADO = 1, entonces salga
fin
lineout(salida)

Obsérvese que no hay llamada previa a lineout(). En la primera


de ellas, el archivo es abierto y el puntero de escritura
colocado. Por otra parte, se observará también que se ha
llamada como subrutina, para disponer del resultado de la

http://www.disc.ua.es/~gil/rexx.html 13/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

operación en la variable RESULT y decidir si abandonar o no el


bucle. Igualmente se podría haber escrito:

resultado=lineout(salida,a.contador2)
si resultado = 1, entonces salga

Finalmente, obsérvese cómo se ha cerrado explícitamente el


archivo de salida, aunque no hubiese sido necesario.

Existen las funciones homologas de linein() y lineout() para


trabajar a nivel de caracteres, no de líneas, y son charin() y
charout(). Además, existen algunas otras funciones
misceláneas que pueden ser útiles. Por ejemplo, lines()
devuelve 1 si quedan líneas por leer de un archivo, y 0 en
caso contrario. La función stream() permite obtener
información sobre un archivo, pero su descripción merece
espacio aparte.

7.2.- La función stream()


-------------------------

La sintaxis de stream() es stream(nombre,operación,comando).


`nombre' es el nombre del archivo. `operación' puede ser una
de las letras `C', `S' o `D'. El primer caso indica que va a
ejecutarse un comando sobre el archivo, y *sólo* en este caso
se proporciona el tercer argumento, que puede ser uno de entre
OPEN, CLOSE, SEEK. OPEN sirve para abrir el archivo. Por
defecto, en modo de lectura/escritura. Si se desea abrir un
archivo para sólo lectura se puede añadir la palabra `READ', o
`WRITE' si el archivo será de sólo escritura. `CLOSE' cierra
el archivo. Finalmente, `SEEK' coloca el puntero de
lectura/escritura en la posición especificada. Esta posición
puede contarse a partir de la posición actual (adelante o
atrás), desde el inicio del archivo o desde el final del
archivo. Algunos ejemplos:

stream(nombre,'C','seek =2') /* puntero en la segunda línea */


stream(nombre,'C','seek <2') /* puntero en la segunda línea */
/* contando desde el final */
stream(nombre,'C','seek +2') /* puntero dos líneas adelante */
stream(nombre,'C','seek -2') /* puntero dos líneas atras */

Otras posibilidades son preguntar si el archivo existe, su


tamaño o su fecha. Estos son los ejemplos:

stream (nombre, 'C', 'consulta existe')


stream (nombre, 'C', 'tamaño de consulta')
stream (nombre, 'C', 'query datetime')

Y el resultado es una cadena con la información pedida.

Por su parte, `S', devuelve una cadena indicando el estado del


archivo. Por ejemplo:

corriente (nombre, 'S')

y la salida puede ser `ERROR', `NOTREADY', `READY' y


`UNKNOWN'. `D' finalmente, es idéntico a `S', salvo que se
añade información adicional. En definitiva, éstas son las
posibilidades de transmisión ():

stream (nombre, 'C', abrir | cerrar | buscar + | - | = | <desplazamiento | consulta


, 'S'
,'RE'

8.- La cola.
============

Para REXX, cola tiene un significado distinto del habitual. En

http://www.disc.ua.es/~gil/rexx.html 14/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

realidad, es una pila, solo que, al contrario que en las pilas


convencionales, la información puede apilarse sobre el último
elemento de la pila o introducirse bajo el primero. De esta
forma, esta estructura particular puede funcionar como pila
LIFO y como pila FIFO simultáneamente. El tratamiento que hace
REXX de esta pila también es particular, aunque depende de la
implementación. En las implementaciones más sofisticadas, la
cola puede persistir aun después de haber acabado un programa
REXX, y quedar disponible para otros programas, bien escritos
en REXX, bien escritos en cualquier otro lenguaje, como C o
Pascal.

Existen tres funciones para trabajar con la cola. `QUEUE'


coloca una línea en la base de la cola. `PUSH' lo hace en la
cima de la cola (pila). De esta manera, los datos introducidos
mediante QUEUE después son leídos en el mismo orden en que
fueron escritos (FIFO), mientras que los datos escritos
mediante `PUSH' son leídos en orden inverso (LIFO). Existe una
única instrucción para leer, la orden `PULL'. Véase el
siguiente ejemplo:

/* uso de la cola en REXX */


cola "uno"
cola "dos"
jalar
tirar b
decir ab
presione "uno"
presionar "dos"
jalar
tirar b
decir ab
presione "uno"
cola "dos"
jalar
tirar b
decir ab

9.- Funciones de cadena.


========================

Como era de esperar en un lenguaje orientado a cadenas, las


funciones para el tratamiento de éstas son numerosas y de alto
nivel. Pueden agruparse en varias categorías, y en este punto
voy a limitarme a nombrarlas y describir su función, sin
entrar en detalles.

9.1.- Funciones para extraer subcadenas de una cadena


-------------------------------------------------- ---

- SUBSTR() extrae una subcadena de una cadena a partir de una


posición determinada
- LEFT() extrae una subcadena por la izquierda
- RIGHT() extrae una subcadena por la derecha
- WORD() extrae una palabra de una cadena, identificada por
su número
- SUBWORD() extrae una subcadena a partir de una determinada
palabra

9.2.- Funciones de edición


--------------------------

- INSERT() inserta una subcadena en una cadena


- OVERLAY() sobreescribe parte de una cadena con otra cadena
- REVERSE() invierte el orden de caracteres de una cadena
- COPIES() replica una cadena un número determinado de veces

9.3.- Funciones para borrar


---------------------------

http://www.disc.ua.es/~gil/rexx.html 15/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

- DELSTR() elimina una subcadena del interior de una cadena


- DELWORD() elimina una palabra dada

9.4.- Funciones para formateo de líneas


---------------------------------------

- SPACE() añade o elimina espacios en blanco


- CENTER() centra una cadena a una anchura determinada,
añadiendo espacios en blanco al principio, si es
necesario
- STRIP() elimina espacios en blanco al principio, final ,
o ambos, en una cadena

9.5.- Funciones para contar


---------------------------

- LENGTH() cuenta el número de caracteres de la cadena


- WORDS() cuenta el número de palabras de la cadena
- WORDLENGTH ()

9.6.- Funciones para comparar


-----------------------------

- VERIFY() dadas dos cadenas, determina el primer carácter


en que coinciden o difieren
- COMPARE() determina si dos cadenas son idénticas
- ABBREV() devuelve 1 si una cadena coincide con los
primeros caracteres de otra

9.7.- Funciones para buscar posiciones


--------------------------------------

- POS() explora una cadena buscando el lugar de comienzo


de una subcadena
- LASTPOS() igual que la anterior, pero comienza desde el
final, hacia atrás
- WORDINDEX() determina la posición del primer carácter de una
palabra dada

10.- REXX y su comunicacion con el entorno.


========================================

10.1.- Entrada desde teclado


----------------------------

Normalmente, en la ejecución de un programa interactivo, éste


pide al usuario que introduzca datos desde teclado. Los
programadores en otros lenguajes saben lo engorroso que puede
llegar a ser la depuración de estos datos de entrada, y por
eso REXX ofrece facilidades inéditas. Así, una cadena de
entrada puede dividirse automáticamente en sus componentes,
asignando cada palabra a una variable. Además, puede hacer un
pre-proceso de la cadena tecleada, pasándola a mayúsculas o
minúsculas antes de efectuar ninguna otra operación. Ya hemos
visto en algunos programas la instrucción `PULL'. En realidad,
esta instrucción es una abreviatura de `PARSE PULL', la
instrucción que lee una cadena introducida por el usuario.
Esta cadena puede pasarse a mayúsculas o minúsculas mediante
las variantes `PARSE UPPER' y `PARSE LOWER'. En su forma
normal, `PARSE PULL', la cadena no sufre modificaciones.
Veamos un ejemplo:

/* uso de parse */
di "introduce tu nombre"
parse pull nombre
si nombre == "Javier" entonces

http://www.disc.ua.es/~gil/rexx.html 16/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

di "Hola Javier"
más
decir "A ti no te conozco"

Si el usuario teclea "javier" o "JAvier" o cualquier cosa


distinta de "Javier", el programa declarará no conocer al
usuario. Cambiemos `parse pull' por `parse upper pull' y
cualquer cosa que escriba el usuario será pasada a mayúsculas
antes de hacer nada con ella, facilitando la identificación de
la cadena:

/* uso de parse */
di "introduce tu nombre"
analizar el nombre del tirón superior
si nombre == "JAVIER" entonces
di "Hola Javier"
más
decir "A ti no te conozco"

Como digo, al leer una cadena introducida por el usuario, las


subcadenas que la constituyen pueden individualizarse y
asignarse a variables. Véase el siguiente ejemplo:

/* uso de parse */
say "introduce tres o más nombres"
parse pull primero segundo tercero resto
say primero segundo tercero "/" resto

Cada palabra va a una variable, y la última variable contiene


lo que reste de la cadena en ese momento. ¿Cómo haríamos para
comprobar que se han introducido exactamente tres nombres? :

/* uso de parse */
correcto=0
say "introduce exactamente tres nombres: "
pull uno dos tres resto
seleccionar
cuando tres = "" luego diga "faltan nombres"
when resto \ = "" luego diga "sobran nombres"
de lo contrario correcto = 1
fin

Otra útil variante de `PARSE' es `PARSE VAR', que en lugar de


tomar su entrada del teclado, lo hace de una variable
previamente asignada. Por ejemplo:

/* uso de parse var */


frase="los cretinos son soltados al mundo en carretones"
parse var frase a b c d
say a b c d /* los cretinos son soltados */

10.2.- Argumentos en línea de comandos


--------------------------------------

Un programa REXX puede ser llamado con argumentos en línea de


comandos, y existe una forma fácil de asignar dichos
argumentos a variables del programa. De esto se encarga la
instrucción `ARG'. Por ejemplo:

/* lectura de argumentos */
arg primero segundo
say primero segundo

Si este programa es salvado con el nombre a.r y se llama como


"a.r nuboso soleado", la salida imprimirá "nuboso" y
"soleado".

Solo hemos hablado del tratamiento de texto, pero no hay

http://www.disc.ua.es/~gil/rexx.html 17/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

ninguna diferencia cuando se trata de números. Tomado


literalmente del manual de IBM: "La forma en que REXX maneja
los números es otra muestra de su flexibilidad. Muchos
lenguajes de computadora tienen reglas específicas para leer
texto y números. En REXX, un número es simplemente una cadena
que puede ser calculada".

10.3.- Comunicación entre programas


-----------------------------------

Un programa REXX puede llamar a otros programas REXX, o a


programas escritos en cualquier lenguaje compilado. Existe un
protocolo de comunicación que funciona en los dos sentidos, y
que permite extender aun más la potencia de REXX. Sin embargo,
este tema me parece demasiado específico para un documento
introductorio como éste, y por otra parte sólo dispongo de las
especificaciones para DOS, muy detalladas por cierto, que
vienen con la ayuda en línea de REXX para el Sistema Operativo
IBM PC-DOS 7.0, por lo que remito allí a los interesados.

10.4.- REXX como lenguaje para elaborar guiones


-----------------------------------------------

Una de las habilidades de REXX consiste en pasar órdenes al


Sistema Operativo para que éste las ejecute, y en capturar el
valor que éste devuelva. Puede aprovecharse entonces el
carácter estructurado de REXX y su potencia a la hora de
procesar cadenas para crear guiones ( .BAT o .CMD en DOS ) muy
elaborados, difíciles o imposibles de escribir de otro modo.

11.- Funciones incluidas.


======================

Son muchas las funciones incluidas en REXX, y muchas más las


que se ofrecen como programas REXX independientes junto con el
intérprete, de manera que puedan ser invocadas desde los
programas. En cuanto a las segundas, dependen claro está de la
implementación de que dispongamos, y pueden considerarse como
un obsequio.

Respecto a las primeras, hemos citado muchas a lo largo de


este documento, pero aún quedan más. Por ejemplo, las
funciones para operaciones lógicas bit a bit: BITAND(),
BITOR() y BITXOR(). Toman dos argumentos, y hacen exactamente
lo que su nombre sugiere.

También tenemos un conjunto de funciones de conversión, como


B2X(), para pasar de binario a hexadecimal, C2D() para pasar
de carácter a decimal, C2X() para pasar de carácter a
hexadecimal, D2C() de decimal a carácter, D2X() de decimal a
hexadecimal, X2B() de hexadecimal a binario, X2C() de
hexadecimal a carácter y X2D() de hexadecimal a decimal.

Algunas funciones matemáticas misceláneas como SIGN(), que


devuelve el signo de un número o expresión, ABS() que devuelve
el valor absoluto de un número y RANDOM() que devuelve un
número pseudo-aleatorio entre dos límites que pueden
definirse.

12.- REXX como lenguaje dinámico.


==============================

Una interesante característica de REXX es que un programa


escrito en este lenguaje puede modificarse a sí mismo en
tiempo de ejecución, y llevarse a cabo las nuevas
instrucciones. He aquí un ejemplo:

/* uso de la orden interpret */


dato = 'hacer 3; di "Hola a todos"

http://www.disc.ua.es/~gil/rexx.html 18/19
13/9/2018 www.disc.ua.es/~gil/rexx.html

interpretar dado

Este programa crea una cadena, y a continuación la ejecuta


como si fuese una línea más del programa. Otro:

/* uso de la orden interpret */


dato = altura
interpretar altura '= 180'

En este caso se crea la variable `altura' y se le asigna el


valor 180.

13.- Para terminar.


===================

Esta introducción está lejos de ser exhaustiva. Ni siquiera


completa. Pero puede que te haya servido en tus primeros pasos
con REXX, y te anime a mirar por la red más documentación, y
a escribir programas en este lenguaje, experimentando sus
muchas ventajas: aritmética de precision arbitraria e
independiente de la plataforma, estructuras de control
flexibles, tratamiento avanzado de cadenas, integración con
su entorno, elegante tratamiento de arrays y estructuras
de datos definidas por el usuario, etc.

Si te interesa lo suficiente, te recomiendo dos adquisiciones.


El manual "REXX User Guide and Reference", de IBM, es un
librito asequible y completo al mismo tiempo, con mucho código
de ejemplo, y con capítulos estructurados en dos partes, parte
básica y parte avanzada. La segunda adquisición es el sistema
operativo PC-DOS 7.0. Aparte de ser el mejor DOS que se ha
hecho, tiene un intérprete REXX muy bueno y un completísimo
sistema de ayuda sobre el lenguaje. Sólo este elemento
justifica el precio del sistema. Pero además tienes un editor
fantastico ( el famoso `E' ) y muchas y buenas sorpresas que
no encontraras en MS-DOS. Vale la pena.

Como soy el único responsable de los errores u omisiones que


puedas encontrar aquí, te ruego que me lo hagas saber a través
de mi dirección: gil@disc.ua.es.

Enero de 2001.

http://www.disc.ua.es/~gil/rexx.html 19/19

Anda mungkin juga menyukai