Rexx se používá jako makrojazyk editorů a textových procesorů;
jako
povelový jazyk databázových, dialogových, operačních systémů a systémů
řízení počítačové sítě; jako jazyk pro tvorbu skriptů umožňujících
automatizovat a integrovat aplikace. V tomto článku bych ale chtěl ukázat,
že Rexx je
také zajímavý programovací jazyk. Jako formu výkladu jsem zvolil svérázný
úvod do jazyka, ve kterém se soustředím především na neobvyklé
výrazové prostředky, nevyskytující se ve svém souhrnu v jiném
programovacím jazyce.
Místo kapitoly "Typy, konstanty a proměnné"
V programech napsaných v jazyce Rexx chybí deklarace a typy,
protože:
- veškeré údaje jsou znakové řetězce proměnné délky;
- symbol je vyhlášen za jméno proměnné, až když je mu
přiřazena hodnota při provádění programu;
- operandem aritmetické operace je řetězec znaků, který je zápisem
desítkového čísla s případnými mezerami na začátku a na konci;
- přesnost výpočtu - počet platných číslic výsledku aritmetické operace -
lze zadat příkazem numeric digits.
Přesnost výpočtu
Kolik číslic má číslo N-faktoriál a jak vypadá prvních třináct
číslic? Pro N rovno 16 nejprve vypočteme číslo 16!, pak sečteme počet
znaků v zápise čísla a nakonec přečteme prvních třináct znaků zleva. Ale
co když je N rovno 1000? Odpověď vypíše na obrazovku následující program příkazy
say.
/* Rexx program UHLER */
Vysledek = NFAKT(1000)
say "Počet číslic =" LENGTH(Vysledek)
say "Prvních třináct číslic =" LEFT(Vysledek, 13)
|
V zápise programu je mezera, například za konstantou
"Počet číslic =",
operátorem operace zřetězení s vložením jedné mezery. Program je tak
krátký, protože jsem využil funkce LEFT a LENGTH, které
jsou součástí
jazyka a nazývají se proto vestavěné (built-in). Ale funkce
NFAKT není
vestavěná
a pokud tuto funkci nenapsal už někdo před námi a není při provádění
programu UHLER dostupná, musíme si ji napsat sami.
/* Rexx funkce NFAKT - výpočet faktoriálu */
N = ARG(1)
numeric digits 3000
Nfaktorial = 1
do J = 2 to N
Nfaktorial = Nfaktorial * J
end
return Nfaktorial
|
Funkce uložená na disku jako samostatný soubor, se nazývá
vnější. Připojíme-li za poslední řádek programu UHLER následující
dva řádky:
a dále text funkce NFAKT, bude NFAKT funkcí vnitřní.
Příkaz exit explicitně ukončuje provádění hlavního programu,
které by jinak
pokračovalo chybným vykonáním příkazu procedure s návěštím
NFAKT; příkaz procedure bez klíčového slova
expose určuje, že
volající program a volaná funkce nesdílí žádné proměnné.
Spustíme-li program UHLER, objeví se na obrazovce:
Počet číslic = 2568
Prvních třináct číslic = 4023872500770
|
V řešení čtvrtého cvičení v odstavci 1.2.5 knihy D. E. Knutha The Art
of Computer Programming, díl 1., čteme: Přesnou hodnotu
1000! určil jako první H. S. Uhler po mnohaletých trpělivých výpočtech na
kalkulátoru. Výsledek uveřejnil v časopise Scripta Mathematica
21 (1955), ss. 266-267 a začíná: 402 38 726 00 770 ...
Cvičení 1
Operátorem operace umocnění je **. Kolik číslic má číslo
devět na devátou na devátou, tj. 9**(9**9)?
Zpracování slov
Rexx definuje slovo jako neprázdný řetězec znaků, který
neobsahuje mezeru. Práci se slovy usnadňují vestavěné funkce a příkaz
parse.
Například funkce WORDS(řetězec) vrací počet slov řetězce.
Můžeme si to ověřit, zadáme-li jako argument větu, jednoznačně definující
číslo K takto: Buď K nejmenší přirozené číslo, které nelze definovat
méně než čtrnácti českými slovy. Výsledek - pointa Berryho
antinomie ze skript E. Fuchse Teorie množin (UJEP Brno, 1974)
- nás donutí slova ve větě opravdu spočítat.
Ale co když oddělovačem slov bude jiný znak než mezera?
Předpokládejme, že oddělovač (právě jeden znak) je uložen v proměnné
Oddelovac. Pak následující fragment programu vypíše všechna
slova zadaného textu v tomto obecném případě.
Om = Oddelovac || " "; Mo = REVERSE(Om)
Text = TRANSLATE(Text, Om, Mo)
do J = 1 to WORDS(Text)
say TRANSLATE(WORD(Text, J), Om, Mo)
end
|
Vestavěná funkce WORD(řetězec, n) vrací n-té
slovo řetězce.
Symbol || je operátor operace zřetězení bez vložení mezery.
A funkce REVERSE?
Vzpomeňte si na Příběh z Rozeklaných hor od Edgara Allana Poa;
... - vždyť Bedlo bez e - co to je jiného než Oldeb obráceně?
Následující program zobrazí na displeji OLDEB.
/* BEDLO */ say REVERSE(LEFT(Bedloe, 5))
|
Funkce TRANSLATE(řetězec, šifrovaná_abeceda,
abeceda)
zamění
každý v řetězci se vyskytující znak z abecedy odpovídajícím
znakem z šifrovací_abecedy (jde o jednoduché substituční šifrování).
Když bude v proměnné Pergamen uložen kryptogram kapitána Kidda, ze
slavné povídky Zlatý brouk Edgara Allana Poa, začínající řetězcem znaků:
53++!305))6*;, pak následující příkaz
say TRANSLATE(Pergamen, ,/* pokračování příště */
"adeghinortlsbpvwmuycf", "5!8346*+(;0)2.']9?:-1")
|
nám odkryje záhadu pergamenu. Řešení začíná: agoodglassint.
Příkaz interpret
Příkaz interpret výraz nejprve vyhodnotí výraz
a výsledný řetězec znaků provede jako příkaz nebo fragment programu.
Program USHER vytváří prostředí, ve kterém můžeme zadat
interpretu jazyka Rexx k provedení příkazy (i příkazy operačního systému)
nebo jednořádkové programy.
/* Rexx program USHER */
do forever
say 'Zadej fragment Rexx programu:'
parse pull Fragment
call INTERPRETUJ Fragment
end
/* Subroutine INTERPRETUJ */
INTERPRETUJ: procedure
interpret ARG(1)
return
|
Příkaz parse pull přijme řádek textu odeslaný z klávesnice
a příkaz interpret jej vykoná. Můžeme tedy například napsat:
if RANDOM(1, 2) = 1 then say "Hlava"; else say "Orel"
|
Totéž je v manuálu uvedeno jako samostatný program. USHER
nás naučí spoustu věcí aniž bychom museli použít editor. Při
vyhodnocování výrazů a studiu vestavěných funkcí nám pomůže následující
program, zapsaný na jednom řádku jako vstup pro program USHER:
do until Exp = ""; say "?"; parse pull Exp; interpret "say" Exp; end
|
Uveďme si příklad možné konverzace:
?
1 + (2 * 12)
25
?
"[" || SPACE(" Edgar Allan Poe ") || "]"
[Edgar Allan Poe]
|
Povšimněte si, že jméno proměnné, v tomto případě Exp, může být
vytvořeno až
za běhu programu. Příkaz procedure v programu USHER
zajistí, aby dynamicky vytvořené jméno proměnné existovalo jen po dobu
provádění podprogramu INTERPRETUJ. Chceme-li, aby hodnota určité
proměnné, nazvěme ji třeba Global, byla dostupná i při dalším
volání podprogramu, musíme změnit jeho první řádek na:
INTERPRETUJ: procedure expose Global
|
... i dospělý člověk je schopen hypnoticky poslechnout návod na podivně
vyhlížející věcičce "Malý kouzelný vyrážeč pojistek: Vlož do zásuvky a
ZASUŇ!", napsal B. Higman v knize Porovnávací studie
programovacích jazyků. Nejprve si zjistěte, jakým způsobem můžete
přerušit aktivní proces ve vámi používaném systému, potom zadejte:
/* Pád do Malströmu */ interpret ARG(1)
|
a pak si zkuste představit, co vlastně prováděl interpret jazyka, který
"hypnoticky poslechl" vás.
V programu USHER je použit "nekonečný" cyklus se záhlavím
do forever. Jak se vlastně ukončí jeho provádění? Zadáme-li
příkaz exit nebo dopustíme-li se například syntaktické chyby.
Druhé "řešení" ukazuje, že program USHER je nutné ještě rozšířit.
Pro ošetření chyb je v jazyce Rexx k dispozici příkaz signal.
Při rozšiřování programu USHER se můžete inspirovat programy
REXXTRY v systému OS/2.
Využitím interpretace v programování (pro zkrácení textu programu)
se zabývá J. Bentley v knize Perly programování.
Cvičení 2
Podle F. M. Whyta se typ podvozku parní lokomotivy
označoval
trojčíslím. Například u lokomotivy Santa Fe třídy 3700 z roku 1917 (podívejte
se na obrázek):
Santa Fe 3700 (4-8-2) měla 14 kol
|
bylo uspořádání kol popsáno jako 4 - 8 - 2. První a poslední
číslo ukazovalo počet nespřažených předních a zadních kol, prostřední číslo
ukazovalo počet spřažených neboli hnacích kol. Napište příkaz, který pro
správně zadanou hodnotu typu podvozku, uloženou v proměnné Typ, určí, kolik
měla lokomotiva celkem kol a výsledek zobrazí.
Funkce SOURCELINE
V článku Reflections on Trusting Trust (CACM, č. 8 1984) se K.
Thompson zmiňuje o úloze napsat co nejkratší program, který by na výstupu
produkoval svou věrnou kopii. Nejkratší program v jazyce Rexx, který
splňuje zadání, je (v prostředí TSO/E by musela být úvodní poznámka
/*Rexx*/):
Funkce SOURCELINE(n) vrací hodnotu n-tého řádku
programu. Předpokládejme, že máme zobrazit zprávu:
+-------------------+
| Zvýrazněná zpráva |
+-------------------+
|
Můžeme napsat program s třemi příkazy say. Výhodu složitějšího
programu POZOR poznáme při opakovaných změnách zprávy.
/* Rexx program POZOR
+-------------------+
| Zvýrazněná zpráva |
+-------------------+
*/
do J = 2 while SOURCELINE(J) <> "*/"
say SOURCELINE(J)
end
|
V jazyce Rexx je i řádek programu znakovým řetězcem přístupným k
zpracování.
Cyklus s více výstupy
Užitečnost takového cyklu dokazoval G. V. Bochmann v práci Multiple
Exits from a Loop Without the GOTO (CACM, č. 7 1973) na řešení
úlohy: Je dán vektor N čísel, najděte index prvního, například nulového,
prvku. O čtrnáct let později byl publikován dopis F. Rubina redakci časopisu
CACM (č. 3, 1987), ve kterém je, jako příklad efektivního použití příkazu
goto, uvedeno řešení úlohy: Je dána matice typu NxN, najděte index prvního nulového řádku.
Je možné, že v roce 2001 (1987 + 14) vyjde příspěvek
o výhodách strastiplného putování programem s příkazy goto, obsahující
úlohu: Je dáno trojrozměrné pole M matic typu NxN, najděte index první
nulové matice. Program ODYSEA ukazuje řešení v jazyce Rexx už nyní.
/* Rexx program ODYSEA */
M = 6; N = 100
X. = 0
X.1.1.1 = 1; X.2.94.15 = 1; X.3.16.8 = 1
X.4.5.2 = 1; X.5.16.30 = 0; X.6.86.3 = 1
do I = 1 to M
do J = 1 to N
do K = 1 to N
if X.I.J.K <> 0 then iterate I
end
end
leave I
end
if I<=M then say I || ". matice je nulová"
else say "Nulová matice neexistuje"
|
Příkaz iterate I ukončuje provádění vnitřních cyklů a výpočet pak
pokračuje další iterací hlavního cyklu s řídicí proměnnou I.
Příkaz leave I ukončuje provádění hlavního cyklu s řídicí
proměnnou I.
Příkaz cyklu v kombinaci s příkazy iterate a leave
umožňuje přehledně zapsat i neobvyklé řídicí struktury (viz obrázek), jinak známé jen
z teorie, například z dodatku J. Hořejše ke knize Z. Manny
Matematická teorie programů.
Zápis X.I.J.K označuje prvek pole X (obdoba zápisu
X(I, J, K) ve FORTRANu nebo X[I, J, K] v Pascalu). Tak
jako kterákoliv jiná proměnná i prvek pole alokuje paměť, až když je mu
přiřazena hodnota. V případě programu ODYSEA alokuje paměť pouze
6 prvků.
Zajímavý je případ, když se hodnota přiřadí přímo jménu pole:
V dalším průběhu provádění programu pak bude každý prvek pole, kterému
nebyla explicitně přiřazena hodnota, považován za nulový.
Asociativní pole
Vnitřní funkce SLOVA určí všechna slova, která se v zadaném textu
vyskytují alespoň K-krát.
/* Rexx funkce SLOVA */
SLOVA: procedure
Text = ARG(1); K = ARG(2)
Seznam. = 0
Vysledek = ""
do J = 1 to WORDS(Text)
Slovo = WORD(Text, J)
Seznam.Slovo = Seznam.Slovo + 1
if Seznam.Slovo = K then Vysledek = Vysledek Slovo
end
return Vysledek
|
Hodnotou proměnné Slovo může být obecně libovolný řetězec znaků. Seznam.Slovo pak označuje prvek pole určený touto hodnotou
jako indexem.
M. F. Cowlishaw, autor jazyka Rexx, uvedl v článku The design of
the Rexx language. IBM Systems Journal,
r. 23, č. 4 1984, ss. 326-335, příklad vnitřní funkce,
která vypustí duplicitní slova v posloupnosti slov. Jde o speciální
případ funkce SLOVA pro K = 1.
jen1: procedure
parse arg text
seznam. = 0
vysledek = ''
do while text <> ''
parse var text slovo text
if seznam.slovo then iterate
seznam.slovo = 1
vysledek = vysledek slovo
end
return vysledek
|
Jak vidíte, jedno a totéž je možné v jazyce Rexx vyjádřit různými
prostředky a sami si můžete zvolit konvenci pro zápis textu programu.
Při provádění programu se malá písmena abecedy
převedou na velká. Výjimku tvoří text uzavřený do apostrofů nebo
uvozovek - zápis řetězcové konstanty - a text poznámky.
Cvičení 3
Práce na textu článku zahrnuje rušení, přesuny a
doplňování vět. Často se mi stává, že stejná formulace je
nakonec uvedena v téměř sousedících větách. Upravte funkci SLOVA
na podprogram, který zobrazí všechny posloupnosti slov začínající a končící
týmž slovem a obsahující zadaný počet K nebo méně slov. Využijte
vestavěnou funkci SUBWORD( řetězec, n, d),
která vrací podřetězec řětězce obsahující d slov řetězce
počínaje jeho n-tým slovem.
Co je až tak zajímavého na jazyku Rexx ...
..., aby stálo za to se jej učit?
Programy v jazyce Rexx jsou snadno přenositelné do prostředí jiných operačních
systémů (důkaz najdete na webovské stránce tvůrce jazyka M. F. Cowlishawa).
Výrazovými prostředky jazyka lze zapsat i složitý algoritmus krátkým
programem.
Krátké programy mohou být užitečné jako prototypy.
Rexx umožňuje vytvářet různá prostředí. Příkladem může být
program USHER - prostředí pro výuku a studium jazyka
a interakci s operačním systémem.
Je vhodný pro výuku programování. Domnívám se, že by mohl být
vynikajícím pomocníkem při studiu základů kybernetiky, matematické teorie
programů nebo konstrukce kompilátorů (viz implementace univerzálního Turingova stroje).
Pokusem o ucelený důkaz možností jazyka při popisu, definici a kódování algoritmů je Album algoritmů a technik.
Je ideálním nástrojem pro vývoj a testování algoritmů. Praktickou ukázkou je můj článek FIND, SELECT, MODIFIND.
Začínající programátor(ka) bude považovat asociativní pole
a interpretaci za přirozené prostředky, i když J. Bentley zahrnul jejich
využití mezi perly programování.
B. W. Kernighan a P. J. Plauger radí v knize The Elements of
Programming Style (v druhém vydání je to dvacáté první
přikázání): Program napište nejprve v jednoduchém pseudojazyce a
teprve potom jej převeďte do jazyka, který používáte. Pro mě je
jazyk Rexx ideálním "pseudojazykem": vyhovuje mi jeho syntaxe a
správnost i fragmentů programů může otestovat počítač.
|
Přitom
všechna kouzla jazyka vyplývají z důsledného uplatňování jen
několika málo obecných výrazových prostředků. V povídce Zánik
domu Usherů napsal E. A. Poe: ... nesnášel žádnou hudbu kromě
jistých akordů hraných na strunné nástroje. Snad právě z tohoto
úzkého výběru, kterým se také omezoval při hře na kytaru, vznikl
do značné míry fantastický styl jeho přednesu.
Řešení cvičení
1. Program KOLIK
/* Rexx program KOLIK */ say 9 ** (9 ** 9)
|
zobrazí na displeji 4.28124773E+369693099; 9**(9**9) má tedy 369 693 100 číslic.
2.
interpret "say -(-" Typ")"
|
3.
do J = 1 to WORDS(Text)
Slovo = WORD(Text, J)
L = Seznam.Slovo
if L <> 0
then do
V = J - L + 1
if V <= K then say SUBWORD(Text, L, V)
end
Seznam.Slovo = J
end
|
Jak vyslovovat slovo "Rexx"?Gerard Shildberger z Hankinsonu v Severní Dakotě mi na tento dotaz odpověděl: REXX se vyslovuje jako "hex" ale s "R" na začátku. Rýmuje se to třeba se slovem flex (kabel). Mike Cowlishaw nazval svůj jazyk REX, ale tento název už byl chráněnou značkou a tak firma IBM přidala další "X".
|
Poznámky a poděkování
První verze "Úvodu ..." byla publikována v časopise Softwarove noviny, r. 6, č. 12 (1995), 114-118. Některé informace jsem převzal ze svých příspěvků uveřejněných v Archive of REXXLIST
REXX & E. A. Poe 98/10/09, Complements to Words, words, words
98/10/26. Dále jsem použil tuto literaturu:
M. F. Cowlishaw: The REXX Language - A Practical Approach to Programming
Prentice-Hall, inc., Engelwood Cliffs, New Jersey 1985
J. Bentley: Associative Arrays
CACM, Vol. 28, No. 6 (June 1985),
570-576
F. Clarke: Words, words, words...
Helpy z Personal REXX for Windows(TM) Version 3.50, Quercus System
H. F. Ledgard, M. Marcotty: A Genealogy of Control Structures
CACM, Vol. 18,
No. 11 (November 1975), 629-639
|