Startseite
Downloads
Lexikon
Sonstiges
Links
Kontakt
Gästebuch
Statistics
Disclaimer

Meine REXX-Programmierungregeln

Ich mit vier

Einführung

Auf dieser Seite stelle ich Ihnen Regeln vor, die ich beim Schreiben von REXX-Routinen benutze. Alle Regeln, die ich mir beim Schreiben von REXX-Quelltexten angewöhnt habe, basieren auf diesen, die man in C- und Java-Programmen eingesetzt und seit langem benutzt hat.

Die Idee für dieses Dokument ist entstanden, als ich bei einer Projektarbeit bemerkt habe, wie unterschiedlich alle Beteiligten Ihre Quelltexte formatieren. Nach dem Austauschen der Projektmitarbeiter und/oder nach dem Wechsel der Aufgabenbereiche, mussten die Mitarbeiter viel Zeit verlieren, um die von Kollegen programmierten Routinen zu verstehen und an eigene Formatierungregeln umzuschreiben.

Die Einführung und Anhaltung dieser gemeinsamen Regeln hat nach kurzer Einarbeitung enorme Zeitersparnisse gebracht, auch nach der Praxisübergabe für die Wartungsprogrammierer.

In diesem Dokument werde ich folgende meine Regeln detailiert erklären.

Schreibregeln für Schlüsselwörter, Variablen- und Funktionsnamen

Für REXX-Schlüsselwörter, Variablen- und Funktionsnamen verzichte ich (außer in speziellen Fällen) auf das Anwenden von Sonderzeichen.

  1. REXX-Schlüsselwörter - immer den ersten Buchstaben groß und den Rest klein (nach der amerikanischen Regel: Was wichtig ist, muß großgeschrieben werden). Übrigens, nach Einführung diese Regeln habe ich entdeckt, dass die gleiche Schreibweise im Edit-Makro "Model", das als Entwicklungshilfe von REXX-Programmen im TSO zur Verfügung steht, von IBM angewendet wurde. Zum Beispiel:
    If name <> '' Then Say name
    Select
      When wert > 0 Then ergebnis = ergebnis + value
      When wert = 0 Then Nop
      Otherwise Say "Wert muß größer oder gleich Null sein:" wert
    End
  2. Variablennamen - immer den ersten Buchstaben klein. Wenn der Name aus mehreren Worten besteht, dann alle Worte zusammengeschrieben und ab dem zweiten Wort, die erste Buchstabe jedes Wortes immer großgeschrieben:
    recipeOfTheDay
    artikelNummer
    tagesLohn
  3. Funktionsnamen - den ersten Buchstaben immer großgeschrieben, weiter wie bei Variablennamen:
    LoadUtil
    WriteFromStem
    ReadToQueue

Einsatz von Hochkommata und Anführungszeichen

Ich versuche folgende Regeln beim Verwenden von Hochkommata und Anführungszeichen einzuhalten:

Es gibt bei REXX keinen Unterschied zwischen der Benutzung von Hochkommata oder Anführungszeichen: im Gegenteil zu, zum Beispiel, der Programmiersprache C, die Hochkommata für den Datentyp Character und Anführungszeichen für den Datentyp string verwendet, REXX kennt nur einen Datentyp - String. Es ist aber besser für die Lesbarkeit des Programms, wenn man bewußt mit dieser REXX-Eigenschaft umgeht. Meine Empfehlung also (so wie in C) Hochkommata für einzelne Zeichen, hexadezimale Zahlen, REXX-Konstante und Null-String anwenden, Anführungszeichen für Zeichenketten. Zum Beispiel:
leer = ''
crlf = '0A0D'x
heute = Date( 'L' )
If Datatype( value ) = 'NUM' ...
vorName = Word( "Karolina J. Kowalczyk", 1 )
Allerdigs sind diese Regeln dieser anderen untergeordnet: Hochkommata und Anführungszeichen sind abwechselnd zu benutzen. Was bedeutet das? Wenn Sie in einem String auch Anführungszeichen als dessen Teil benutzen müssen, gibt es dafür zwei Möglichkeiten:
  1. Sie schreiben jedes Anführungszeichen, das ein Teil des Strings ist, doppelt.
  2. Sie benutzen zur Abgrenzung des Strings die Hochkommata, und die Anführungszeichen im String bleiben einzeln.
