Programmeren in REXX/Functies deel 2
In dit hoofdstuk behandelen we een tweede reeks functies die standaard met REXX wordt geleverd.
Het gaat om functies die toelaten getallen te formatteren. Ook functies die getallen of strings omzetten van één formaat in een ander. We overlopen ook nog even hoe REXX met getallen werkt en hoe we de nauwkeurigheid van de uitkomsten kunnen bepalen.
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.
Hoe rekent REXX ?
[bewerken]Het resultaat van een numerieke bewerking is een karakterreeks. De regels die daarbij spelen zijn:
- Het resultaat is berekend tot een maximaal aantal significante cijfers. Standaard is de nauwkeurigheid beperkt tot 9 cijfers, maar dit aantal kan aangepast worden. Indien het resultaat meer cijfers nodig heeft wordt het afgerond, dus 2/3=0.666666667;
- Behalve bij deling en machtsverheffing worden de nullen aan eind van de decimalen behouden, dus 2.40+2=4.40;
- Als het resultaat nul is dan wordt het steeds als één enkel cijfer 0 voorgesteld;
- Exponentiële notatie wordt pas gebruikt als het aantal significante cijfers vóór het decimaal punt groter wordt dan de nauwkeurigheid (standaardwaarde=9). Ook indien het aantal decimalen meer dan 2 maal de precisie overschrijdt, schakelt REXX over op exponentiële notatie, bijvoorbeeld:
1e6 * 1e6 «1E+12» en niet «1000000000000» 1 / 3E10 «3.33333333E-11» en niet «0.0000000000333333333»
- Alvorens een berekening aan te vatten zal REXX de spaties wegnemen. Het getal wordt dan beperkt tot het aantal significante cijfers plus één. De bewerking zelf gebeurt met het dubbele van de opgegeven nauwkeurigheid (standaard dus 2*9 cijfers);
- Noteer dat ".92" omgezet wordt tot "0.92".
- Machtsverheffing kan enkel met een exponent die een geheel positief of negatief getal is.
NUMERIC - nauwkeurigheid van bewerkingen bepalen
[bewerken]Alvorens dieper in te gaan op de numerieke functies moeten we het bevel numeric nog bespreken.
numeric digits [uitdrukking] of numeric fuzz [uitdrukking] of numeric form [formaat]
De eerste vorm bepaalt de nauwkeurigheid waarmee numerieke bewerkingen moeten worden uitgevoerd. Standaard worden daarvoor 9 cijfers gebruikt.
Noteer dat met de functie digits() de actuele cijfergrootte kan worden opgevraagd.
say 1*999999999 «999999999» say 1*9999999999 «1.00000000E+10»
Dus, eens men de nauwkeurigheid overschrijdt wordt het getal afgerond.
Met numeric fuzz kunnen we bepalen hoeveel cijfers bij vergelijkingen moeten worden verwaarloosd. Dit aantal moet kleiner zijn dan digits().
De actuele nauwkeurigheid kan worden opgevraagd met de functie fuzz. De "numeric fuzz" kan lokaal voor een subroutine worden veranderd.
en zal dus ook invloed hebben op eventuele afrondingen van getallen.
numeric fuzz 5 say 0.123456789=0.123409876 «0» numeric fuzz 6 say 0.123456789=0.123409876 «1»
Met numeric form tenslotte controleren we de vorm van de exponentiële notatie van getallen. We kunnen 2 formaten kiezen:
- scientific, waarbij slechts één significant cijfer voor de komma geplaatst wordt. Dit is de standaard;
- engeneering, waarbij in het exponentieel deel de macht steeds een veelvoud van 3 is.
De actuele exponent-vorm kan men opvragen met de functie form().
numeric form scientific say 1*9999999999 «1.00000000E+10» numeric form engineering say 1*9999999999 «10.0000000E+9»
Manipulatie van getallen
[bewerken]De meeste functies en bewerkingen die we hier beschrijven zullen tot fouten leiden indien de tokens geen geldig getal voorstellen. De nu volgende functie laat toe te anticiperen op dat soort fouten.
DATATYPE - controle op type gegevens
[bewerken]datatype(string[,type])
Als we geen type opgeven, dan antwoord deze functie «NUM» als de string een geldig numeriek getal is. In het ander geval is het antwoord «CHAR».
Maar we kunnen ook expliciet een type opgeven. Dan is het antwoord «1» (waar) als de string van dat type is, anders krijgen we «0» (onwaar). Het vetjes gedrukte karakter is voldoende.
- Alphanumeric (enkel A-Z, a-z en/of 0-9 karakters),
- Binary (enkel 1 en 0 karakters),
- Lowercase (enkel kleine karakters),
- Mixed (kleine en grote karakters gemengd),
- Number (numerisch),
- lOgical (exact een 1 of een 0 karakter, zonder spaties rond)
- Symbol of Variable (geldig symbool voor een variabele),
- Uppercase (enkel hoofdletters),
- Whole (geheel getal),
- heXadecimal (enkel 0-9 en/of A-F karakters)
Hier enkele voorbeelden:
say datatype("Abc") datatype("12") «CHAR NUM» say datatype("Abc","N") datatype("Abc","M") «0 1» say datatype('11101110','Binary') «1» 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 noch een karakter 1 ('31'x) noch een karakter 0 ('30'x), dus geen string die een binair getal voorstelt.
Met de gelijkwaardige types Variable en Symbol wordt getest of de string een syntactisch geldige naam heeft om als REXX variabele gebruikt te kunnen worden.
say datatype("AB%","Symbol") «0»
Er is nog een andere functie die ons soms kan helpen, namelijk symbol.
SYMBOL - geldigheid tokens
[bewerken]symbol("token")
Deze functie antwoord:
- «BAD» als het token geen geldige naam heeft om als variabele gebruikt te worden;
- «LIT« als het wel een geldige naam heeft, doch nog niet is geïnitialiseerd;
- «VAR» als het een geldige naam voor een variabele is, en ze reeds bestaat (een waarde kreeg).
Hier is het belangrijk te begrijpen dat we het te onderzoeken token doorgaans als een constante moeten schrijven, anders onderzoeken we de inhoud van het token op geldigheid, wat slechts sporadisch nodig is. Voorbeelden:
a=10 say symbol("A") «VAR» say symbol(a) «BAD» say symbol(hello) «LIT» say symbol("Hello") «LIT»
In het tweede geval schrijven we de variabele niet als constante string, dus testen we "10" wat geen geldig token is. In het derde geval lukt het nog net omdat hello nog niet geïnitialiseerd is, en dus zichzelf als waarde heeft. In het laatste voorbeeld zijn we beveiligd tegen het feit dat de variabele hello een waarde toegekend kreeg.
ABS - Absolute waarde van een getal
[bewerken]abs(getal)
Deze functie geeft de absolute waarde (positieve waarde) van het getal terug. De opgegeven parameter kan natuurlijk een uitdrukking zijn die na interpretatie in een getal moet resulteren. Is het geen getal, dan hebben we een uitvoeringsfout (93: Target must be a number).
say abs(24-12) «12» say abs(12-24) «12»
MAX en MIN - maximum en minimum zoeken
[bewerken]max(getal[,getal][,getal]...) min(getal[,getal][,getal]...)
Deze functies geven respectievelijk het grootste of het kleinste van een reeks getallen terug.
a=12 ; b=14 say max(a,b,24) min(4,-1*b,24) «24 -14»
RANDOM - een willekeurig getal genereren
[bewerken]random([maximum]) of random([minimum,maximum[,zaadje])
Deze functie geeft een willekeurig, positief geheel getal terug. Zonder parameters is het getal gelegen tussen 0 en 999. Maar er kan een minimum en/of een maximum worden meegegeven waartussen het getal moet liggen (het bereik is wel beperkt tot 100000).
Indien men een herhaling van dezelfde willekeurige reeks getallen wil bekomen, dan kan men een zaadje (een getal) meegeven bij de eerste oproep. Met hetzelfde zaadje zullen de getallen die dan gegenereerd worden steeds in dezelfde volgorde terugkomen. Handig als men een spelletje wil later herbeginnen met dezelfde kaarten of teerlingworpen.
SIGN - wat is het teken van een getal ?
[bewerken]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 weglaten
[bewerken]trunc(getal[,decimalen])
Kapt het getal, zonder afronding, af bij het aantal opgegeven decimalen. Bij ontstentenis van een aantal decimalen houdt men enkel het geheel getal over.
Wil men een afgerond getal bekomen, dan moet men gebruik maken van de nu volgende format functie.
FORMAT - een getal formatteren voor uitvoer
[bewerken]format(getal[,vóór][,na])
Het getal wordt eerst afgerond volgens de geldende afrondingsregels. Het resultaat is exact gelijk aan wat "getal+0" zou geven.
Men kan echter ook bepalen hoeveel cijfers er in het resultaat vóór en/of na de komma moeten komen. Geeft men een aantal vóór de komma dat te klein is om het getal te kunnen bevatten, dan treedt er een fout op. Hou daarbij ook rekening met een eventueel minteken. We kunnen ook meer posities voor de komma voorzien, in welk geval er spaties vooraan worden toegevoegd. Deze functie is, zoals haar naam impliceert, nuttig om het formaat van getallen onder controle te houden, bijvoorbeeld om de decimale punten onder elkaar te aligneren.
say trunc(10.676,2) «10.67» say format(10.676,,2) «10.68» numeric fuzz 4 say format(12.342233332,4,3) « 12.342» say format(12.343392211,4,3) « 12.343» say format(12.343792211,4,3) « 12.344»
Merk het verschil op tussen trunc en format wat de afronding betreft.
Omzetten van formaten
[bewerken]Een reeks functies laat toe data om te zetten van één formaat [karakter(C), decimaal(D), hexadecimaal(X) of binair(B)] naar een ander.
C2D - van karakter naar decimaal
[bewerken]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 een 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 Two's complement. De opgegeven lengte zal bepalen of de eerste bit op 1 staat en of we dus een negatief getal bekomen.
Dit zijn voorbeelden op een PC, dus voor een ASCII karaktertabel:
say c2d('A') «65» say c2d('a') «97» say c2d(' ') «32» 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 het negatief (2-complement) getal -1. Met de lengte 2 hebben we binair echter "0000000011111111", en dat resulteert dan weer in het positief getal 255.
C2X - van karakter naar hexadecimaal
[bewerken]c2x(string)
Onze string wordt nu omgezet naar zijn hexadecimale voorstelling. Het resultaat is een reeks karakters (0-9 en A-F).
say c2x("A") «41» say c2x(' A') «2041» say c2x('11111111'b) «FF»
D2C - van decimaal naar karakter
[bewerken]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
[bewerken]d2x(getal)
Het getal wordt omgezet naar hexadecimale voorstelling.
say d2x(10) «A» (dit is '0A'x, een controlekarakter) say d2x(c2d('K')) «4B»
X2C - van hexadecimaal naar karakter
[bewerken]x2c(hexadecimale-string)
De hexadecimale string wordt naar karakters omgezet. De karakters zijn dan niet noodzakelijk leesbaar en als ze resulteren in controlekarakters kunnen onverwachte dingen gebeuren.
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
[bewerken]x2d(hexadecimale-string)
Ditmaal wordt de hexadecimale string omgezet naar de decimale waarde ervan.
say x2d('7E7E') «32382» say x2d('4B') «75»
B2X - van binair naar hexadecimaal
[bewerken]b2x(binaire-string)
De bits worden omgezet in een meer leesbare hexadecimale vorm.
say b2x('11110000') «F0» say b2x(11110000) «F0»
X2B - van hexadecimaal naar binair
[bewerken]x2b(hexadecimale-string)
Het omgekeerde van voorgaande functie.
say x2b(4B) «01001011» say x2b(b2x(11110000)) «11110000» say x2b(4B0) «010010110000»
In ons tweede voorbeeld zullen we een programma beschrijven dat handig is om met deze functies vlot te kunnen werken.
Binair rekenen
[bewerken]Met onderstaande functies kan binair worden gerekend.
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
[bewerken]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
[bewerken]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
[bewerken]bitxor(string1[,string2][,pad]) say c2x(bitxor('11111111'b,'A')) «BE» ('10111110'b)