Programmeren in REXX/Bevelen, deel 1
Dit hoofdstuk geeft meer uitleg bij een eerste reeks veel gebruikte REXX bevelen.
SAY
bewerkensay parameter
We beginnen met het eenvoudigste bevel, namelijk say. De instructie zal alles wat als parameter is meegegeven, na interpretatie, op het scherm afdrukken. De parameter kan zijn opgebouwd uit constanten, variabelen of een combinatie ervan.
In dit voorbeeld hebben we zelfs een Carriage Return en Line Feed (kortweg CrLf) toegevoegd om het afdrukken op 2 regels te laten geschieden:
/* Voorbeeld voor SAY */ crlf='0D0A'x say 'Hallo !'crlf'Tot ziens !'
De zinnen «Hallo !» en «Tot ziens !» komen onder elkaar op het scherm te staan.
EXIT
bewerkenHet exit bevel beëindigd het programma. Er kan desgevallend een geheel getal als parameter worden meegegeven. Dit getal stelt dan de returncode van het programma voor. Zonder parameter is de returncode steeds nul (0). Deze returncode kan opgepikt worden door de oproeper. Windows maakt er spijtig genoeg geen gebruik van en toont de returncode niet.
DO
bewerkenEen DO bevel duidt het begin aan van een reeks samenhorende statements. De reeks moet worden afgesloten door een END bevel.
Men noemt dit een DO-blok.
DO-blokken kunnen andere DO-blokken bevatten (genest zijn). Het is een goede gewoonte om de statements binnen het blok te laten inspringen om de leesbaarheid te vergroten.
Fundamentele vorm
bewerkenDe meest fundamentele vorm dient uitsluitend om statements logisch bij elkaar te houden:
do say 'Hello' say 'Goodbye' end
Deze constructie komt men veel tegen in combinatie met bv. het if bevel dat we wat verder zullen leren gebruiken.
if 7>5 then do say 'Hello' ; say 'Goodbye' end
Merk ook op dat het aligneren van "do" en "end" de leesbaarheid bevorderen. We zijn soms ook voorstander van volgende schrijfwijze:
if 7>5 then do say 'Hello' ; say 'Goodbye' end
Ze heeft het voordeel compacter te zijn (smallere marges), en wij beschouwen het als zijnde een IF-blok.
Itereren
bewerkenEen DO-blok kan ook dienen om de reeks statements een aantal maal te laten uitvoeren. In zijn meest eenvoudige vorm bepaalt een parameter het aantal iteraties dat moet worden uitgevoerd:
do 7 ; say 'Hello' ; end
Het aantal iteraties kan ook aangegeven worden via een variabele. Het moge duidelijk zijn dat de waarde van de variabele een geheel getal moet zijn, anders zal men een REXX fout krijgen (Code 26: Invalid whole number).
a=7 do a ; say 'Hello' ; end
We kunnen ook een variabele opgeven die dan de teller van de lus wordt. Deze teller kan desgevallend binnen de lus gebruikt worden. We bepalen daarbij ook een begin- en eindwaarde.
a=1 do i = 1 to 10 /* bereken 10 faculteit */ a = a * i end
Deze lus zal 10 maal doorlopen worden, waarbij de variabele i respectievelijk de waarde 1, 2, 3, ... 10 zal bevatten. Het resultaat van deze lus is dat we 10! (faculteit) hebben berekend.
Een alternatieve schrijfwijze bekomt men met het for sleutelwoord. Dit is dan ook gelijkwaardig aan vorige voorbeeld:
a=1 do i = 1 for 10 /* bereken 10 faculteit */ a = a * i end
Bijkomend kunnen we een stapwaarde opgeven waarmee de teller bij elke iteratie moet worden verhoogd. Zonder dat is de stap natuurlijk altijd 1.
a=1 do i=2 to 10 by 2 a=a*i /* we berekenen 2 * 4 * 6 * 8 * 10 */ end
Deze stapwaarde kan ook negatief zijn, maar dan moet men van een groot getal naar een klein getal itereren, anders zal men de lus nooit meer verlaten.
a=1 do i=10 to 2 by -2 a=a*i /* we berekenen 10 * 8 * 6 * 4 * 2 */ end
Eindeloze lussen
bewerkenMet do forever geven we aan dat er oneindig lang geïtereerd moet worden. Uiteraard kan dat nooit de bedoeling zijn. We zullen dus op een bepaald ogenblik uit de lus moeten springen. Dit kan bereikt worden door het bevel leave te gebruiken. Meestal zal dit gebeuren na het testen van een voorwaarde, zoals in dit voorbeeld:
a=2 do forever a=a*a /* berekenen van de machten van 2 */ if a>256 then leave /* maar stoppen als we voorbij 256 komen */ end
Iteratiestappen overslaan
bewerkenWe kunnen naar een volgende iteratie gaan (en dus een gedeelte van de lus niet behandelen) mits gebruik van het iterate bevel. In het volgende voorbeeld lijsten we alle machten van 2 tussen 1 en 10, maar niet de 5-de macht.
do i = 1 to 10 say i if i=5 then iterate say 2**i end
Iets gelijkaardigs zou kunnen bekomen worden door de lus-variabele i te manipuleren, doch dit houdt gevaren in en maakt het programma minder leesbaar.
Geneste lussen
bewerkendo i = 1 to 10 do j = 1 to 2 say i*j end j end i
Merk op dat we de lusvariabele kunnen opgeven bij het end bevel. Dit verhoogt zeker de leesbaarheid indien er veel programmalijnen in de lus zitten. De laatst begonnen lus moet natuurlijk eerst sluiten. We kunnen de lussen niet "kruisen".
Conditionele lussen
bewerkenWe kunnen ook blijven itereren tot aan een bepaalde voorwaarde is voldaan. Dit doen we met een do until bevel.
a=1 do until a>5 say a ; a=a+1 end
Wanneer a een waarde groter dan 5 behaalt verlaten we de lus. Deze voorwaarde wordt telkens aan het einde van de lus getest, en de lus wordt dus minstens éénmaal uitgevoerd.
Willen we echter de test uitvoeren aan het begin van de lus, dan gebruiken we het bevel do while.
do while a<6 say a ; a=a+1 end
Deze lus geeft hetzelfde resultaat als die met "do until".
De lusvariabele wordt telkens aan het einde van de lus (of bij een iterate bevel) verhoogd met de stap. Bij het terugkeren naar het do bevel is de waarde dus op een bepaald moment groter geworden dan de eindwaarde en stopt de iteratie. Willen we m.a.w. na de lus vertellen hoeveel maal de lus doorlopen werd, dan moeten we terug een stap aftrekken. |
IF
bewerkenDe IF constructie wordt gebruikt om voorwaardelijk te programmeren:
if test then do statements indien test waar end else do statements indien test onwaar end
Ditzelfde kunnen we op een meer compacte manier schrijven, waardoor het geheel van de constructie gemakkelijker te overzien is op eenzelfde schermhoogte, zeker als er veel statements in de blokken zitten:
if test then do statements indien test waar end else do statements indien test onwaar end
We hebben dit al een IF-blok genoemd, naar analogie met het DO-blok.
Het gedeelte else is niet verplicht als het niet nodig is.
Indien slechts één statement moet worden uitgevoerd is een DO-blok ook niet nodig:
if a//7=0 then say a 'is deelbaar door 7' else say a 'is niet deelbaar door 7'
Of, met compacte schrijfwijze
if a//7=0 then say a 'is deelbaar door 7' else say a 'is niet deelbaar door 7'
Condities kunnen complexe vormen aannemen door gebruik van & (en) en | (of). Dit is een voorbeeld:
vandaag=date('Weekday') if vandaag='Sunday' | vandaag='Saturday' then say 'Het is weekend !!!'
De voorwaarde die in het if bevel wordt getest moet resulteren in «1» als het resultaat waar is, en «0» als het onwaar is. Gelet daarop kunnen we ook iets schrijven in de aard van:
if 1 then say 'Het is waar!' ; else say 'Het is niet waar!'
Merk tenslotte op dat else een aparte instructie moet zijn, reden waarom hier het scheidingsteken ; is gebruikt.
Ook if-blokken kunnen genest zijn. Daarbij moet men opletten dat de logica gerespecteerd wordt:
if voorwaarde1 then if voorwaarde2 then statement else statement else statement
Stel dat voor het binnenste IF-blok geen else nodig is, dan moeten we er toch één schrijven omdat anders de andere else zou worden gebruikt. Als statement gebruikt men dan het bevel NOP wat een afkorting is voor No OPeration, "niets doen".
SELECT
bewerkenMet een IF kunnen we slechts twee gevallen (waar/onwaar) testen. Indien we meer dan 2 mogelijke uitkomsten kan verwachten gebruiken we liever een SELECT-blok dan een reeks geneste IF bevelen.
select when voorwaarde_1 then statement when voorwaarde_2 then do statements end otherwise /* voor alle andere gevallen */ statements of NOP end
Otherwise is niet strikt nodig, maar men moet natuurlijk opletten dat alle mogelijkheden behandeld worden. Otherwise vangt de onverwachte gevallen mooi op.
Indien een when meerdere instructies nodig heeft moet men natuurlijk een DO-blok gebruiken. Otherwise bevat een impliciet DO-blok, dus alle instructies tussen otherwise en end horen automatisch logisch samen.
PARSE
bewerkenHet PARSE bevel is uitermate krachtig. Het combineert een aantal nuttige bewerkingen om strings op te splitsen.
De algemene syntaxis is:
parse [upper] bron sjabloon
Hierbij wordt de bron opengebroken volgens wat is opgegeven in het sjabloon (template in het Engels). Het Engels werkwoord to parse betekent ontleden, analyseren. Het Van Dale woordenboek vermeldt zelfs parseren als een bestaand Nederlands woord. Maar het is misschien gemakkelijker te onthouden dat parse iets zal "opsplitsen in partjes".
De bron wordt opgegeven als een combinatie van een codewoord en parameters.
Het sjabloon is in zijn eenvoudigste vorm een reeks namen van variabelen.
In zijn eenvoudigste vorm behelst het opsplitsen dus een bewerking waarbij woorden uit de bron aan de respectievelijke variabelen van het sjabloon worden geässigneerd. Indien het optioneel codewoord upper is toegevoegd zal de bron tevens naar hoofdletters worden vertaald alvorens in de variabelen terecht te komen. Maar door meer complexe sjablonen te gebruiken kan men het opsplitsen intelligenter maken. We leren daar later meer over in het hoofdstuk Parse in Detail.
Laten we nu bekijken welke soorten bronnen mogelijk zijn.
Parse bronnen
bewerkenARG : argumenten ophalen
bewerkenparse arg sjabloon
De bron verwijst hier naar de parameters (ofte argumenten) die men bij het oproepen van het programma of subroutine heeft meegegeven. Stel dat we volgend programma oproepen met de argumenten Jan Klaas,
/* Programma behandelt parameters */ parse arg voornaam familienaam say "De voornaam van" familienaam "is" voornaam exit
dan mogen we terecht verwachten de zin «De voornaam van Klaas is Jan» op het scherm te zien verschijnen. Indien ook nog het codewoord upper zou zijn toegevoegd, dan bekwamen we «De voornaam van KLAAS is JAN».
LINEIN : lezen van toetsenbord
bewerkenMet linein wordt het toetsenbord de bron. Het programma zal daarbij wachten tot de gebruiker de enter-toets heeft gebruikt, al dan niet na intikken van data. De ingevoerde data worden dan door het sjabloon opgesplitst.
parse linein sjabloon
Bijvoorbeeld:
parse linein gegevens say 'Je hebt "'gegevens'" ingetikt.'
PULL : lezen van de programmabuffer
bewerkenIn de programmabuffer ofte stack kunnen tekstregels aanwezig zijn. Deze werden er bijvoorbeeld worden aan toegevoegd met de bevelen PUSH of QUEUE (meer details hierover in het hoofdstuk Werken met de programmabuffer).
Indien de stack leeg is gaat het programma wachten op gegevens ingebracht op het klavier (en is het dus equivalent aan het gebruik van linein als bron).
parse pull input say 'De eerste lijn van de programmabuffer bevatte "'input'"'
VALUE : opsplitsen van een uitdrukking
bewerkenparse value een_uitdrukking with sjabloon
Een uitdrukking bestaan uit constanten, variabelen en/of functies. Daar de uitdrukking kan resulteren in een zin bestaande uit verschillende woorden, is in deze vorm het sleutelwoord with verplicht om de bron van het sjabloon te scheiden. Als we dit uitvoeren,
parse value "123-4567890-22" with bank '-' kaartnummer '-' controle say "De kaart met nummer" kaartnummer "werd uitgegeven bij bank" bank"."
dan krijgen we «De kaart met nummer 4567890 werd uitgegeven bij bank 123.». Hier hebben we de bron niet gesplitst waar er spaties voorkomen, maar wel waar er streepjes zijn.
parse value "Dit is een lange zin" with woord1 woord2 rest
Nu zal de variabele rest de woorden «een lange zin» bevatten, want er zijn niet genoeg variabelen in het sjabloon om alle woorden van de bron op te nemen. De laatste variabele krijgt dus hetgeen overblijft in de bron. Hoe we dat eventueel anders kunnen oplossen leren we straks bij de bespreking van de verschillende soorten sjablonen.
VAR : de waarde van een variabele opsplitsen
bewerkenparse var variabele sjabloon
Als bron geven we nu de naam van een variabele op.
a="Dit is een tekst." parse var a woord1 woord2 woord3 say 'Het tweede woord van "'a'" is "'woord2'"' say 'Het derde woord is echter "'woord3'"'
Dit zal het volgende op het scherm afdrukken
Het tweede woord van "Dit is een tekst." is "is" Het derde woord is echter "een tekst."
Nu nog een paar speciale bronnen voor het parse bevel:
SOURCE : programma karakteristieken ophalen
bewerkenDoor source als bron op te geven, vragen we meer informatie op over het programma zelf.
parse source sjabloon
Al naargelang het besturingssysteem kan men andere antwoorden krijgen. Doch normaal mogen we als eerste drie woorden de volgende verwachten:
- de naam van het besturingssysteem;
- een aanduiding van hoe ons programma werd opgeroepen (COMMAND, SUBROUTINE of FUNCTION, later meer hierover);
- de naam en het volledige pad van ons programma.
parse source systeem oproeping programma say 'Het programma in uitvoering is:' programma say 'Het werd opgeroepen als:' oproeping say 'Het draait onder controle van:' systeem
Op een Windows systeem zou dit het volgende kunnen geven:
Het programma in uitvoering is: C:\Program Files\mijnrexxprogramma.rex Het werd opgeroepen als: COMMAND Het draait onder controle van: WindowsNT
VERSION : versienummer van REXX
bewerkenparse version versie
assigneert de versie van REXX (bv. «REXX-ooRexx_3.1(MT) 6.01 20 Apr 2007») aan de variabele versie. Nuttig om bijvoorbeeld na te gaan of een bepaalde functie in deze versie van REXX kan gebruikt worden.
Sjabloonvormen
bewerkenTot nu toe hebben we meestal aangenomen dat het sjabloon uit een rij tokens (variabelen) bestaat. De woorden uit de bron worden daarbij één voor één aan de variabelen toegekend. Hierbij speelt het aantal spaties tussen de woorden van de bron geen belang.
Een sjabloon kan echter complexere vormen aannemen en begrenzers bevatten. Deze begrenzers kunnen constanten, kolomnummers of indirecte kolomnummers zijn.
Hier een voorbeeld met een constante begrenzer:
myVar = "Smith, John" parse var MyVar LastName "," FirstName say "First name is:" firstName say "Last name is:" lastName
Hierbij duidt de constante "," aan waar er moet worden gesplitst. De begrenzer zelf verdwijnt daarbij. Dit levert dus:
First name is: John Last name is: Smith
Merk op dat de spatie die in de bron net voor "John" staat ook blijft bestaan in de variabele firstname ! Als we constante begrenzers gebruiken worden extra spaties niet langer gereduceerd tot één enkele ! We kunnen zeggen dat we zijn overgestapt van "zoeken naar één of meer spaties" op "zoeken naar de expliciet opgegeven constante".
In het laatste statement hebben we een dubbele spatie vóór de variabele lastname geplaatst, omdat het mooi aligneert met het statement erboven. Doch hier blijft uiteindelijk slechts één spatie over, want het aantal spaties tussen de woorden van een statement heeft geen belang, dat leerden we vroeger al.
En nu een eenvoudig voorbeeld waarin we kolombegrenzers gebruiken:
myVar = "WSBN nl-3-34-343-00010" parse var MyVar 6 Taal 8 9 HoofdRubriek 10 18 VolgNummer say "De taal van het Wikibook is:" Taal say "De hoofdrubriek is :" HoofdRubriek say "en het volgnummer is :" VolgNummer
met als resultaat:
De taal van het Wikibook is: nl De hoofdrubriek is : 3 en het volgnummer is : 00010
Het parse bevel zal eerst de karakters vanaf positie 6 tot en met 7 in variabele Taal plaatsen. Het karakter op positie 8 wordt verwaarloosd. Positie 9 gaat naar HoofdRubriek, posities 10 t.e.m. 17 vallen weg en tenslotte gaat alles vanaf positie 18 naar variabele VolgNumber.
Naamgeving van variabelen Iedereen begrijpt wel dat cryptische namen voor variabelen (xyz17) de leesbaarheid niet bevorderen. Beter is het een naam te kiezen die de verwachte inhoud ervan beschrijft, zoals ons VolgNummer. Voor REXX worden alle karakters hoofdletters, maar wij hebben de mogelijkheid onze namen meer leesbaarheid te geven door met combinaties van hoofd- en kleine letters te werken. Een alternatieve schrijfwijze die men ook dikwijls gebruikt is volg_nummer. |
In het hoofdstuk Parse in meer detail zullen we dus nog uitgebreid terugkomen op dit belangrijk en krachtig bevel.