Zum Beispiel Sie wollen exakt folgenden Satz als String in einer REXX-Routine anwenden:
"Hallo World" waren die ersten Worte des ersten C-Programms.
Im ersten Fall müssten Sie diesen String in einer REXX-Routine so Schreiben:
"""Hallo World"" waren die ersten Worte des ersten C-Programms."
Verwenden Sie aber die zweite Möglichkeit, so wird der String einfach folgendermaßen aussehen:
'"Hallo World" waren die ersten Worte des ersten C-Programms.'
Ein weiteres (praktisches) Beispiel zeigt eine umgekehrte Situation - Sie verwenden (warum auch immer) die Hochkommata zum Begrenzen des Strings und Sie müssen im String selbst die Hochkommata benutzen. Angenommen Sie müssen im TSO eine Dateienliste anwenden:
'JANOSCH.PR.EXEC' 'SYS1.CMDPROC.EXEC' 'SYS1.CMDPROC.CLIST'
Sie können das in einer REXX-Prozedur so:
'''JANOSCH.PR.EXEC'' ''SYS1.CMDPROC.EXEC'' ''SYS1.CMDPROC.CLIST'''
oder so:
"'JANOSCH.PR.EXEC' 'SYS1.CMDPROC.EXEC' 'SYS1.CMDPROC.CLIST'"

schreiben. Entscheiden Sie selbst was informatiever und lesbarer ist.

Einrückung

Der Einrückungsgedanke wurde durch die Darstellungsmöglichkeiten der Bildschirme ausgelöst. Im Textmodus sind es meistens 80 Zeichen x 24 Zeilen. Die möglichen 132 Zeichen im TSO sind für die Programmierung (vor allem für die Programmierer mit Augenproblemen - also für fast alle) meistens unzumutbar und mehr als 43 Zeilen passen auch nicht auf den Bildschirm. In grafischen Editoren auf PC's ist man zwar schon etwas flexibler, aber trotzdem paßt eine REXX-Routine fast nie vollständig ins Fenster. Man ist also zum Scrollen verdonnert! Was allerdings den Überblick über das Programm sehr verschlechtert. Wie kann man sich dagegen wehren? Einfach durch platzsparendes Editieren:

  1. Einrücken von Programmblöcken auf 2 Leerzeichen begrenzen.
  2. Die "Do - End"-Blöcke auf den Auslöser anhängen.
Zum Beispiel:
----+----1----+----2----+----3----+----4----+----5----
If value <= preis Then Do
  Say "Dein Kontostand rückt in die Gefahrenzone."
  Say "Warte lieber bis zum nächsten Zahltag."
End
Else Do
  Say "Wieviel Euro möchtest Du ausgeben?"
  Pull ausgabe
  If Datatype( ausgabe ) = 'NUM' Then
    value = value - ausgabe
  Else Do
    Say "Programm beendet"
    Exit
  End
End

Sie werden staunen, wieviele von Ihrer REXX-Exec jetzt auf ein Bildschirm mit der Auflösung von 80 Zeichen x 43 Zeilen paßt, ohne dass Sie hier und da scrollen müssen.

Programmstruktur

Alle meine REXX-Programe haben immer den gleichen modularen Aufbau. Das macht das Lesen des Programms, die Änderungen und die Fehlersuche viel einfacher:

  1. Hauptprogramm, das in folgende Blöcke unterteilt ist:
    • Kommentarblock,
    • Umgebung- und Parameter-Block,
    • Fehlerbehandlung
    • Registrierung von zusätzlichen (dynamischen) Function-Packages - falls notwendig,
    • Programmkern, der meistens aus Aufrufen von eigenen REXX-Prozeduren besteht,
    • Programm-Abschluß,
    • Fehlerbehandlungsprozedur(en).
  2. Interne REXX-Prozeduren - die ich persönlich, aus Performance-Gründen, gegenüber den externen Prozeduren bevorzuge.

Hier ein Beispiel (für Windows und OS/2), wie der Rahmen von so einem REXX-Programm aussehen könnte:

/* REXX Kommentarblock ****************************************/
/*                                                            */
/* Program name:                                              */
/*                                                            */
/* Function....:                                              */
/* Syntax......:                                              */
/* Author......:                                              */
/* Create date.:                                              */
/* Version.....: 0.1                                          */
/*                                                            */
/* Changes.....: No                                           */
/*                                                            */
/* © Name des Autors, 2008, 2010. All rights reserved.        */
/**************************************************************/
Trace o
/*==========( Umgebung- und Parameter-Block )===========*/
Parse Source environment calledAs procName .
If calledAs = "COMMAND" Then
  Parse Arg arg1 arg2
Else
  Parse Arg arg1, arg2

/*================( Fehlerbehandlung )==================*/
Signal On Failure Name CLEANUP
Signal On Halt Name CLEANUP
Signal On Syntax Name CLEANUP

/*=======( Registrierung von Function-Packages )========*/
Call LoadUtil                           /* Nur für OS/2 */

/*==================( Programmkern )====================*/

....... zum Beispiel Prozeduraufrufe .................

/*================( Programm-Abschluss )================*/
Call SysDropFuncs
If calledAs = "COMMAND" Then Exit rc

Return rc

/*============( Fehlerbehandlungsprozedur )=============*/
CLEANUP:
  Say Space( Errortext( rc ) "RC="rc )
  desc = Condition( "D" )
  If desc <> '' then desc = "(" || desc || ")"
  Say Condition( "C" ) Space( "condition" desc "running" procName )
  Say "Sourceline" sigl ">>>" SourceLine( sigl )
  Say "Restart please. If the error recurs, notify the author."
  Call SysDropFuncs
Exit

Exit rc
/*=============( INTERNE REXX-PROZEDUREN )==============*/

/*=====( RexxUtil-Support für OS/2 initializieren )=====*/
LoadUtil: Procedure

If RxFuncQuery('SysLoadFuncs') Then Do
  Call RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'
  Call SysLoadFuncs
End /* If RxFuncQuery... */

Return rc
/*** End LoadUtil ***/

....... Weitere REXX-Prozeduren ........................

Das ist allerdings ein sehr einfaches Beispiel. Etwas mehr umfangreiches können Sie von hier aus laden. Aber trotzdem soll, meines Erachtens nach, ein Programmgerüst so einfach sein, wie es nur geht. Beachten Sie bitte dafür die nächste Regel.

Tips & Tricks

Der große Erfolg von REXX entstand, meiner Meinung nach, dadurch, dass Mike F. Cowlishaw ihn so logisch und einfach gestaltet hat. Diese Sprache hat in der Syntax nichts Überflüssiges. Und obwohl sie so einfach ist, kann sie trotzdem alle Anforderungen erfüllen. Dabei ist das Programmieren mit REXX so einfach als würde man ein ganz normales Gespräch führen. Warum sollen wir das durch das komplizierte Programmieren ändern? Hier einige Beispiele von meisten Problemen und Hinweise, wie man sie vermeiden kann.

Variablennamen

Ich versuche für Funktionen oder Variablen keine unverständlichen und langen Namen zu vergeben, womöglich auch ohne Sonderzeichen. Vergleichen Sie bitte einfach diese Variablennamen:

__Recipe_of_the_day__
mit
recipeOfTheDay

oder

_day_#_
mit
dayNumber

Vorteile werden Sie sofort sehen, wenn Sie diese Namen in einer REXX-Exec öfter verwenden.

Sparsames Codieren

Zum zerlegen von Zeichenketten wird von den meisten (unerfahrenen) Programmierern die Built-in-Funktion SubsStr verwendet: diese Funktion ist auch aus anderen Programmiersprachen bereits bekannt, die Anwendung ist logisch und einfach. Leider aber auch (woran die meisten nicht denken) sehr unperformant. Hier ein Beispiel (aus echtem Leben!), wie man diese Funktion vergewaltigen kann:

/* Sehr umständlich und deswegen nicht zu empfehlen :( */
heute = Date( "S" )    /* Datum im Format: yyyymmdd */
jahr = SubStr( heute, 1, 4 )
mon  = SubStr( heute, 5, 2 )
tag  = SobStr( heute, 7, 2 )
Diese schreckliche Konstruktion kann man im REXX so elegant mit Hilfe der PARSE-Anweisung umsetzen:
Parse Value Date( "S" ) With jahr 5 mon 7 tag .

Dadurch wird nicht nur der Quellcode kürzer und einfacher zum Lesen und Verstehen aber auch das Programm schneller: drei Aufrufe der Funktion SubsStr dauern wesentlich länger als eine Verwendung der PARSE-Anweisung.

Modularisierung

Ein weiterer Vorteil besteht durch die Modularisierung des Programms. Es ist eigentlich keine REXX-Erfindung, aber meistens (gerade deshalb, weil das REXX-Programmieren so einfach ist) vergessen das die REXX-Anfänger. Sie programmieren "in einem Atemzug" von Anfang bis zum Ende in einer Prozedur. Da verliert man sich sehr schnell. Und die Pflege und Wartung solcher Monsterprogramme stellt stellt, aus Sicht eines Programmierers, eine Katastrophe dar.

Mein Vorschlag: alles in kleine Prozeduren unterteilen, und das Hauptprogram vorwiegend aus den Aufrufen der Prozeduren aufbauen. Mit möglichst wenig REXX-Codierung. Der zusätzliche Vorteil dieser Methode ist, dass sie viele von diesen Prozeduren wieder verwenden können und viel Zeit sparen.

Datenstrukturierung

REXX kennt nur einen Datentyp: Zeichenkette variabler Länge. Diese werden oft in ein weiterer zusammengesetzten Datentyp - Verbund (Satz) zusammen vereinigt. Im Prinzip auch nichts Neues. Es wird in allen höheren Programmiersprachen angewendet, wie z. B. Strukturen oder Unionen in C oder C++. Aber wie kann man das im REXX einstellen? REXX kennt doch keine solchen Datentypen.

Ich habe dafür eine Lösung auf Basis von internen REXX-Prozeduren erarbeitet. Hier beschreibe ich diese Lösung auf Beispiel einer Datenstruktur für einen Datensatz mit dem festen Aufbau - Felder mit den festen Längen und festen Positionen im Satz (der Satz selbst kann allerdings variable Länge besitzen). Diese Methode eignet sich auch für Datensätze mit variablem Aufbau. In diesem Fall entfallen dann die Angaben zur Feldposition und Feldlänge. Man muss allerdings in der Parse-Prozedur Schablonen zum Zerlegen der Sätze definieren.

Die Methode kann man in folgenden Punkten erläutern:
Beispiel:

Die folgende Beschreibung wird dieses Verfahren an einem Beispiel erläutern. Das Beispiel basiert auf dem Aufbau eines Satzes aus dem im RACF bekanntem Utility IRRDBU00. Der Utility entlädt die RACF-Datenbank in eine Flatfile (eine Datei mit dem festen Satzaufbau pro Satztyp). Wir verarbeiten hier beispielsweise den Satztyp 0260 (s. g. WRKATTR-Segment) Der Satz besteht aus Feldern, die in der folgenden Tabelle zusammengefasst sind.

Feldname Feldposition Feldlänge Feldbeschreibung
RECORD_TYPE 1 4 Record type of the User WORKATTR Data record (0260).
NAME 6 8 User ID as taken from the profile name.
AREA_NAME 15 60 Area for delivery.
BUILDING 76 60 Building for delivery.
DEPARTMENT 137 60 Department for delivery.
ROOM 198 60 Room for delivery.
ADDR_LINE_1 259 60 Address line 1.
ADDR_LINE_2 320 60 Address line 2.
ADDR_LINE_3 381 60 Address line 3.
ADDR_LINE_4 442 60 Address line 4.
ACCOUNT 503 255 Account number.

Basierend auf der oberen Tabelle wurden folgende Daten-Prozeduren definiert:

1. Prozedur zum Zerlegen des Satzes in einzelne Felder. Bitte beachten, dass die Trennzeichen (in dem Fall Leerzeichen) in ein "." (Punkt) - den Platzhalter der PARSE-Anweisung - eingelesen sind.

/*==================( Parse input record of USWRK )==================*/
UswrkParse: Procedure Expose uswrk.
Parse Arg 1 uswrk._recordType , /* Record type of the User WORKATTR  */
          5 .                 , /* Data record (0260).               */
          6 uswrk._name       , /* User ID as taken from the         */
         14 .                 , /* profile name.                     */
         15 uswrk._areaName   , /* Area for delivery.                */
         75 .                 ,
         76 uswrk._building   , /* Building for delivery.            */
        136 .                 ,
        137 uswrk._department , /* Department for delivery.          */
        197 .                 ,
        198 uswrk._room       , /* Room for delivery.                */
        258 .                 ,
        259 uswrk._addrLine1  , /* Address line 1.                   */
        319 .                 ,
        320 uswrk._addrLine2  , /* Address line 2.                   */
        380 .                 ,
        381 uswrk._addrLine3  , /* Address line 3.                   */
        441 .                 ,
        442 uswrk._addrLine4  , /* Address line 4.                   */
        502 .                 ,
        503 uswrk._account    , /* Account number.                   */
        758 .

Return uswrk._name
/*** End UswrkParse ***/

2. Prozedur zur Beschreibung des Satzes und Felder.

/*==================( Description values of USWRK )==================*/
UswrkDesc: Procedure Expose uswrk.

/*==========( Length block of UswrkDesc )===========*/
uswrk._len. = 0
uswrk._len._recordType = 4
uswrk._len._name       = 8
uswrk._len._areaName   = 60
uswrk._len._building   = 60
uswrk._len._department = 60
uswrk._len._room       = 60
uswrk._len._addrLine1  = 60
uswrk._len._addrLine2  = 60
uswrk._len._addrLine3  = 60
uswrk._len._addrLine4  = 60
uswrk._len._account    = 255

/*=========( Position block of UswrkDesc )==========*/
uswrk._pos. = 0
uswrk._pos._recordType = 1
uswrk._pos._name       = uswrk._pos._recordType + uswrk._len._recordTyp + 1
uswrk._pos._areaName   = uswrk._pos._name       + uswrk._len._name      + 1
uswrk._pos._building   = uswrk._pos._areaName   + uswrk._len._areaName  + 1
uswrk._pos._department = uswrk._pos._building   + uswrk._len._building  + 1
uswrk._pos._room       = uswrk._pos._department + uswrk._len._departmen + 1
uswrk._pos._addrLine1  = uswrk._pos._room       + uswrk._len._room      + 1
uswrk._pos._addrLine2  = uswrk._pos._addrLine1  + uswrk._len._addrLine1 + 1
uswrk._pos._addrLine3  = uswrk._pos._addrLine2  + uswrk._len._addrLine2 + 1
uswrk._pos._addrLine4  = uswrk._pos._addrLine3  + uswrk._len._addrLine3 + 1
uswrk._pos._account    = uswrk._pos._addrLine4  + uswrk._len._addrLine4 + 1

Return uswrk._pos._account + uswrk._len._account - 1
/*** End UswrkDesc ***/

3. Prozedur zum Erstellen eines Satzes in dem vorgegebenen Format.

/*======================( Create record USWRK )======================*/
UswrkCreate: Procedure Expose uswrk.
Parse Arg rec

rec = Overlay( uswrk._recordType, rec, uswrk._pos._recordType, uswrk._len._recordType )
rec = Overlay( uswrk._name, rec, uswrk._pos._name, uswrk._len._name )
rec = Overlay( uswrk._areaName, rec, uswrk._pos._areaName, uswrk._len._areaName )
rec = Overlay( uswrk._building, rec, uswrk._pos._building, uswrk._len._building )
rec = Overlay( uswrk._department, rec, uswrk._pos._department, uswrk._len._department )
rec = Overlay( uswrk._room, rec, uswrk._pos._room, uswrk._len._room )
rec = Overlay( uswrk._addrLine1, rec, uswrk._pos._addrLine1, uswrk._len._addrLine1 )
rec = Overlay( uswrk._addrLine2, rec, uswrk._pos._addrLine2, uswrk._len._addrLine2 )
rec = Overlay( uswrk._addrLine3, rec, uswrk._pos._addrLine3, uswrk._len._addrLine3 )
rec = Overlay( uswrk._addrLine4, rec, uswrk._pos._addrLine4, uswrk._len._addrLine4 )
rec = Overlay( uswrk._account, rec, uswrk._pos._account, uswrk._len._account )
Return rec
/*** End UswrkCreate ***/

Diese Prozeduren müssen in das Block INTERNE REXX-PROZEDUREN eines REXX-Programmes kopiert werden und danach können sie folgendermaßen verwendet werden (hier ein sehr vereinfachtes Beispiel, das nur diese Idee vermitteln und erläutern soll:

dsnINP = "'RACF.FLATFILE.USERS(USWRK)'"
ddnINP = "USWRKINP"

rc = AllocIt( dsnINP, ddnINP )
rc = Read( ddnINP )
...
Call UswrkDesc
...
Do i = 1 To stem.0
  userid = UswrkParse( stem.i )
  Say Left( userid, uswrk._len._name ) uswrk._room
  If userid = "USR0001" Then Do
    Say "Frau" usrwrk._name "hat geheiratet."
    uswrk._name = "MUSTERFRAU,MARIA"
    Say "Ihr neuer Name lautet:" usrwrk._name
    rec = UswrkCreate( rec )
    Queue rec
  End
  ...
End
...
Erklärungen:
  1. Das Programm dient offensichtlich zur Ausgabe aus RACF der Zimmernummern der Mitarbeiter, wenn sie im WORKATTR-Segment abgespeichert wurden.
  2. Die Sätze aus der Datei dsnINP werden in die Stem-Variable stem. eingelesen.
  3. Anzahl der Eingelesenen Sätze steht in der Variablen stem.0
  4. Die Beschreibung des Satzes wird durch den Prozeduraufruf Call UswrkDesc eingelesen und fürs Programm bekannt gemacht. Diese Routine muss nur ein mal vor der Verarbeitung aufgerufen werden.
  5. In der iterativen DO-Schleife werden die Benutzernummern (Userid) und Zimmernummern paarweise auf dem Bildschirm ausgegeben.
  6. Wenn eine vorgegebene Benutzernummer (hier USR0001) gefunden wird, dann wird der zugehöriger Name entsprechend geändert und im Stapelspeicher für eventuelle weitere Verarbeitung abgelegt.
Your privacy: This web site has no cookies, no advertising and does not provide access data to anyone for any reason.
Copyright © Janosch R. Kowalczyk 1999, 2010. All rights reserved.
Most recent revision on 25 Jan 2010 (25) - 16:48:31
Jakoxx Logo