MainframeSupports
tip uge 37/2014:

Det er pludselig gået op for mig, at jeg aldrig har skrevet et tip om rekursion i COBOL. Du synes måske det lyder som en dårlig joke, men det er faktisk muligt at lave rekursive kald i COBOL, der virker. Du kan også komme frygteligt galt afsted, og begge dele vil jeg forsøge at illustrere i dette tip.

Følgende program tager PARM-feltet fra JCL og skriver indholdet ud i enkeltdele adskilt af kommaer. Hvis du for eksempel starter det med en TSO CALL MY.LOAD(PARSING) 'A,B B,CDE , FGH', så vil output på SYSOUT se således ud:

PARMNO=0001, LOCALNO=0001, VALUE=<A>
PARMNO=0002, LOCALNO=0001, VALUE=<B B>
PARMNO=0003, LOCALNO=0001, VALUE=<CDE >
PARMNO=0004, LOCALNO=0001, VALUE=< FGH>
PARMNO=0004, LOCALNO=0003 AFTER RECURSION
PARMNO=0004, LOCALNO=0002 AFTER RECURSION
PARMNO=0004, LOCALNO=0001 AFTER RECURSION

Selve programmet ser således ud:

IDENTIFICATION DIVISION.
PROGRAM-ID. PARSING RECURSIVE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  COMMAPOS PIC S9(9) BINARY.
01  PARMNO PIC S9(4) BINARY VALUE 0.
01  PGMPARM.
  02  PGMPARM-LENGTH PIC S9(4) BINARY.
  02  PGMPARM-CONTENS PIC X(100).
LOCAL-STORAGE SECTION.
01  LOCALNO PIC S9(4) BINARY VALUE 0.
LINKAGE SECTION.
01  JCLPARM.
  02  JCLPARM-LENGTH PIC S9(4) BINARY.
  02  JCLPARM-CONTENS PIC X(100).
PROCEDURE DIVISION USING JCLPARM.
    MOVE JCLPARM TO PGMPARM
    PERFORM PGMSTART
    GOBACK
    .
PGMSTART.
    MOVE 1 TO COMMAPOS
    ADD 1 TO PARMNO
    ADD 1 TO LOCALNO
    IF PGMPARM-LENGTH > 0
      INSPECT PGMPARM-CONTENS(1:PGMPARM-LENGTH)
        TALLYING COMMAPOS FOR CHARACTERS BEFORE ','
      DISPLAY 'PARMNO=' PARMNO
              ', LOCALNO=' LOCALNO ', VALUE=<'
              PGMPARM-CONTENS(1:COMMAPOS - 1) '>'
      IF COMMAPOS <= JCLPARM-LENGTH
        SUBTRACT COMMAPOS FROM PGMPARM-LENGTH
        MOVE PGMPARM-CONTENS(COMMAPOS + 1:)
          TO PGMPARM-CONTENS
        MOVE PARMNO TO LOCALNO
        CALL 'PARSING' USING PGMPARM
        DISPLAY 'PARMNO=' PARMNO
                ', LOCALNO=' LOCALNO ' AFTER RECURSION'
      END-IF
    END-IF
    .
END PROGRAM PARSING.

Første og vigtigste detalje, når du vil lave rekursion i COBOL er, at der efter program-navnet i PROGRAM-ID. skal stå RECURSIVE. Hvis der ikke står RECURSIVE får du en runtime fejl, når programmet kalder sig selv direkte eller indirekte via et andet program. Du kan ikke benytte RECURSIVE på et nested program hvilket betyder, at rekursion kun er muligt med selvstændige load-moduler. Anden detalje er, at alle variable i WORKING-STORAGE SECTION overlever fra kald til kald, hvilket der absolut ikke er noget nyt i. Hvis du har brug for at have nogle variable, der IKKE overlever fra kald til kald, så skal du erklære en såkaldt LOCAL-STORAGE SECTION som i mit eksempel.

Funktionen af WORKING-STORAGE i forhold til LOCAL-STORAGE kan du studere ved at sammenholde output eksemplet med programkoden. Her er det især vigtigt at bemærke, at variable, der skal indeholde samme værdi både før og efter det rekursive kald, skal erklæres i LOCAL-STORAGE.

Den trænede COBOL programmør vil hurtigt bemærke, at jeg ikke har udnyttet mine variable særligt optimalt. Det skyldes primært, at progammet også kan anvendes til at illustrere, hvor galt det kan gå, hvis du prøver at lave rekursion med PERFORM i stedet for med CALL. Hvis du ændrer CALL 'PARSING' USING PGMPARM til en PERFORM PGMSTART så burde du få nogenlunde samme funktionalitet bortset fra, at LOCAL-STORAGE og WORKING-STORAGE vil fungere ens.

Men, men, men, efter den første rekursive PERFORM vil programmet loope. Det skyldes, at COBOL kun gemmer en retur-adresse for hver paragraf/section. Resultat er, at når PGMSTART slutter, så returnerer COBOL kontrollen til statement'et lige efter PERFORM PGMSTART inde i PGMSTART. Det vil DISPLAY 'PARMNO=' PARMNO afsløre for dig, når det vælter ud med DISPLAY's på SYSOUT. Husk, at være klar til at skyde programmet ned, hvis du vil se, om det virkelig kan passe.

Forrige danske tip        Last tip in english        Tip oversigten