Programmeren in REXX/Functies deel 2: verschil tussen versies
Verwijderde inhoud Toegevoegde inhoud
Kleine aanpassingen |
Aanvullingen en verbeteringen |
||
Regel 5:
Zoals steeds zullen resultaten tussen « en » worden getoond. Als er sprake is van een '''pad''' karakter, dan dient dit karakter om de (kortste) string op te vullen. Standaard wordt daar de spatie voor gebruikt.
=Beheer van datums en tijd=
Omdat datums en tijden geen gebruik maken van het tientallig stelsel is het werken ermee niet altijd eenvoudig en vraagt het grote aandacht. Fouten zijn snel gemaakt.
==TIME==
'''time'''([optie])
Met deze functie kan men de huidige tijd opvragen. Deze kan in verschillende formaten worden verkregen (enkel eerste karakter is daarbij nodig):
*Time('''N'''ormal) «15:57:48» (dit is de standaard-optie)
*Time('''C'''ivil) «3:57pm» (bij ons weinig gebruikt)
*Time('''L'''ong) «15:57:48.315000» (microseconden)
*Time('''H'''ours) «15» (uren sinds het begin v/d dag)
*Time('''M'''inutes) «957» (minuten sinds het begin v/d dag)
*Time('''S'''econds) «57468» (seconden sinds het begin v/d dag)
En nog twee speciale opties:
*Time('''E'''lapsed) «0.45455» (misschien)
*Time('''R'''eset) «10.03100» (misschien)
De eerste reeks behoeft weinig meer uitleg, behalve dat bij het '''L'''ong formaat, men op PC systemen geen grotere nauwkeurigheid dan milliseconden moet verwachten.
Als de functie '''time''' meermaals in hetzelfde statement wordt opgeroepen dan zal ze steeds dezelfde waarde teruggeven. Het is alsof de tijd dan even stilstaat. Bovenstaande resultaten zijn allemaal samen in één statement uitgevoerd, dus slaan de uitkomsten op dezelfde tijd.
De twee laatste opties vragen wel wat meer uitleg. Ze laten toe de tijd te meten die een taak erover heeft gedaan. Het is dus een soort chronometer.
<br>Bekijk deze volgorde:
call time 'R' /* zet de chronometer op 0, Reset dus */
/* ...een eerste reeks bewerkingen... */
say 'Reeks 1 nam' time('E') 'seconden in beslag.'
/* ...een tweede reeks bewerkingen... */
say 'Reeksen 1 en 2 namen samen' time('R') 'seconden in beslag.'
/* ...een derde reeks bewerkingen... */
say 'Reeks 3 duurde' time('E') 'seconden.'
In het eerste statement doen we een '''Reset''' van de chronometer. We zetten hem op nul. Vermits we niet geïnteresseerd zijn in de tijd die de chronometer op dat moment had gebruiken we een '''call''' bevel.
<br>Na de eerste reeks bewerkingen vragen we de '''Elapsed''' (verlopen) tijd op. De chronometer wordt daarbij niet terug op nul gezet, en zal dus blijven voortlopen.
<br>Na de tweede reeks bewerkingen vragen we weer de verlopen tijd op, maar tegelijkertijd zetten we de chronometer terug op nul.
<br>Uiteindelijk vragen we hoeveel tijd de derde reeks bewerkingen in beslag namen.
Noteer ook dat REXX slechts één chronometer bezit. Als hij wordt gestart in een subroutine, dan zal hij zijn waarde verliezen (en terug op nul vallen) bij het verlaten van de subroutine.
Hier volgen een aantal voorbeelden van tijdsrekening:
parse value time() time('S') with TijdNu DagSeconden
parse var TijdNu uu ':' mm ':' ss /* splits tijd in uren, minuten en seconden */
AantalSeconden=uu*3600 + mm*60 + ss /* hoeveel is dat in seconden */
say AantalSeconden=DagSeconden /* «1» want anders maakten we een fout */
Plus4min= AantalSeconden + 4*60 /* doe er 4 minuten bij */
/* Nu terug omzetten naar uu:mm:ss formaat... */
uu = Plus4min % 3600 /* Gehele deling */
if uu>23 then uu=uu-24 /* Misschien middernacht overschreden bij optelling */
rest = Plus4min // 3600 /* Rest na deling door 3600 (= aantal sec in 1 uur */
mm = rest % 60 /* Omzetten naar minuten via gehele deling */
ss = Plus4min // 60 /* Seconden zijn rest na deling door 60 */
Say 'Binnen 4 minuten zal het' right(uu,2,0)':'right(mm,2,0)':'right(ss,2,0) 'zijn.'
Het laatste deel is zeker een functie waard als men deze omzetting vele malen moet uitvoeren. Ze kan er dan in een zeer compacte vorm als volgt uitzien:
S2T: Procedure /* Zet een aantal Seconden om in normale Tijdformaat */
parse arg secs .
if secs > 86400 then secs=secs-86400 /* Meer dan 24 uur ? */
return right(secs % 3600,2,0) || ':' ||,
right(secs // 3600 % 60,2,0) || ':' ||,
right(secs // 60,2,0)
Bestudeer deze functie nog eens aandachtig zodat alle aspecten ervan duidelijk worden. Ze lijkt misschien nogal cryptisch, maar ze is alvast performant omdat er geen werkvariabelen worden aangemaakt.
==DATE==
'''date'''([optie])
Het oorspronkelijk formaat, met slechts één mogelijke parameter ''optie'', is nogal rudimentair. Men kan ermee de huidige datum in verschillende formaten opvragen. De ''optie'' (eerste letter is daarbij voldoende) bepaalt het formaat. We vernoemen enkel de voornaamste die men in alle REXX implementaties zal terugvinden:
*'''N'''ormal - dit formaat is standaard als geen optie wordt meegegeven. We krijgen de datum in de vorm '''dd mnd jjjj''', bv. 16 mar 2011 (maand in 3 karakters, Engelse namen);
*'''B'''asedate - het aantal dagen sinds 1 januari van het jaar 1. Er wordt hier geen rekening gehouden met de aanpassingen aan de kalender zoals o.a. Paus Gregorius ze doorvoerde in 1582. Voor meer informatie over
tijdrekening, zie [[w:Jaartelling|dit artikel]];
*'''D'''ays - het aantal dagen sinds het begin van dit jaar;
*'''E'''uropean - de datum in verkort Europees formaat (dd/mm/jj);
*'''M'''onth - de maand in dit jaar, niet afgekorte Engelse naam, bv. "May";
*'''O'''rdered - de datum in formaat jj/mm/dd. Kan dienen om te sorteren, maar het jaartal heeft slechts 2 posities;
*'''S'''orted - vandaar dat dit formaat beter aangepast is om te sorteren: jjjjmmdd;
*'''U'''sa - in Amerikaans formaat: dd/mm/jj;
*'''W'''eekday - de dag van de week in niet afgekorte Engelse tekst, bv. "Saturday".
OORexx biedt ook nog het '''L'''anguage formaat aan, waarbij de datum in lokaal formaat wordt gegeven, bv. 17 januari 2011. Het lokaal formaat wordt in de systeeminstellingen bepaald. Een REXX programma mag geen veronderstellingen maken omtrent het resultaat omdat het programma op een ander systeem in de prak kan lopen als er een andere land-instellingen is gemaakt.
Kortom, de meest zinvolle formaten zijn N, B, S, D, M en W.
Bij de eeuwwisseling, en om de jaar-2000 problematiek te kunnen aanpakken werd deze functie drastisch uitgebreid. De datum kan nu variabel zijn (dus niet enkel die van vandaag). Meer nog, de datum kan via de functie worden omgezet van één formaat in een ander waarbij scheidingstekens kunnen gekozen worden. Dit is een voorbeeld:
Gisteren = date('Normal',date('B')-1,'B')
''Gisteren'' zal de waarde «18 Nov 2011» hebben als het vandaag 19 november 2011 zou zijn.
Laten we hierbij een beetje meer uitleg geven:
*de eerste parameter bepaalt nog steeds het formaat waarin we de datum willen terugkrijgen;
*de tweede parameter is de datum die moet worden omgevormd. In dit geval gebruiken we '''date('B')''' om de basisdagen te krijgen en trekken we er één van af om naar gisteren over te gaan;
*de derde parameter maakt aan de functie duidelijk in welk formaat de tweede parameter is opgegeven.
Bovenstaand voorbeeld is het enige waarmee we met datums kunnen rekenen. Daarom is de '''basedate''' zo belangrijk. Dagen bijtellen of aftrekken van andere formaten heeft weinig zin of is zelfs onmogelijk. '''date('N')-1''' zal bijvoorbeeld een ''bad arithmetic operation'' geven, want date('N') is geen getal.
Dus, als we een datum ter beschikking hebben in een variabele, dan kunnen we die omvormen. Laten we nog een voorbeeld bekijken:
sdate=19530422 /* Dit is een Sorted datum */
say 'Kris is geboren op' date('W',sdate,'S')',' date(,sdate,'S')
Het antwoord dat we hier mogen verwachten is «Kris is geboren op Wednesday, 22 Apr 1953». Mooi is dat nog niet. Willen we het volledig in het Nederlands vertalen, dan moeten we wat meer moeite doen, bijvoorbeeld zo:
sdate=19530422 /* Dit is een Sorted datum */
dagen='maandag dinsdag woensdag donderdag vrijdag zaterdag zondag'
maanden='januari februari maart april mei juni',
'juli augustus september oktober november december'
basisdatum=date('B',sdate,'S') /* omzetten van sdate naar basisdatum */
/* Restdeling van basisdatum door 7 geeft een cijfer van 0 tot 6 */
/* waarbij 0 staat voor maandag en 6 voor een zondag */
dagnr=(basisdatum // 7)
dag = word(dagen,dagnr + 1) /* index mag niet nul zijn */
parse var sdate jjjj 5 mm 7 dd /* opsplitsen van sdate */
say 'Kris is geboren op' dag',' dd word(maanden,mm) jjjj
Nu wordt het resultaat «Kris is geboren op vrijdag, 22 april 1953». We kunnen dat wat inkorten:
dag=word(dagen,date('B',sdate,'S')//7+1)
say 'Kris is geboren op' dag',' substr(sdate,7) word(maanden,substr(sdate,5,2)) left(sdate,4)
Om te besluiten geven we nog een paar voorbeelden waarbij speciale scheidingstekens worden gebruikt. Die tekens kunnen we als 4de en 5de parameter geven bij de oproep van de '''date''' functie. De 4de parameter zegt welk teken in het resultaat moet verschijnen, en de 5de parameter zegt in de plaats van welk teken dit moet komen. Als het vandaag 19 november 2011 is, dan geven:
IsoDate = date('S',,,'-') «2011-11-19» (toevoegen scheidingsteken)
bdate = date('B',IsoDate,'S',,'-') «734459» (weglaten scheidingsteken)
mindate = date(,,,'-') «19-Nov-2011»(toevoegen scheidingsteken)
In het eerste geval voegen we mintekens toe aan een Sorted datum (jjjjmmdd). REXX weet waar het moet !
<br>In het tweede geval zetten we die IsoDate weer om naar een Basedate, waarbij we de functie meegeven dat er mintekens in de IsoDate zitten.
<br>In het derde geval vragen we mintekens toe te voegen in de Normale datum, en weer weet REXX hoe het moet.
=NUMERIC - nauwkeurigheid van bewerkingen bepalen=
Regel 27 ⟶ 136:
=Manipulatie van getallen=
De meeste functies en bewerkingen die we hier beschrijven zullen tot fouten leiden indien de tokens geen geldig getal voorstellen.
<!------------------------------------------------->
==DATATYPE - controle op type gegevens==
Regel 51 ⟶ 160:
say datatype('3','Logical') datatype(12,'O') «0 0»
say datatype('01000001'b,'B') «0»
In het laatste geval is de binaire string gelijk aan '41'x, of de letter «A» in het ASCII stelsel, en dat is
Met de gelijkwaardige types '''V'''ariable en '''S'''ymbol wordt getest of de string een syntactisch geldige naam
say datatype("AB%","Symbol") «0»
Regel 64 ⟶ 173:
*'''«VAR»''' als het een geldige naam is en ook is geïnitialiseerd.
Hier is het belangrijk te begrijpen dat we het te onderzoeken token als een constante moeten schrijven, anders onderzoeken we de '''inhoud''' van het token op geldigheid.
Voorbeelden:
a=10
say symbol("A") «VAR»
say symbol(a) «BAD»
say symbol(hello) «LIT»
say symbol("Hello") «LIT»
In het tweede geval maken we de fout de variabele niet als constante te schrijven, en dus testen we "10", en dit is geen geldig token. In het derde geval lukt het nog net omdat ''hello'' nog niet geïnitialiseerd is, en dus zichzelf als waarde heeft. Maar het laatste voorbeeld is dus veel veiliger.
<!------------------------------------>
==ABS - Absolute waarde van een getal==
'''abs'''(''getal'')
Deze functie geeft de absolute waarde (positieve waarde) van het getal terug.
say abs(24-12) «12»
say abs(12-24) «12»
Regel 79 ⟶ 195:
say max(a,b,24) min(4,-1*b,24) «24 -14»
<!------------------------------------>
==RANDOM - een willekeurig getal genereren==
'''random'''([''maximum''])
of
'''random'''([''minimum,maximum''['',zaadje''])
Deze functie geeft een willekeurig,
Indien men een herhaling van dezelfde willekeurige reeks getallen wil bekomen, dan kan men een ''zaadje'' (een getal) meegeven bij de eerste oproep.
<!------------------------------------>
==SIGN - wat is het teken van een getal ?==
'''sign'''(''getal'')
Het antwoord is «-1» voor een negatief ''getal'', «0» indien het ''getal'' nul is en «1» voor een positief getal.
say sign(-12 * -4) «1»
<!------------------------------------>
==TRUNC - decimalen
'''trunc'''(''getal''[,''decimalen''])
Kapt het ''getal'', '''zonder afronding''', af bij het aantal opgegeven ''decimalen''.
Wil men een afgerond getal bekomen, dan moet men gebruik maken van de nu volgende '''format''' functie
<!------------------------------------>
==FORMAT - een getal formatteren voor uitvoer==
Regel 111 ⟶ 227:
Merk het verschil tussen '''trunc''' en '''format''' wat de afronding betreft.
<!---------------------------------------------------------------------------->
==Omzetten van formaten==
Een reeks functies laat toe
<!------------------------------------>
===C2D - van karakter naar decimaal
'''c2d'''(string[,lengte])
Deze functie zal de binaire waarde van karakterreeks ''string'' omzetten naar een decimaal getal. Hiermee wordt bedoeld dat de ''string'' als een opeenvolging van bits wordt gezien, en dat die reeks dan als decimaal getal wordt geïnterpreteerd.
Een binaire waarde kan geïnterpreteerd worden als een absolute waarde, of een getal met teken. In het laatste geval zal het getal negatief zijn als de eerste bit op 1 staat. Lees voor meer informatie over negatieve binaire getallen het artikel [[w:2-complement|Two's complement]].
Dit zijn voorbeelden op een PC, dus voor een ASCII karaktertabel:
say c2d('A') «65»
say c2d('a') «97»
say c2d(' '
say c2d('AB') «16706»
say c2d('FF'x) «255»
say c2d('FF'x,1) «-1»
say c2d('FF'x,2) «255»
In het laatste 2 gevallen hebben we een lengte meegegeven. Met een lengte 1 hebben we binair "11111111", en dus
(
<!------------------------------------->
===C2X -
'''c2x'''(string)
Onze string wordt nu omgezet in zijn hexadecimale notatie.
Regel 136 ⟶ 253:
say c2x(' A') «2041»
say c2x('11111111'b) «FF»
===D2C - van decimaal naar karakter
'''d2c'''(getal)
Het getal wordt omgezet naar een karakterreeks.
say d2c(12) «♀» (dit is een controlekarakter !)
say d2c(65) «A»
say d2c(6645351) «efg»
<!------------------------------------->
===D2X - van decimaal naar hexadecimaal
'''d2x'''(getal)
Het getal wordt omgezet
say d2x(10) «A» (dit is '0A'x, een controlekarakter)
say d2x(c2d('K')) «4B»
<!------------------------------------->
===X2C - van hexadecimaal naar karakter
'''x2c'''(hexadecimale-string)
De hexadecimale string wordt
say x2c('4B') «K»
say x2c('7E7E') «~~»
say x2c('4B'x) Fout: Only 0-9, a-f, A-F, and blank are valid; found "K"
Het laatste voorbeeld is een fout die regelmatig gemaakt wordt. De parameter moet niet in hexadecimale REXX notatie geschreven worden, enkel de hexadecimale karakters zijn nodig.
<!------------------------------------->
===X2D - van hexadecimaal naar decimaal
'''x2d'''(hexadecimale-string)
Ditmaal wordt de hexadecimale string omgezet naar de decimale waarde ervan.
Regel 157 ⟶ 280:
say x2d('4B') «75»
<!------------------------------------->
===B2X - van binair naar hexadecimaal
'''b2x'''(binaire-string)
De bits worden omgezet in een meer leesbare hexadecimale vorm.
Regel 163 ⟶ 286:
say b2x(11110000) «F0»
<!------------------------------------->
===X2B - van hexadecimaal naar binair
'''x2b'''(hexadecimale-string)
Het omgekeerde van voorgaande functie.
say x2b(4B) «01001011»
say x2b(b2x(11110000)) «11110000»
Regel 171 ⟶ 294:
<!----------------------------------------------------------------------->
==Binair rekenen==
Met onderstaande functies kan binair worden gerekend.
{|border="1" style="border-collapse:collapse;border:1px solid silver;"
|-
| 1 || AND || 1 ||=|| 1
|-
| 1 || AND || 0 ||=|| 0
|-
| 1 || OR || 1 ||=|| 1
|-
| 1 || OR || 0 ||=|| 1
|-
| 1 || XOR || 1 ||=|| 0
|-
| 1 || XOR || 0 ||=|| 1
|}
<!------------------------------------->
===BITAND - bit per bit AND-en===
'''bitand'''(string1[,string2][,pad])
say c2x('A') «41»
say x2b(41) «01000001»
say bitand('11111111'b,'A') «A»
say bitand('00001111'b,'A') «@»
<!------------------------------------->
===BITOR - bit per bit OR-en===
'''bitor'''(string1[,string2][,pad])
say c2x(bitor('11111111'b,'A')) «FF»
say bitor('00001111'b,'A') «O» ('4F'x)
<!------------------------------------->
===BITXOR - bit per bit XOR-en===
'''bitxor'''(string1[,string2][,pad])
say c2x(bitxor('11111111'b,'A')) «BE» ('10111110'b)
{{75%}}
{{sub}}
|