[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 579.   Istruzioni della divisione «PROCEDURE DIVISION»

Nelle sezioni di questo capitolo sono raccolti e descritti, in ordine alfabetico, i modelli sintattici delle istruzioni principali del linguaggio COBOL, da usare nella divisione PROCEDURE DIVISION.

579.1   Istruzione «ACCEPT»

L'istruzione ACCEPT permette di ricevere un'informazione da una fonte esterna al programma e di assegnarla a una variabile. Generalmente si usa questa istruzione per consentire all'utente l'inserimento di un valore attraverso il terminale che sta utilizzando, ma, a seconda delle possibilità offerte dal compilatore, può servire anche per l'acquisizione di altri dati.

                  .--      /  mnemonic-name     \  --.
                  |        |                    |    |
                  |        |  implementor-name  |    |
                  |        |                    |    |
ACCEPT identifier |  FROM  <  DATE              >    |
¯¯¯¯¯¯            |  ¯¯¯¯  |  ¯¯¯¯              |    |
                  |        |  DAY               |    |
                  |        |  ¯¯¯               |    |
                  `--      \  TIME              /  --'
                              ¯¯¯¯

La fonte di dati per l'istruzione ACCEPT può essere dichiarata attraverso un nome mnemonico, definito nel paragrafo SPECIAL-NAMES della sezione INPUT-OUTPUT SECTION (sezione 571.3), un nome particolare che dipende da funzionalità speciali del compilatore, oppure un nome che fa riferimento alla data o all'orario attuale (le parole chiave DATE, DAY, TIME).

Tabella 579.2. Parole chiave standard per definire l'accesso alle informazioni data-orario.

Parola chiave Descrizione
DATE
Fornisce la data attuale, formata da sei cifre numeriche, secondo la forma yymmdd (due cifre per l'anno, due per il mese e due per il giorno).
DAY
Fornisce il giorno attuale, formato da cinque cifre numeriche, secondo la forma yyddd (due cifre per l'anno e tre cifre per il giorno, espresso in quantità di giorni trascorsi dall'inizio dell'anno).
TIME
Fornisce l'orario attuale, formato da otto cifre numeriche, secondo la forma hhmmsscc (due cifre per l'ora, due per i minuti, due per i secondi e due per i centesimi di secondo).

L'esempio seguente dimostra il funzionamento e l'utilizzo di queste parole chiave standard:

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-ACCEPT-DATE-TIME.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 DATE-WRITTEN. 2005-02-27.
000500*
000600 ENVIRONMENT DIVISION.
000700*
000800 DATA DIVISION.
000900*
001000 WORKING-STORAGE SECTION.
001100 77  MY-DATE      PIC X(30).
001200 77  MY-DAY       PIC X(30).
001300 77  MY-TIME      PIC X(30).
001400*
001500 PROCEDURE DIVISION.
001600*
001700 MAIN.
001800     ACCEPT MY-DATE FROM DATE.
001900     ACCEPT MY-DAY  FROM DAY.
002000     ACCEPT MY-TIME FROM TIME.
002100     DISPLAY "DATE: ", MY-DATE.
002200     DISPLAY "DAY:  ", MY-DAY.
002300     DISPLAY "TIME: ", MY-TIME.
002400*
002500     STOP RUN.
002600*

Avviando questo programma il giorno 27 gennaio 2005, alle ore 13:30.45, si dovrebbe ottenere il risultato seguente:

DATE: 050227                        
DAY:  05058                         
TIME: 13304500                      

Tabella 579.5. Parole chiave non standard.

Parola chiave Descrizione
CONSOLE
SYSIN
Quando non si specifica la fonte dei dati per l'istruzione ACCEPT, si intende il terminale dal quale il programma è stato avviato; spesso, i compilatori considerano l'uso della parola chiave CONSOLE, o di SYSIN, come sinonimo di questo comportamento, anche se è quello predefinito.
COMMAND-LINE
I compilatori per i sistemi Unix consentono spesso di accedere al contenuto della riga di comando usata per avviare il programma, attraverso l'uso di questa parola chiave.

L'esempio successivo dimostra l'uso di un nome mnemonico per dichiarare l'origine dei dati. Sono evidenziate le righe più significative:

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-ACCEPT.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 DATE-WRITTEN. 2005-02-27.
000500*
000600 ENVIRONMENT DIVISION.
000700 CONFIGURATION SECTION.
000800 SOURCE-COMPUTER.
000900     OPENCOBOL.
001000 SPECIAL-NAMES.
001100     CONSOLE IS STANDARD-INPUT.
001200*
001300 DATA DIVISION.
001400*
001500 WORKING-STORAGE SECTION.
001600 77  MESSAGGIO            PIC X(30).
001700*
001800 PROCEDURE DIVISION.
001900*
002000 MAIN.
002100     DISPLAY "INSERISCI IL MESSAGGIO".
002200     ACCEPT MESSAGGIO FROM STANDARD-INPUT.
002300     DISPLAY "HAI INSERITO: ", MESSAGGIO.
002400*
002500     STOP RUN.
002600*

579.2   Istruzione «ADD»

L'istruzione ADD consente di eseguire delle somme. Sono previsti diversi formati per l'utilizzo di questa istruzione.

     /              \
     | identifier-1 |
ADD  <              >...  TO { identifier-n  [ROUNDED] }...
¯¯¯  | literal-1    |     ¯¯                  ¯¯¯¯¯¯¯
     \              /
    
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Nello schema sintattico appena mostrato, si vede che dopo la parola chiave ADD si elencano una serie di costanti o variabili con valore numerico, da sommare assieme, sommando poi quanto ottenuto al contenuto delle variabili specificate dopo la parola chiave TO. L'opzione ROUNDED richiede di eseguire un arrotondamento se la variabile ricevente non può rappresentare in modo esatto il valore; l'opzione SIZE ERROR serve a eseguire un'istruzione nel caso una delle variabili riceventi non possa accogliere la porzione più significativa del valore ottenuto dalla somma. Si osservi l'esempio seguente:

000000     ADD 1, 2, 3, TO A.

Supponendo che la variabile A, prima della somma contenga il valore 10, dopo la somma contiene il valore 16 (1+2+3+10).

     /              \  /              \
     | identifier-1 |  | identifier-2 |
ADD  <              >  <              >...
¯¯¯  | literal-1    |  | literal-2    |
     \              /  \              /

    GIVING { identifier-n  [ROUNDED] }...
    ¯¯¯¯¯¯                  ¯¯¯¯¯¯¯
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Quando al posto della parola chiave TO, si usa GIVING, la somma dei valori che precede tale parola chiave viene assegnata alle variabili indicate dopo, senza tenere in considerazione il loro valore iniziale nella somma. Valgono le stesse considerazioni già fatte a proposito delle opzioni ROUNDED e SIZE ERROR. Si osservi l'esempio seguente:

000000     ADD 1, 2, 3, GIVING A.

Qualunque sia il valore iniziale della variabile A, dopo la somma questa contiene il valore 6 (1+2+3).

     /               \
     | CORR          |
ADD  < ¯¯¯¯          >  identifier-1  TO  identifier-2  [ROUNDED]
¯¯¯  | CORRESPONDING |                ¯¯                 ¯¯¯¯¯¯¯
     \ ¯¯¯¯¯¯¯¯¯¯¯¯¯ /

    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

In questo ultimo caso, la somma fa riferimento a variabili strutturate, dove i campi della prima variabile devono essere sommati ai campi della seconda variabile che hanno lo stesso nome della prima. Valgono le stesse considerazioni già fatte a proposito delle opzioni ROUNDED e SIZE ERROR.

579.3   Istruzione «CLOSE»

Attraverso l'istruzione CLOSE si può chiudere un file aperto. Questa istruzione non riguarda i file definiti esplicitamente per le funzionalità di riordino e fusione del COBOL, perché questi non vengono aperti. La sintassi dell'istruzione può essere più o meno ricca, a seconda delle estensioni che offre il compilatore; tuttavia, lo schema seguente si adatta alla maggior parte delle situazioni:

       /               .--      /           \  --.  \
       |               |        | NO REWIND |    |  |
CLOSE  <  file-name-1  |  WITH  < ¯¯ ¯¯¯¯¯¯ >    |  > ...
¯¯¯¯¯  |               |        | LOCK      |    |  |
       \               `--      \ ¯¯¯¯      /  --'  /

Il file indicato viene chiuso, eventualmente con delle opzioni. Se si tratta di un file sequenziale a nastro, si può utilizzare l'opzione NO REWIND, con la quale si vuole evitare che il nastro venga riavvolto automaticamente dopo la chiusura, così da poter accedere eventualmente a un file successivo, già esistente o da creare sullo stesso nastro. L'opzione LOCK serve a impedire che il file possa essere riaperto nel corso del funzionamento del programma.

Nel caso si utilizzino dei nastri, quelli che il programma ha chiuso senza riavvolgere, vengono comunque riavvolti alla conclusione del programma stesso; inoltre, alla conclusione del programma vengono chiusi automaticamente i file che sono rimasti ancora aperti.

579.4   Istruzione «COMPUTE»

L'istruzione COMPUTE consente di calcolare un'espressione aritmetica, assegnando il risultato a una o più variabili:

COMPUTE  { identifier  [ROUNDED] }... = arithmetic-expression
¯¯¯¯¯¯¯                 ¯¯¯¯¯¯¯
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

La variabile che nello schema sintattico appare con il nome identifier deve essere scalare e di tipo numerico, anche se può contenere una maschera di modifica. Possono essere indicate più variabili a sinistra del segno = e ognuna riceve una copia del risultato dell'espressione alla destra.

L'opzione ROUNDED serve a richiedere un arrotondamento se la variabile ricevente non può rappresentare il risultato con la stessa precisione ottenuta dal calcolo dell'espressione; l'opzione SIZE ERROR consente di richiamare un'istruzione nel caso una delle variabili riceventi non fosse in grado di contenere la parte più significativa del valore ottenuto calcolando l'espressione.

000000     COMPUTE D = A * B + C.

L'esempio mostra che si vuole assegnare alla variabile D il risultato dell'espressione A * B + C (A moltiplicato B, sommato a C).

579.5   Istruzione «DELETE»

L'istruzione DELETE cancella un record logico da un file organizzato in modo relativo o a indice (sono esclusi i file organizzati in modo sequenziale).

DELETE  file-name  RECORD  [INVALID KEY imperative-statement]
¯¯¯¯¯¯                      ¯¯¯¯¯¯¯

Per poter cancellare un record è necessario che il file sia stato aperto in lettura e scrittura (I-O).

Se il file viene utilizzato con un accesso sequenziale, l'opzione INVALID KEY non è applicabile e non deve essere scritta nell'istruzione. Inoltre, utilizzando un accesso sequenziale, prima di eseguire un'istruzione DELETE è necessario che il puntatore del record sia stato posizionato attraverso un'istruzione READ. L'istruzione READ deve precedere immediatamente l'istruzione DELETE, che così può cancellare il record appena letto.

Quando il file viene utilizzato con un accesso diretto (RANDOM) o dinamico (DYNAMIC), l'opzione INVALID KEY è obbligatoria, a meno di avere dichiarato un'azione alternativa, in caso di errore, nella zona di istruzioni definite come DECLARATIVES, all'inizio della divisione PROCEDURE DIVISION. Per individuare il record da cancellare, si fa riferimento alla chiave, come specificato dalla dichiarazione RECORD KEY, associata al file in questione. Se si tenta di cancellare un record indicando una chiave che non esiste, si ottiene l'errore che fa scattare l'esecuzione dell'istruzione associata all'opzione INVALID KEY.

Dipende dal compilatore il modo in cui viene trattato effettivamente il record da cancellare: questo potrebbe essere sovrascritto con un valore prestabilito, oppure potrebbe essere semplicemente segnato per la cancellazione; in ogni caso, il record non viene cancellato fisicamente dal file.

Quando si accede al file attraverso un indice, bisogna considerare che la cancellazione può provocare la comparsa di record con chiavi doppie, se la cancellazione implica la sovrascrittura del record con un certo valore; inoltre, se il file contiene record con chiavi doppie, la cancellazione di un record specificando la sua chiave, può portare a cancellare quello sbagliato. Pertanto, in presenza di file a indice con chiavi doppie, conviene usare un accesso sequenziale per individuare in modo esatto il record da cancellare.

579.6   Istruzione «DISPLAY»

L'istruzione DISPLAY consente di emettere un messaggio attraverso un dispositivo che consenta di farlo. Generalmente, se usata senza opzioni, la visualizzazione avviene attraverso il terminale dal quale è stato avviato il programma.

         /            \     .--      /                  \  --.
         | literal    |     |        | implementor-name |    |
DISPLAY  <            >...  |  UPON  <                  >    |
¯¯¯¯¯¯¯  | identifier |     |  ¯¯¯¯  | mnemonic-name    |    |
         \            /     `--      \                  /  --'

Osservando lo schema sintattico si vede che dopo la parola chiave DISPLAY si possono mettere delle costanti letterali o dei nomi di variabile. Questi elementi possono rappresentare sia valori alfanumerici, sia numerici (tuttavia, il compilatore potrebbe rifiutarsi di accettare delle variabili di tipo INDEX): è il compilatore che provvede a eseguire le conversioni necessarie. L'elenco di costanti o di variabili viene concatenato prima della visualizzazione.

L'aggiunta dell'opzione UPON consente di specificare dove deve essere emesso il messaggio. Si può indicare una parola chiave definita dal compilatore, che identifica qualche tipo di dispositivo, oppure un nome mnemonico, da specificare nel paragrafo SPECIAL-NAMES della sezione INPUT-OUTPUT SECTION (sezione 571.3).

Tabella 579.17. Parole chiave non standard.

Parola chiave Descrizione
CONSOLE
SYSOUT
Quando non si specifica la fonte dei dati per l'istruzione ACCEPT, si intende il terminale dal quale il programma è stato avviato; spesso, i compilatori considerano l'uso della parola chiave CONSOLE, o di SYSOUT, come sinonimo di questo comportamento, anche se è quello predefinito.

L'esempio successivo mostra un uso abbastanza comune dell'istruzione DISPLAY:

000000     DISPLAY "ATTENZIONE: ", A, " + ", B, " = ", C.

L'esempio mostra in particolare il concatenamento che si vuole ottenere. Si ricorda che non è importante se le variabili utilizzate nell'istruzione sono alfanumeriche o numeriche, perché è il compilatore che provvede a convertire tutto nel modo più appropriato al tipo di dispositivo che deve emettere il messaggio.

579.7   Istruzione «DIVIDE»

L'istruzione DIVIDE consente di eseguire delle divisioni, fornendone il risultato ed eventualmente il resto. Sono previsti diversi formati per l'utilizzo di questa istruzione.

        /              \
        | identifier-1 |
DIVIDE  <              >  INTO  { identifier-2 [ROUNDED] }...
¯¯¯¯¯¯  | literal-1    |  ¯¯¯¯                  ¯¯¯¯¯¯¯
        \              / 

    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Nello schema sintattico appena mostrato, si vede che dopo la parola chiave DIVIDE viene indicato un valore, in forma costante o attraverso una variabile; questo valore viene diviso per la variabile indicata dopo la parola chiave INTO e il risultato viene assegnato alla stessa variabile che funge da divisore. Se appaiono più variabili dopo la parola INTO, la divisione viene ripetuta per ognuna di quelle, assegnando rispettivamente il risultato.

L'opzione ROUNDED richiede di eseguire un arrotondamento se la variabile ricevente non può rappresentare in modo esatto il valore; l'opzione SIZE ERROR serve a eseguire un'istruzione nel caso una delle variabili riceventi non possa accogliere la porzione più significativa del valore ottenuto dalla somma. Si osservi l'esempio seguente:

000000     DIVIDE 100 INTO A.

Supponendo che la variabile A, prima della divisione contenga il valore 5, dopo l'operazione contiene il valore 20 (100/5). Si potrebbe scrivere la stessa cosa utilizzando l'istruzione COMPUTE:

000000     COMPUTE A = 100 / A.

Lo schema sintattico successivo mostra l'utilizzo di DIVIDE in modo da non alterare i valori utilizzati come divisori:

        /              \  /      \  /              \
        | identifier-1 |  | INTO |  | identifier-2 |
DIVIDE  <              >  < ¯¯¯¯ >  <              >
¯¯¯¯¯¯  | literal-1    |  | BY   |  | literal-2    |
        \              /  \ ¯¯   /  \              /

    GIVING identifier-3  [ROUNDED]
    ¯¯¯¯¯¯                ¯¯¯¯¯¯¯
    [ REMAINDER  identifier-4  [ROUNDED] ]
      ¯¯¯¯¯¯¯¯¯
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Nella forma appena mostrata, dove le parole INTO e BY sono equivalenti, la divisione avviene immettendo il risultato dell'operazione nella variabile indicata dopo la parola GIVING. Valgono le stesse considerazioni già fatte a proposito delle opzioni ROUNDED e SIZE ERROR. Si osservi l'esempio seguente che ripete sostanzialmente l'esempio già mostrato in precedenza:

000000     DIVIDE 100 BY 5 GIVING A.

Utilizzando l'opzione REMAINDER, si fa in modo che il resto della divisione venga inserito nella variabile che segue tale parola. Tuttavia, si osservi che per resto si intende ciò che rimane moltiplicando il quoziente ottenuto (identifier-3) per il divisore (identifier-2 o literal-2), sottraendo poi questo valore ottenuto dal dividendo (identifier-1 o literal-1). Si osservi l'esempio che segue:

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-DIVIDE.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 DATE-WRITTEN. 2005-02-27.
000500*
000600 ENVIRONMENT DIVISION.
000700*
000800 DATA DIVISION.
000900*
001000 WORKING-STORAGE SECTION.
001100 77  A            PIC 9(10)V99.
001200 77  B            PIC 9(10)V99.
001400*
001500 PROCEDURE DIVISION.
001600*
001700 MAIN.
001800     DIVIDE 100 BY 3 GIVING A REMAINDER B.
001900     DISPLAY "100 / 3 = ", A, " CON IL RESTO DI ", B.
002000*
002100     STOP RUN.
002200*

Una volta compilato questo programma, se viene messo in funzione si dovrebbe ottenere il risultato seguente, che dovrebbe chiarire di che tipo di resto si parla con questa istruzione:

100 / 3 = 0000000033.33 CON IL RESTO DI 0000000000.01

579.8   Istruzione «EXIT»

L'istruzione EXIT serve a concludere anticipatamente l'esecuzione di un gruppo di paragrafi, attraverso un'istruzione PERFORM. L'istruzione EXIT deve essere usata da sola, all'interno di un paragrafo tutto per sé:

paragraph-name

    EXIT.
    ¯¯¯¯

Si osservi che un programma COBOL scritto in modo ordinato non dovrebbe avere bisogno di questa istruzione.

000000     PERFORM UNO THRU TRE.
000000     ...
000000 UNO.
000000     ...
000000 DUE.
000000     ...
000000     IF ...
000000       THEN
000000           GO TO TRE.
000000     ...
000000 TRE.
000000     EXIT.
000000 QUATTRO.
000000     ...

L'esempio appena mostrato serve a dare un'idea del significato dell'istruzione EXIT: la chiamata iniziale con l'istruzione PERFORM richiede l'esecuzione sequenziale dei paragrafi da UNO a TRE, ma nel paragrafo DUE si verifica una condizione e al suo avverarsi si esegue un salto (GO TO) al paragrafo TRE, che conclude comunque la chiamata principale.

Come già accennato, dal momento che l'uso dell'istruzione EXIT implica l'utilizzo di GO TO, che notoriamente complica la comprensibilità di un programma in modo eccessivo, entrambe queste istruzioni sono da evitare accuratamente.

579.9   Istruzione «GO TO»

L'istruzione GO TO consente di saltare all'inizio di un paragrafo specificato, senza ritorno. Sono previsti due modi di utilizzo:

GO TO  procedure-name
¯¯

Oppure:

GO TO  { procedure-name }... DEPENDING ON identifier
¯¯                           ¯¯¯¯¯¯¯¯¯

Nel primo caso, l'esecuzione dell'istruzione passa il controllo al paragrafo indicato; nel secondo, viene scelto il paragrafo a cui passare il controllo in base al valore indicato dopo la parola DEPENDING. Il valore in questione deve essere un numero intero, rappresentato attraverso una variabile (altrimenti non ci sarebbe motivo di usarlo), dove il valore uno rappresenta il primo paragrafo nominato dopo le parole GO TO e il valore n rappresenta il paragrafo n-esimo dello stesso elenco.

L'utilizzo dell'istruzione GO TO complica la lettura di un programma sorgente COBOL e, secondo il parere di molti, andrebbe abolita. Si veda a questo proposito: Edsger W. Dijkstra, Go To Statement Considered Harmful, 1968, <http://www.acm.org/classics/oct95/>, <http://www.kbs.uni-hannover.de/Lehre/SWTG/goto.pdf>, <http://www.cs.utsa.edu/~wagner/CS3723/nogoto/harm2.html> e altri indirizzi.

579.10   Istruzione «IF»

L'istruzione IF consente di eseguire un gruppo di istruzioni solo se si verifica una condizione, o se questa non si verifica. Il formato di questa istruzione è visibile nello schema seguente:

               /                    \  .--      /                    \  --.
               | { statement-1 }... |  |        | { statement-2 }... |    |
IF  condition  <                    >  |  ELSE  <                    >    |
¯¯             | NEXT SENTENCE      |  |  ¯¯¯¯  | NEXT SENTENCE      |    |
               \ ¯¯¯¯ ¯¯¯¯¯¯¯¯      /  `--      \ ¯¯¯¯ ¯¯¯¯¯¯¯¯      /  --'

Le istruzioni che seguono immediatamente la condizione (statement-1), vengono eseguite se la condizione si avvera; le istruzioni del gruppo che segue la parola ELSE vengono eseguite se la condizione non si avvera. Le istruzioni del primo e del secondo gruppo, possono contenere altre istruzioni IF.

Si osservi che la parola THEN è un separatore, ma viene usata spesso per migliorare la lettura di un'istruzione IF:

000000 IF ALTEZZA IS GREATER THAN 190
000000   THEN
000000       DISPLAY "LA PERSONA E` MOLTO ALTA!",
000000       PERFORM PERSONA-MOLTO-ALTA;
000000   ELSE
000000       IF ALTEZZA IS GREATER THAN 170
000000         THEN
000000             DISPLAY "LA PERSONA E` ABBASTANZA ALTA.",
000000             PERFORM PERSONA-ALTA;
000000         ELSE
000000             DISPLAY "LA PERSONA HA UN'ALTEZZA MEDIA O BASSA".

L'esempio mostra un'istruzione IF che ne contiene un'altra dopo la parola ELSE. Si può osservare che il punto fermo che conclude il gruppo di istruzioni appare solo alla fine della prima istruzione IF e costituisce l'unico modo per poter comprendere dove finisce tutta la struttura. Si osservi che la rappresentazione della struttura con dei rientri appropriati serve per individuare facilmente i livelli di annidamento esistenti.

Data la particolarità di questo esempio, i rientri potrebbero essere gestiti in modo diverso, per sottolineare la presenza di una serie di condizioni alternative (ELSE IF):

000000 IF ALTEZZA IS GREATER THAN 190
000000 THEN
000000     DISPLAY "LA PERSONA E` MOLTO ALTA!",
000000     PERFORM PERSONA-MOLTO-ALTA;
000000 ELSE IF ALTEZZA IS GREATER THAN 170
000000 THEN
000000     DISPLAY "LA PERSONA E` ABBASTANZA ALTA.",
000000     PERFORM PERSONA-ALTA;
000000 ELSE
000000     DISPLAY "LA PERSONA HA UN'ALTEZZA MEDIA O BASSA".

579.11   Istruzione «INSPECT»

L'istruzione INSPECT consente di scandire una variabile contenente una stringa alfanumerica, allo scopo di contare alcuni caratteri o di rimpiazzare alcuni dei caratteri della stringa. Sono previsti tre schemi sintattici per l'uso di questa istruzione, per il conteggio, la sostituzione, oppure per entrambe le cose simultaneamente.

Conteggio dei caratteri
INSPECT identifier-1 TALLYING
¯¯¯¯¯¯¯              ¯¯¯¯¯¯¯¯
    /                     /                                     .-- /          \           /                \ --.  \     \
    |                     |                                     |   |  BEFORE  |           |  identifier-4  |   |  |     |
    |                     |  CHARACTERS                         |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |     |
    |                     |  ¯¯¯¯¯¯¯¯¯¯                         |   |  AFTER   |           |  literal-2     |   |  |     |
    |                     |                                     `-- \  ¯¯¯¯¯   /           \                / --'  |     |
    <  identifier-2  FOR  <                                                                                        >...  >...
    |                ¯¯¯  |  /           \  /                \  .-- /          \           /                \ --.  |     |
    |                     |  |  ALL      |  |  identifier-3  |  |   |  BEFORE  |           |  identifier-4  |   |  |     |
    |                     |  <  ¯¯¯      >  <                >  |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |     |
    |                     |  |  LEADING  |  |  literal-1     |  |   |  AFTER   |           |  literal-2     |   |  |     |
    \                     \  \  ¯¯¯¯¯¯¯  /  \                /  `-- \  ¯¯¯¯¯   /           \                / --'  /     /
Sostituzione di caratteri
INSPECT identifier-1 REPLACING
¯¯¯¯¯¯¯              ¯¯¯¯¯¯¯¯¯
    /                                         /                \  .-- /          \           /                \ --.  \
    |                                         |  identifier-5  |  |   |  BEFORE  |           |  identifier-4  |   |  |
    |  CHARACTERS                         BY  <                >  |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |
    |  ¯¯¯¯¯¯¯¯¯¯                         ¯¯  |  literal-3     |  |   |  AFTER   |           |  literal-2     |   |  |
    |                                         \                /  `-- \  ¯¯¯¯¯   /           \                / --'  |
    <                                                                                                                >
    |  /  ALL      \  /                \      /                \  .-- /          \           /                \ --.  |
    |  |  ¯¯¯      |  |  identifier-3  |      |  identifier-5  |  |   |  BEFORE  |           |  identifier-4  |   |  |
    |  <  LEADING  >  <                >  BY  <                >  |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |
    |  |  ¯¯¯¯¯¯¯  |  |  literal-1     |  ¯¯  |  literal-3     |  |   |  AFTER   |           |  literal-2     |   |  |
    \  \  FIRST    /  \                /      \                /  `-- \  ¯¯¯¯¯   /           \                / --'  /
          ¯¯¯¯¯
Conteggio e sostituzione
INSPECT identifier-1 TALLYING
¯¯¯¯¯¯¯              ¯¯¯¯¯¯¯¯
    /                     /                                     .-- /          \           /                \ --.  \     \
    |                     |                                     |   |  BEFORE  |           |  identifier-4  |   |  |     |
    |                     |  CHARACTERS                         |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |     |
    |                     |  ¯¯¯¯¯¯¯¯¯¯                         |   |  AFTER   |           |  literal-2     |   |  |     |
    |                     |                                     `-- \  ¯¯¯¯¯   /           \                / --'  |     |
    <  identifier-2  FOR  <                                                                                        >...  >...
    |                ¯¯¯  |  /           \  /                \  .-- /          \           /                \ --.  |     |
    |                     |  |  ALL      |  |  identifier-3  |  |   |  BEFORE  |           |  identifier-4  |   |  |     |
    |                     |  <  ¯¯¯      >  <                >  |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |     |
    |                     |  |  LEADING  |  |  literal-1     |  |   |  AFTER   |           |  literal-2     |   |  |     |
    \                     \  \  ¯¯¯¯¯¯¯  /  \                /  `-- \  ¯¯¯¯¯   /           \                / --'  /     /

    REPLACING
    ¯¯¯¯¯¯¯¯¯
    /                                         /                \  .-- /          \           /                \ --.  \
    |                                         |  identifier-5  |  |   |  BEFORE  |           |  identifier-4  |   |  |
    |  CHARACTERS                         BY  <                >  |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |
    |  ¯¯¯¯¯¯¯¯¯¯                         ¯¯  |  literal-3     |  |   |  AFTER   |           |  literal-2     |   |  |
    |                                         \                /  `-- \  ¯¯¯¯¯   /           \                / --'  |
    <                                                                                                                >
    |  /  ALL      \  /                \      /                \  .-- /          \           /                \ --.  |
    |  |  ¯¯¯      |  |  identifier-3  |      |  identifier-5  |  |   |  BEFORE  |           |  identifier-4  |   |  |
    |  <  LEADING  >  <                >  BY  <                >  |   <  ¯¯¯¯¯¯  >  INITIAL  <                >   |  |
    |  |  ¯¯¯¯¯¯¯  |  |  literal-1     |  ¯¯  |  literal-3     |  |   |  AFTER   |           |  literal-2     |   |  |
    \  \  FIRST    /  \                /      \                /  `-- \  ¯¯¯¯¯   /           \                / --'  /
          ¯¯¯¯¯

In tutti gli schemi sintattici, la variabile indicata dopo la parola INSPECT, che viene annotata come identifier-1, deve contenere una stringa di caratteri, da scandire.

L'opzione BEFORE o AFTER, permette di individuare una posizione nella stringa, da prendere come limite finale, o come punto iniziale, per l'elaborazione. In pratica, la variabile identifier-4, o la costante letterale literal-2, serve a rappresentare una sottostringa (anche un solo carattere), che all'interno della stringa complessiva si trova per prima (a partire da sinistra); se si usa la parola BEFORE, l'elaborazione deve avvenire nella parta iniziale della stringa, fino a quella sottostringa di riferimento esclusa; se si usa la parola AFTER, l'elaborazione deve avvenire nella parta finale della stringa, subito dopo quella sottostringa. Naturalmente, se la sottostringa indicata non esiste nella stringa, è come se l'opzione BEFORE o AFTER non fosse stata aggiunta.

Con il primo schema sintattico, si vogliono contare i caratteri della stringa che soddisfano certe condizioni. Il conteggio viene eseguito incrementando il valore contenuto nella variabile indicata nello schema come identifier-2, che deve essere numerica. Si osservi che la variabile non viene azzerata automaticamente, pertanto il suo valore iniziale viene sommato al conteggio eseguito.

Il conteggio può riguardare tutti i caratteri della stringa o della porzione iniziale o finale selezionata, utilizzando la parola CHARACTERS. Si osservi l'esempio successivo che utilizza solo questo tipo di conteggio.

Listato 579.36. Programma elementare che scandisce una stringa e conta i caratteri contenuti.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-INSPECT-TALLYING-1.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-15.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 77  STRINGA-DI-CARATTERI PIC X(30).
001400 77  CONTATORE-1          PIC 99 VALUE IS 0.
001500 77  CONTATORE-2          PIC 99 VALUE IS 0.
001600 77  CONTATORE-3          PIC 99 VALUE IS 0.
001700*
001800 PROCEDURE DIVISION.
001900*------------------------- LIVELLO 0 -----------------------------
002000 MAIN.
002100     MOVE "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123"
002200          TO STRINGA-DI-CARATTERI.
002300     INSPECT STRINGA-DI-CARATTERI
002400             TALLYING CONTATORE-1
002500             FOR CHARACTERS,
002600             TALLYING CONTATORE-2
002700             FOR CHARACTERS BEFORE INITIAL "H",
002800             TALLYING CONTATORE-3
002900             FOR CHARACTERS AFTER INITIAL "H".
003000     DISPLAY "CONTATORI: ", CONTATORE-1, " ", CONTATORE-2, " ",
003100             CONTATORE-3.
003200     STOP RUN.

L'esempio appena mostrato utilizza un'istruzione INSPECT per contare tre cose in una stringa, con una sola scansione: i caratteri contenuti in tutta la stringa; i caratteri fino alla comparsa della prima lettera «H»; i caratteri che si trovano dopo la lettera «H»:

            30 caratteri
    <---------------------------->
    ABCDEFGHIJKLMNOPQRSTUVWXYZ0123
    <-----> <-------------------->
 7 caratteri      22 caratteri

Compilando l'esempio e avviando il programma eseguibile che si ottiene, si dovrebbe vedere il risultato seguente:

CONTATORI: 30 07 22

Con la parola ALL si intendono contare tutte le corrispondenze con una certa sottostringa (identifier-3 o literal-1), contenuta nella stringa complessiva o nella porzione specificata successivamente. Con la parola LEADING, si vogliono contare solo le corrispondenze che avvengono in modo contiguo, purché inizino dal principio della zona di interesse.

Listato 579.39. Programma elementare che scandisce una stringa e conta i caratteri che corrispondono a delle sottostringhe.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-INSPECT-TALLYING-2.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-15.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 77  STRINGA-DI-CARATTERI PIC X(30).
001400 77  CONTATORE-1          PIC 99 VALUE IS 0.
001500 77  CONTATORE-2          PIC 99 VALUE IS 0.
001600 77  CONTATORE-3          PIC 99 VALUE IS 0.
001700*
001800 PROCEDURE DIVISION.
001900*------------------------- LIVELLO 0 -----------------------------
002000 MAIN.
002100     MOVE "ABCDEFGHIAAAABBBBCCCCDDDDEEEEF"
002200          TO STRINGA-DI-CARATTERI.
002300     INSPECT STRINGA-DI-CARATTERI
002400             TALLYING CONTATORE-1
002500             FOR ALL "E",
002600             TALLYING CONTATORE-2
002700             FOR LEADING "A" AFTER INITIAL "I",
002800             TALLYING CONTATORE-3
002900             FOR LEADING "B" BEFORE INITIAL "I".
003000     DISPLAY "CONTATORI: ", CONTATORE-1, " ", CONTATORE-2, " ",
003100             CONTATORE-3.
003200     STOP RUN.

In questo esempio viene cercata la corrispondenza con tutte le lettere «E»; le lettere «A» adiacenti che iniziano a partire dalla prima apparizione della lettera «I»; le lettere «B» adiacenti e iniziali, che si trovano prima di quella stessa lettera «I».

            5 lettere «E»
        -------------------------
        |                    ||||
    ABCDEFGHIAAAABBBBCCCCDDDDEEEEF
            |\\\\
            | ¯¯¯¯¯¯4 lettere «A» adiacenti e iniziali
            |
   lettera «I» di riferimento

Non ci sono lettere «B» adiacenti e iniziali
prima del riferimento.

Compilando l'esempio e avviando il programma eseguibile che si ottiene, si dovrebbe vedere il risultato seguente:

CONTATORI: 05 04 00

Il secondo schema sintattico mostra l'uso di INSPECT per rimpiazzare delle sottostringhe. L'interpretazione dello schema è simile a quella del conteggio, con la differenza che si aggiunge la parola chiave BY, che ha alla sinistra la sottostringa da rimpiazzare e alla destra il suo nuovo valore. Quando si usa la parola CHARACTERS, si intende rimpiazzare tutta la stringa (o tutta la porzione prima o dopo un certo riferimento), con qualcosa con un carattere; le parole ALL e LEADING funzionano sostanzialmente come nel conteggio, riferendosi a tutte le sottostringhe di un certo tipo o a tutte le sottostringhe iniziali e adiacenti, dello stesso tipo. In questo schema, si aggiunge la parola FIRST, che identifica solo una prima corrispondenza, non ripetuta.

Listato 579.42. Programma che scandisce una stringa e sostituisce alcuni suoi contenuti. Il programma sfrutta un'estensione al linguaggio standard, che permette di eseguire più sostituzioni in una sola istruzione INSPECT.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-INSPECT-REPLACING.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-15.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 77  STRINGA-DI-CARATTERI PIC X(30).
001400*
001500 PROCEDURE DIVISION.
001600*------------------------- LIVELLO 0 -----------------------------
001700 MAIN.
001800     MOVE "AAAAAABBBBBBCCCCCCDDDDDDEEEEEE"
001900          TO STRINGA-DI-CARATTERI.
002000     INSPECT STRINGA-DI-CARATTERI REPLACING
002100             CHARACTERS BY "X" AFTER INITIAL "DDD",
002200             LEADING "BB" BY "YZ" AFTER INITIAL "AAAAAA",
002300             FIRST "C" BY "W",
002400             ALL "C" BY "P".
002500     DISPLAY STRINGA-DI-CARATTERI.
002600     STOP RUN.

L'esempio appena mostrato sfrutta un'estensione al linguaggio tradizionale, in modo da ottenere più sostituzioni con una sola passata. L'esempio fatto in questo modo permette di capire cosa succede in queste situazioni particolari.

AAAAAABBBBBBCCCCCCDDDDDDEEEEEE
                     XXXXXXXXX  CHARACTERS BY "X" AFTER INITIAL "DDD"
      YZYZYZ                    LEADING "BB" BY "YZ" AFTER INITIAL "AAAAAA"
            W                   FIRST "C" BY "W"
             PPPPP              ALL "C" BY "P"
AAAAAAYZYZYZWPPPPPDDDXXXXXXXXX

Compilando l'esempio e avviando il programma eseguibile che si ottiene, si dovrebbe vedere il risultato seguente che rappresenta soltanto il contenuto finale della variabile elaborata:

AAAAAAYZYZYZWPPPPPDDDXXXXXXXXX

579.12   Istruzione «MOVE»

L'istruzione MOVE copia o assegna un valore in una o più variabili di destinazione. Sono disponibili due modi di usare questa istruzione:

      /              \
      | identifier-1 |
MOVE  <              >  TO  { identifier-2 }...
¯¯¯¯  | literal-1    |  ¯¯
      \              /

Oppure:

      /               \
      | CORRESPONDING |
MOVE  < ¯¯¯¯¯¯¯¯¯¯¯¯¯ >  identifier-1  TO  { identifier-2 }...
¯¯¯¯  | CORR          |                ¯¯
      \ ¯¯¯¯          /

Nel primo caso, ciò che appare dopo la parola chiave MOVE può essere il nome di una variabile, oppure una costante. Il valore contenuto nella variabile o rappresentato dalla costante, viene copiato in tutte le variabili indicate dopo la parola TO, rispettando eventualmente le regole di modifica stabilite dai modelli di definizione delle variabili.

Nel secondo caso, avendo aggiunto la parola CORRESPONDING (o soltanto CORR), si copia il contenuto di una variabile strutturata in una o più variabili strutturate, abbinando però i campi aventi lo stesso nome. In pratica, con il secondo schema si vogliono copiare i campi della prima variabile strutturata che hanno gli stessi nomi di quelli contenuti nella seconda variabile strutturata. Diversamente, per una copia di una variabile strutturata in altre variabili, mantenendo inalterata la struttura originale dei dati, si usa il primo schema sintattico.

È bene ricordare che in alcuni casi la copia dei dati non può essere eseguita; per esempio non si può assegnare a una variabile numerica un'informazione alfanumerica (tenendo conto che una variabile numerica che contiene delle regole di modifica, all'atto della sua lettura offre un'informazione alfanumerica).

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-MOVE.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 DATE-WRITTEN. 2005-02-28.
000500
000600 ENVIRONMENT DIVISION.
000700
000800 DATA DIVISION.
000900
001000 WORKING-STORAGE SECTION.
001100 01  RECORD-1.
001200     02 A         PIC 999V99.
001300     02 B         PIC X(10).
001400     02 C         PIC 99999.
001500
001600 01  RECORD-2.
001700     02 C         PIC 9999999.
001800     02 B         PIC X(12).
001900     02 A         PIC 9999V999.
002000
002100 PROCEDURE DIVISION.
002200
002300 MAIN.
002400     MOVE 123.45       TO A OF RECORD-1.
002500     MOVE "ABCDEFGHIJ" TO B OF RECORD-1.
002600     MOVE 12345        TO C OF RECORD-1.
002700     DISPLAY "RECORD-1: ", RECORD-1.
002800     DISPLAY "    A:    ", A OF RECORD-1.
002900     DISPLAY "    B:    ", B OF RECORD-1.
003000     DISPLAY "    C:    ", C OF RECORD-1.
003100
003200     MOVE RECORD-1 TO RECORD-2.
003300     DISPLAY "RECORD-2: ", RECORD-2
003400     DISPLAY "    A:    ", A OF RECORD-2.
003500     DISPLAY "    B:    ", B OF RECORD-2.
003600     DISPLAY "    C:    ", C OF RECORD-2.
003700
003800     MOVE CORRESPONDING RECORD-1 TO RECORD-2.
003900     DISPLAY "RECORD-2: ", RECORD-2
004000     DISPLAY "    A:    ", A OF RECORD-2.
004100     DISPLAY "    B:    ", B OF RECORD-2.
004200     DISPLAY "    C:    ", C OF RECORD-2.
004300
004400     STOP RUN.

L'esempio mostra un programma in cui ci sono due variabili strutturate, contenenti campi, simili, con lo stesso nome, ordinati in modo differente. Dopo aver assegnato dei valori ai campi della prima variabile, il contenuto della variabile viene copiato nella seconda; successivamente, viene ripetuta la copia in modo corrispondente.

Se si compila il programma con OpenCOBOL e si avvia ciò che si ottiene, si dovrebbe vedere un risultato simile a quello seguente, dove si può notare la differenza tra un tipo di copia e l'altra:

RECORD-1: 12345ABCDEFGHIJ12345
    A:    123.45
    B:    ABCDEFGHIJ
    C:    12345
RECORD-2: 12345ABCDEFGHIJ12345      
    A:    5   .000
    B:    CDEFGHIJ1234
    C:    12345A 
RECORD-2: 0012345ABCDEFGHIJ  0123450
    A:    0123.450
    B:    ABCDEFGHIJ  
    C:    0012345

Si osservi che una variabile di tipo INDEX non può essere usata con l'istruzione MOVE. Per assegnare un valore a una tale variabile occorre servirsi dell'istruzione SET.

579.13   Istruzione «MULTIPLY»

L'istruzione MULTIPLY consente di eseguire delle moltiplicazioni. Sono previsti due diversi formati per l'utilizzo di questa istruzione.

          /              \
          | identifier-1 |
MULTIPLY  <              >  BY  { identifier-2 [ROUNDED] }...
¯¯¯¯¯¯¯¯  | literal-1    |  ¯¯                  ¯¯¯¯¯¯¯
          \              / 

    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Nello schema sintattico appena mostrato, si vede che dopo la parola chiave MULTIPLY viene indicato un valore, in forma costante o attraverso una variabile; questo valore viene moltiplicato per la variabile indicata dopo la parola chiave BY e il risultato viene assegnato alla stessa variabile che funge da moltiplicatore. Se appaiono più variabili dopo la parola BY, la moltiplicazione viene ripetuta per ognuna di quelle, assegnando rispettivamente il risultato.

L'opzione ROUNDED richiede di eseguire un arrotondamento se la variabile ricevente non può rappresentare in modo esatto il valore; l'opzione SIZE ERROR serve a eseguire un'istruzione nel caso una delle variabili riceventi non possa accogliere la porzione più significativa del valore ottenuto dalla somma. Si osservi l'esempio seguente:

000000     MULTIPLY 100 BY A.

Supponendo che la variabile A, prima della divisione contenga il valore 5, dopo l'operazione contiene il valore 500 (100×5). Si potrebbe scrivere la stessa cosa utilizzando l'istruzione COMPUTE:

000000     COMPUTE A = 100 * A.

Lo schema sintattico successivo mostra l'utilizzo di MULTIPLY in modo da non alterare i valori utilizzati come moltiplicatori:

          /              \        /              \
          | identifier-1 |        | identifier-2 |
MULTIPLY  <              >   BY   <              >
¯¯¯¯¯¯¯¯  | literal-1    |   ¯¯   | literal-2    |
          \              /        \              /

    GIVING identifier-3  [ROUNDED]
    ¯¯¯¯¯¯                ¯¯¯¯¯¯¯
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Nella forma appena mostrata, la moltiplicazione avviene immettendo il risultato dell'operazione nella variabile indicata dopo la parola GIVING. Valgono le stesse considerazioni già fatte a proposito delle opzioni ROUNDED e SIZE ERROR. Si osservi l'esempio seguente che ripete sostanzialmente l'esempio già mostrato in precedenza:

000000     MULTIPLY 100 BY 5 GIVING A.

579.14   Istruzione «OPEN»

L'istruzione OPEN serve ad aprire un file, o un gruppo di file, specificando la modalità di accesso. Quando l'accesso a un file richiede l'esecuzione di alcune procedure meccaniche preliminari, questa istruzione serve a eseguirle. L'istruzione OPEN non riguarda i file dichiarati esplicitamente per il riordino e la fusione.

      /  INPUT   { file-name  [ WITH NO REWIND ] }...  \
      |  ¯¯¯¯¯                       ¯¯ ¯¯¯¯¯¯         |
      |  OUTPUT  { file-name  [ WITH NO REWIND ] }...  |
OPEN  <  ¯¯¯¯¯¯                      ¯¯ ¯¯¯¯¯¯         >...
¯¯¯¯  |  I-O     { file-name }...                      |
      |  ¯¯¯                                           |
      \  EXTEND  { file-name }...                      /
         ¯¯¯¯¯¯

Dopo la parola chiave OPEN inizia l'elenco dei file che si vogliono aprire, cominciando con la parola chiave che definisce la modalità di accesso desiderata: INPUT richiede un accesso in lettura; OUTPUT un accesso in scrittura; I-O un accesso in lettura e scrittura; EXTEND un accesso in estensione (scrittura).

Il tipo di accesso consentito dipende dall'organizzazione dei file o dalla modalità di accesso; nelle versioni più vecchie del linguaggio, l'apertura in estensione (EXTEND) può essere usata soltanto per i file sequenziali; l'apertura in lettura e scrittura (I-O) richiede che il file sia collocato in un'unità di memorizzazione ad accesso diretto, come nel caso dei dischi.

L'opzione NO REWIND si riferisce al riavvolgimento automatico del nastro, che riguarda, evidentemente, solo file sequenziali su unità ad accesso sequenziale, che possono richiedere un'operazione di riavvolgimento. Se si usa questa opzione, si intende evitare che il nastro venga riavvolto automaticamente alla chiusura del file stesso. Per i file su disco, o comunque su unità ad accesso diretto, anche se si tratta di file con organizzazione sequenziale, questa opzione non deve essere usata.

Quando un file viene aperto (con questa istruzione) è possibile accedervi secondo la modalità prevista, con le istruzioni appropriate. L'apertura va eseguita una sola volta e la chiusura (con l'istruzione CLOSE) dichiara la conclusione delle operazioni con quel file. Se un file deve essere riaperto all'interno del programma, probabilmente perché vi si vuole accedere secondo una modalità differente, o per altri motivi, è necessario che alla chiusura non sia utilizzata l'opzione lock, che altrimenti impedirebbe di farlo.

L'apertura in lettura che si ottiene con la parola chiave READ serve ad accedere a un file esistente in modo da poter leggere il suo contenuto; l'apertura fa sì che la posizione relativa, iniziale, all'interno del file, corrisponda al primo record logico. Se il file non esiste, si presenta una condizione di errore.

L'apertura in scrittura che si ottiene con la parola chiave OUTPUT serve a creare un file, ma se il file esiste già, questo viene azzerato completamente.

L'apertura in lettura e scrittura che si ottiene con la parola chiave I-O serve a permettere l'accesso a un file esistente, sia per leggere i dati, sia per modificarli. La posizione relativa iniziale è quella del primo record logico.

L'apertura in estensione che si ottiene con la parola chiave EXTEND, può essere utilizzata soltanto con file sequenziali e serve a consentire l'aggiunta di record a partire dalla fine del file iniziale. Pertanto, il puntatore relativo iniziale si trova dopo la fine dell'ultimo record logico e l'utilizzo di questo file avviene nello stesso modo di un'apertura in scrittura, con la differenza che il contenuto precedente non viene cancellato.

Se il file che viene aperto è associato a una variabile indicata con l'opzione FILE STATUS nell'istruzione SELECT (nella sezione FILE-CONTROL di ENVIRONMENT DIVISION), il valore di tale variabile viene aggiornato.

Tabella 579.55. File con organizzazione sequenziale (e accesso sequenziale).

Istruzione Apertura in lettura (INPUT) Apertura in scrittura (OUTPUT) Apertura in lettura e scrittura (I-O) Apertura in estensione (EXTEND)
READ
X X
WRITE
X X
REWRITE
X

Tabella 579.56. File con organizzazione relativa o a indice, con accesso sequenziale.

Istruzione Apertura in lettura (INPUT) Apertura in scrittura (OUTPUT) Apertura in lettura e scrittura (I-O)
READ
X X
WRITE
X
REWRITE
X
START
X X
DELETE
X

Tabella 579.57. File con organizzazione relativa o a indice, con accesso diretto (random).

Istruzione Apertura in lettura (INPUT) Apertura in scrittura (OUTPUT) Apertura in lettura e scrittura (I-O)
READ
X X
WRITE
X X
REWRITE
X
START
DELETE
X

Tabella 579.58. File con organizzazione relativa o a indice, con accesso dinamico.

Istruzione Apertura in lettura (INPUT) Apertura in scrittura (OUTPUT) Apertura in lettura e scrittura (I-O)
READ
X X
WRITE
X X
REWRITE
X
START
X X
DELETE
X

579.15   Istruzione «PERFORM»

L'istruzione PERFORM consente di eseguire un gruppo di istruzioni, contenute all'interno di sezioni o di paragrafi della divisione PROCEDURE DIVISION, riprendendo poi il funzionamento nell'istruzione successiva.

Sono disponibili schemi sintattici diversi, perché la chiamata di queste procedure può essere gestita in maniere differenti. In effetti, questa istruzione è il mezzo con cui realizzare delle iterazioni, normali e con enumerazione, pertanto si rende necessaria questa flessibilità da parte dell'istruzione PERFORM.

Nelle sezioni successive vengono descritte le varie forme di utilizzo dell'istruzione PERFORM, per livelli successivi di complessità. Si tenga conto che la spiegazione riguardo al funzionamento per un certo livello, riguarda anche quelli più complessi successivi.

579.15.1   Chiamata semplice

                           .--  /           \                  --.
                           |    |  THROUGH  |                    |
PERFORM  procedure-name-1  |    <  ¯¯¯¯¯¯¯  >  procedure-name-2  |
¯¯¯¯¯¯¯                    |    |  THRU     |                    |
                           `--  \  ¯¯¯¯     /                  --'

Secondo la forma di utilizzo più semplice dell'istruzione PERFORM, la chiamata esegue una volta sola l'intervallo di procedure indicate. Per procedure qui si intendono dei paragrafi, oppure delle sezioni intere della divisione PROCEDURE DIVISION.

Se si indica soltanto un nome (di paragrafo o di sezione), si intende eseguire solo la procedura relativa; se si indica la parola THROUGH o THRU seguita da un altro nome, si intendono eseguire tutti i paragrafi o tutte le sezioni dal primo al secondo nome incluso.

Il fatto che la chiamata di una procedura avvenga in modo così libero, implica la necessità di stabilire delle restrizioni alle chiamate annidate: una procedura, o un insieme di procedure chiamate attraverso l'istruzione PERFORM, possono contenere delle chiamate annidate. Queste chiamate interne, per poter essere eseguite correttamente, devono riguardare delle procedure più interne, oppure completamente esterne.

Figura 579.60. Schematizzazione delle forme di annidamento consentite e di quella non consentita (sbarrata).

esempio

La figura mostra schematicamente i vari modi in cui le istruzioni PERFORM possono annidarsi, o possono in qualche modo riguardare le stesse porzioni di codice. L'ultimo esempio, in basso a destra, non è ammissibile perché la chiamata dei paragrafi da D a F verrebbe interrotta alla conclusione del paragrafo D, con il rientro dalla prima istruzione PERFORM.

579.15.2   Chiamata ripetuta un certo numero di volte

                           .--  /           \                  --.
                           |    |  THROUGH  |                    |
PERFORM  procedure-name-1  |    <  ¯¯¯¯¯¯¯  >  procedure-name-2  |
¯¯¯¯¯¯¯                    |    |  THRU     |                    |
                           `--  \  ¯¯¯¯     /                  --'
        /                \
        |  identifier-1  |
        <                >  TIMES
        |  integer-1     |  ¯¯¯¯¯
        \                /

Aggiungendo allo schema già visto un numero intero, espresso sia in forma costante, sia attraverso una variabile, seguito dalla parola TIMES, si intende ottenere a ripetizione della chiamata del gruppo di procedure indicato per quella quantità di volte.

Se il valore numerico indicato è pari a zero, oppure si tratta di un numero negativo, la chiamata delle procedure viene ignorata semplicemente.

579.15.3   Chiamata ripetuta con condizione di uscita

                           .--  /           \                  --.
                           |    |  THROUGH  |                    |
PERFORM  procedure-name-1  |    <  ¯¯¯¯¯¯¯  >  procedure-name-2  |
¯¯¯¯¯¯¯                    |    |  THRU     |                    |
                           `--  \  ¯¯¯¯     /                  --'

        UNTIL  condition-1
        ¯¯¯¯¯

Quando nell'istruzione PERFORM compare la parola chiave UNTIL, seguita da una condizione, si intende eseguire il gruppo di procedure indicate ripetutamente, fino a quando la condizione specificata restituisce il valore Falso.

La condizione di uscita viene verificata prima di eseguire ogni iterazione, pertanto, se risulta Vero all'inizio, le procedure non vengono eseguite.

Rispetto ai linguaggi di programmazione comuni, il COBOL attribuisce alla parola UNTIL un significato opposto, anche se logico: «si esegue il ciclo fino a quanto si verifica la condizione». Il problema è che nel senso comune ciò significa che il ciclo va ripetuto in quanto la condizione continua ad avverarsi, mentre secondo il senso del COBOL il ciclo va ripetuto fino a quando si verifica la condizione di uscita, nel senso che il verificarsi della condizione di uscita fa terminare il ciclo.

Figura 579.63. Diagramma di flusso dell'istruzione PERFORM iterativa con una condizione di uscita.

diagramma di flusso

579.15.4   Chiamata ripetuta con condizione di uscita e incremento di contatori

                           .--  /           \                  --.
                           |    |  THROUGH  |                    |
PERFORM  procedure-name-1  |    <  ¯¯¯¯¯¯¯  >  procedure-name-2  |
¯¯¯¯¯¯¯                    |    |  THRU     |                    |
                           `--  \  ¯¯¯¯     /                  --'

                 /              \        / identifier-3 \      /              \
                 | identifier-2 |        |              |      | identifier-4 |
        VARYING  <              >  FROM  < index-name-2 >  BY  <              >
        ¯¯¯¯¯¯¯  | index-name-1 |  ¯¯¯¯  |              |  ¯¯  | literal-2    |
                 \              /        \ literal-1    /      \              /

                UNTIL  condition-1
                ¯¯¯¯¯
        /         /              \        / identifier-6 \      /              \ \
        |         | identifier-5 |        |              |      | identifier-7 | |
        |  AFTER  <              >  FROM  < index-name-4 >  BY  <              > |
        <  ¯¯¯¯¯  | index-name-3 |  ¯¯¯¯  |              |  ¯¯  | literal-4    | >...
        |         \              /        \ literal-3    /      \              / |
        |                                                                        |
        \       UNTIL  condition-2                                               /
                ¯¯¯¯¯

Con l'aggiunta della parola chiave VARYING, si intende gestire un contatore numerico (rappresentato nello schema da identifier-2 o da index-name-1, che pertanto può essere una variabile numerica o un indice di una tabella), specificando il valore di partenza dopo la parola FROM, l'incremento a ogni ciclo dopo la parola BY e la condizione di uscita dopo la parola UNTIL.

Possono essere gestiti più contatori, con un limite che dipende dal compilatore. A ogni modo, per aggiungere un contatore si usa la parola AFTER, che ne introduce la descrizione, così come per la parola VARYING.

Il contatore che viene incrementato a ogni ciclo, è quello più interno, ovvero quello descritto dall'ultima parola AFTER. Quando per quel contatore si verifica la condizione di uscita, viene incrementato il contatore del livello precedente (la penultima parola AFTER o direttamente VARYING in mancanza di quella) e azzerato quello interno.

Il ciclo termina quando sono scattate tutte le condizioni di uscita dei vari contatori.

Il linguaggio non pone vincoli alla gestione dei contatori indicati nell'istruzione PERFORM, che possono essere alterati durante l'esecuzione delle procedure chiamate dall'istruzione stessa e in qualche modo possono contaminarsi tra di loro. Sta evidentemente al programmatore evitare di creare confusione nel programma, osservando anche che la sequenza esatta delle operazioni di incremento e azzeramento dei contatori cambia leggermente da uno standard all'altro del linguaggio.

Figura 579.65. Diagramma di flusso dell'istruzione PERFORM iterativa con l'incremento di un solo contatore.

diagramma di flusso

Figura 579.66. Diagramma di flusso dell'istruzione PERFORM iterativa con la gestione di due contatori.

diagramma di flusso

L'esempio seguente mostra in modo molto semplice la gestione di tre contatori, che scandiscono valori interi da zero a due, senza fare nulla altro di particolare.

Listato 579.67. Programma chiama un paragrafo incrementando tre contatori.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-PERFORM.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-17.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 77  CONTATORE-1          PIC 99.
001400 77  CONTATORE-2          PIC 99.
001500 77  CONTATORE-3          PIC 99.
001600*
001700 PROCEDURE DIVISION.
001800*------------------------- LIVELLO 0 -----------------------------
001900 MAIN.
002000     PERFORM VISUALIZZA-CONTATORI
002100             VARYING CONTATORE-1 FROM 0 BY 1
002200                     UNTIL CONTATORE-1 >= 2,
002300             AFTER   CONTATORE-2 FROM 0 BY 1
002400                     UNTIL CONTATORE-2 >= 2,
002500             AFTER   CONTATORE-3 FROM 0 BY 1
002600                     UNTIL CONTATORE-3 >= 2.
002700*
002800     STOP RUN.
002900*------------------------- LIVELLO 1 -----------------------------
003000 VISUALIZZA-CONTATORI.
003100     DISPLAY CONTATORE-1, " ", CONTATORE-2, " ", CONTATORE-3.
003200*

Una volta compilato questo programma, avviando ciò che si ottiene, si può vedere il risultato seguente:

00 00 00
00 00 01
00 01 00
00 01 01
01 00 00
01 00 01
01 01 00
01 01 01

579.16   Istruzione «READ»

L'istruzione READ serve a ottenere un record logico da un file, che risulta essere già stato aperto, in modo tale da consentire la lettura (INPUT o I-O). Sono disponibili formati diversi per l'utilizzo di questa istruzione, che dipendono dall'organizzazione del file a cui si accede.

Organizzazione sequenziale, relativa e a indice
READ file-name [NEXT] RECORD  [ INTO identifier ]
¯¯¯¯            ¯¯¯¯            ¯¯¯¯
    [ AT END  { imperative-statement }... ]
         ¯¯¯
Organizzazione relativa
READ file-name RECORD  [ INTO identifier ]
¯¯¯¯                     ¯¯¯¯
    [ INVALID KEY { imperative-statement }... ]
      ¯¯¯¯¯¯¯
Organizzazione a indice
READ file-name RECORD  [ INTO identifier ]
¯¯¯¯                     ¯¯¯¯
    [ KEY IS  data-name ]
      ¯¯¯
    [ INVALID KEY { imperative-statement }... ]
      ¯¯¯¯¯¯¯

In tutti gli schemi sintattici che riguardano l'istruzione READ, si può vedere che viene indicato immediatamente il nome del file (già aperto) che si vuole leggere. Successivamente, appare una parola chiave opzionale, INTO, che precede il nome di una variabile; se viene specificata questa informazione, si intende fare in modo che il record logico ottenuto dal file, oltre che essere disponibile nella variabile strutturata dichiarata appositamente per questo, dopo l'indicatore di livello FD relativo, sia anche copiato in un'altra variabile. Inoltre, le istruzioni imperative (imperative-statement) che si possono inserire dopo le parole AT END e INVALID KEY, servono a dichiarare cosa deve fare il programma nel caso la lettura fallisca per qualche motivo.

Se il file che viene letto è associato a una variabile indicata con l'opzione FILE STATUS nell'istruzione SELECT (nella sezione FILE-CONTROL di ENVIRONMENT DIVISION), il valore di tale variabile viene aggiornato.

Nel caso di un file a cui si accede sequenzialmente, si applica il primo schema sintattico. In questo caso l'istruzione READ fornisce il record attuale e sposta in avanti il puntatore al record, in modo che una lettura successiva fornisca il prossimo record. Quando l'accesso è dinamico e si vuole leggere un file in modo sequenziale, occorre aggiungere l'opzione NEXT, per richiedere espressamente l'avanzamento al record successivo.

Quando si accede sequenzialmente, oppure in modo dinamico ma specificando che si richiede il record successivo, si può verificare un errore che consiste nel tentativo di leggere oltre la fine del file. Se ciò accade e se è stata specificata l'opzione AT END, vengono eseguite le istruzioni che seguono tali parole.

La lettura sequenziale di un file relativo, comporta l'aggiornamento del valore della «chiave relativa», ovvero di quanto specificato con la dichiarazione RELATIVE KEY dell'istruzione SELECT.

La lettura sequenziale può essere applicata anche a un file organizzato a indice; in tal caso, la sequenza di lettura corrisponde a quella della chiave principale.

Quando si accede in modo diretto ai record all'interno di un file relativo, si utilizza il secondo schema sintattico, per ottenere il record specificato dal numero contenuto nella variabile che funge da chiave (come specificato nell'istruzione SELECT, attraverso la dichiarazione RELATIVE KEY). Se un record con quel numero non esiste, si verifica la condizione controllata dall'opzione INVALID KEY e il programma esegue le istruzioni che questa controlla.

Il terzo formato sintattico si usa per i file organizzati a indice, con accesso diretto, in base alla chiave specificata. La chiave in questione è quella primaria, salvo specificarla nell'istruzione READ con l'opzione KEY IS. La chiave cercata deve essere scritta in corrispondenza del campo che la contiene, all'interno del record dichiarato dopo l'indicatore di livello FD relativo al file, secondo le specifiche dell'istruzione SELECT (RECORD KEY, o ALTERNATE RECORD KEY). Se la lettura avviene con successo, si ottiene il record che contiene quella chiave; altrimenti si verifica la condizione controllata dall'opzione INVALID KEY e le istruzioni relative vengono eseguite.

La lettura ad accesso diretto di un file a indice, consente di ottenere il primo record che soddisfa la corrispondenza con la chiave cercata; se sono presenti record con chiavi doppie, le altre corrispondenze devono essere raggiunte attraverso una lettura sequenziale.

Listato 579.72. Programma elementare che legge un file sequenziale, ad accesso sequenziale.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-READ-SEQ.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               TINYCOBOL 0.61,
000600               OPENCOBOL 0.31.
000700 DATE-WRITTEN. 2005-03-12.
000800*
000900 ENVIRONMENT DIVISION.
001000*
001100 INPUT-OUTPUT SECTION.
001200*
001300 FILE-CONTROL.
001400*
001500     SELECT FILE-DA-LEGGERE ASSIGN TO "input.seq"
001600                            ORGANIZATION IS SEQUENTIAL.
001700*
001800 DATA DIVISION.
001900*
002000 FILE SECTION.
002100*
002200 FD  FILE-DA-LEGGERE
002300     LABEL RECORD IS STANDARD.
002400*
002500 01  RECORD-DA-LEGGERE PIC X(30).
002600*
002700 WORKING-STORAGE SECTION.
002800 01  EOF               PIC 9   VALUE ZERO.
002900*
003000 PROCEDURE DIVISION.
003100*------------------------- LIVELLO 0 -----------------------------
003200 MAIN.
003300     OPEN INPUT FILE-DA-LEGGERE.
003400     READ FILE-DA-LEGGERE
003500          AT END
003600                MOVE 1 TO EOF.
003700     PERFORM LETTURA UNTIL EOF = 1.
003800     CLOSE FILE-DA-LEGGERE.
003900*
004000     STOP RUN.
004100*------------------------- LIVELLO 1 -----------------------------
004200 LETTURA.
004300     DISPLAY RECORD-DA-LEGGERE.
004400     READ FILE-DA-LEGGERE
004500          AT END
004600                MOVE 1 TO EOF.
004700*

Listato 579.73. Programma elementare che legge un file sequenziale, ad accesso dinamico. Le differenze rispetto all'esempio precedente sono evidenziate.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-READ-DYN.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               TINYCOBOL 0.61,
000600               OPENCOBOL 0.31.
000700 DATE-WRITTEN. 2005-03-12.
000800*
000900 ENVIRONMENT DIVISION.
001000*
001100 INPUT-OUTPUT SECTION.
001200*
001300 FILE-CONTROL.
001400*
001500     SELECT FILE-DA-LEGGERE ASSIGN TO "input.seq"
001600                            ORGANIZATION IS SEQUENTIAL
001700                            ACCESS MODE IS DYNAMIC.
001800*
001900 DATA DIVISION.
002000*
002100 FILE SECTION.
002200*
002300 FD  FILE-DA-LEGGERE
002400     LABEL RECORD IS STANDARD.
002500*
002600 01  RECORD-DA-LEGGERE PIC X(30).
002700*
002800 WORKING-STORAGE SECTION.
002900 01  EOF               PIC 9   VALUE ZERO.
003000*
003100 PROCEDURE DIVISION.
003200*------------------------- LIVELLO 0 -----------------------------
003300 MAIN.
003400     OPEN INPUT FILE-DA-LEGGERE.
003500     READ FILE-DA-LEGGERE
003600          AT END
003700                MOVE 1 TO EOF.
003800     PERFORM LETTURA UNTIL EOF = 1.
003900     CLOSE FILE-DA-LEGGERE.
004000*
004100     STOP RUN.
004200*------------------------- LIVELLO 1 -----------------------------
004300 LETTURA.
004400     DISPLAY RECORD-DA-LEGGERE.
004500     READ FILE-DA-LEGGERE NEXT RECORD
004600          AT END
004700                MOVE 1 TO EOF.
004800*

579.17   Istruzione «REWRITE»

L'istruzione REWRITE consente si sovrascrivere un record logico all'interno di un file, purché questo risieda all'interno di un'unità che consente un accesso diretto ai dati (le unità sequenziali come i nastri sono escluse). Per utilizzare l'istruzione REWRITE il file deve essere stato aperto in lettura e scrittura (I-O); inoltre, il record deve avere una dimensione fissa.

File organizzati in modo sequenziale
REWRITE record-name  [ FROM identifier ]
¯¯¯¯¯¯¯                ¯¯¯¯
File organizzati in modo da consentire un accesso diretto
REWRITE record-name  [ FROM identifier ]
¯¯¯¯¯¯¯                ¯¯¯¯
    [ INVALID KEY { imperative-statement }... ]
      ¯¯¯¯¯¯¯

Gli schemi sintattici mostrati hanno in comune la prima parte: il nome della variabile che fa riferimento al record, serve a individuare implicitamente il file a cui si fa riferimento; la variabile indicata dopo la parola FROM, permette di copiare tale variabile su quella del record, prima di procedere alla sovrascrittura, come se si usasse l'istruzione MOVE prima di REWRITE:

MOVE identifier TO record-name;
¯¯¯¯
REWRITE record-name
¯¯¯¯¯¯¯
    [ INVALID KEY { imperative-statement }... ]
      ¯¯¯¯¯¯¯

Quando si utilizza l'istruzione REWRITE con un file aperto in modo sequenziale, prima è necessario che sia stata eseguita una lettura del record che si vuole sovrascrivere; la lettura implica la selezione del record. Nel caso particolare di un accesso sequenziale a un file con indice, oltre che leggere preventivamente il record da sovrascrivere, occorre accertarsi che la riscrittura mantenga la stessa chiave, altrimenti la riscrittura non avviene e si attiva invece l'opzione INVALID KEY (con l'esecuzione delle istruzioni che questa controlla). Oltre a questo, se il file prevede l'esistenza di una chiave secondaria e non sono ammesse chiavi doppie, se il record da sovrascrivere contiene una chiave secondaria già esistente in un altro, si ottiene, anche in questo caso, l'attivazione dell'opzione INVALID KEY.

Quando l'istruzione REWRITE si applica a file aperti attraverso un accesso diretto, dinamico o con chiave, la sovrascrittura non richiede più di procedere prima a una lettura del record, perché è sufficiente indicarlo tramite il numero (RELATIVE KEY) oppure attraverso la chiave primaria. In tal caso, la condizione INVALID KEY si verifica quando il numero del record o la chiave primaria non corrispondono a nulla di già esistente nel file. Nel caso particolare dei file con indice, la condizione INVALID KEY si avvera anche quando, non essendo previste chiavi doppie, si tenta di modificare un record, immettendo però una chiave secondaria (non quella primaria) già esistente in un altro.

Listato 579.77. Programma elementare che legge un file sequenziale, ad accesso sequenziale, che quando incontra un record contenente lettere «A», lo sostituisce con lettere «Z».

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-READ-SEQ-REWRITE.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               TINYCOBOL 0.61,
000600               OPENCOBOL 0.31.
000700 DATE-WRITTEN. 2005-03-12.
000800*
000900 ENVIRONMENT DIVISION.
001000*
001100 INPUT-OUTPUT SECTION.
001200*
001300 FILE-CONTROL.
001400*
001500     SELECT FILE-DA-MODIFICARE ASSIGN TO "input.seq"
001600                               ORGANIZATION IS SEQUENTIAL.
001700*
001800 DATA DIVISION.
001900*
002000 FILE SECTION.
002100*
002200 FD  FILE-DA-MODIFICARE
002300     LABEL RECORD IS STANDARD.
002400*
002500 01  RECORD-DA-MODIFICARE PIC X(30).
002600*
002700 WORKING-STORAGE SECTION.
002800 01  EOF               PIC 9   VALUE ZERO.
002900*
003000 PROCEDURE DIVISION.
003100*------------------------- LIVELLO 0 -----------------------------
003200 MAIN.
003300     OPEN I-O FILE-DA-MODIFICARE.
003400     READ FILE-DA-MODIFICARE
003500          AT END
003600                MOVE 1 TO EOF.
003700     PERFORM LETTURA-RISCRITTURA UNTIL EOF = 1.
003800     CLOSE FILE-DA-MODIFICARE.
003900*
004000     STOP RUN.
004100*------------------------- LIVELLO 1 -----------------------------
004200 LETTURA-RISCRITTURA.
004300     IF RECORD-DA-MODIFICARE = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
004400     THEN
004500         MOVE "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"
004600              TO RECORD-DA-MODIFICARE,
004700         REWRITE RECORD-DA-MODIFICARE.
004800     READ FILE-DA-MODIFICARE
004900          AT END
005000                MOVE 1 TO EOF.
005100*

579.18   Istruzione «SEARCH»

L'istruzione SEARCH scandisce una tabella alla ricerca di un elemento che soddisfi una condizione, più o meno articolata, posizionando l'indice della tabella stessa in corrispondenza dell'elemento trovato. Sono disponibili due schemi sintattici: il primo serve per scandire le tabelle in modo sequenziale; il secondo serve per scandire delle tabelle ordinate, attraverso una ricerca binaria.

Ricerca sequenziale
                      .--         /                \ --.
                      |           |  identifier-2  |   |
SEARCH  identifier-1  |  VARYING  <                >   |
¯¯¯¯¯¯                |  ¯¯¯¯¯¯¯  |  index-name-1  |   |
                      `--         \                / --'
    [ AT END  { imperative-statement-1 }... ]
         ¯¯¯
    /                     /                                \  \
    |                     |  { imperative-statement-2 }... |  |
    <  WHEN  condition-1  <                                >  >...
    |  ¯¯¯¯               |  NEXT SENTENCE                 |  |
    \                     \  ¯¯¯¯ ¯¯¯¯¯¯¯¯                 /  /
Ricerca binaria per tabelle ordinate
SEARCH ALL  identifier-1  [ AT END  { imperative-statement-1 }... ]
¯¯¯¯¯¯ ¯¯¯                     ¯¯¯
                       /                                \
                       |  { imperative-statement-2 }... |
    WHEN  condition-1  <                                >
    ¯¯¯¯               |  NEXT SENTENCE                 |
                       \  ¯¯¯¯ ¯¯¯¯¯¯¯¯                 /

In entrambi i formati di utilizzo dell'istruzione SEARCH, la variabile indicata come identifier-1 deve essere stata dichiarata con l'opzione OCCURS e con l'opzione INDEXED BY (pertanto è obbligatorio che gli sia stato attribuito un indice in modo esplicito). Nel caso del secondo formato, che si utilizza per una ricerca binaria, è obbligatorio che la variabile indicata come identifier-1 sia stata dichiarata con l'opzione KEY IS, che sta a specificare il fatto che la tabella è ordinata in base a una certa chiave.

L'opzione AT END di entrambi gli schemi sintattici precede una o più istruzioni da eseguire nel caso la ricerca fallisca.

La parola chiave WHEN precede una condizione, che deve essere soddisfatta per lo scopo della ricerca, dopo la quale vengono eseguite le istruzioni successive (imperative-statement-2). Quando la scansione avviene in modo sequenziale, secondo il primo formato, la condizione può essere espressa in modo abbastanza libero, inoltre si possono indicare condizioni differenti e gruppi diversi di istruzioni da eseguire; quando invece la ricerca avviene in modo ordinato (ricerca binaria), ci può essere una sola condizione, che verifichi la corrispondenza della chiave con il valore cercato (se ci sono chiavi secondarie, si combinano le condizioni con l'operatore AND).

La condizione di una ricerca in una tabella ordinata (ricerca binaria) deve rispettare i limiti dello schema sintattico seguente, dove le metavariabili data-name sono le chiavi di ordinamento, che vanno indicate con gli indici necessari:

/               /               \  /  identifier-3        \  \
|               |  IS EQUAL TO  |  |                      |  |
|  data-name-1  <     ¯¯¯¯¯     >  <  literal-1           >  |
<               |  IS =         |  |                      |  >
|               \     ¯         /  \  arith-expression-1  /  |
|                                                            |
\  condition-name-1                                          /

    .--                                                           --.
    |      /             /             \ / identifier-4       \ \   |
    |      |             | IS EQUAL TO | |                    | |   |
    |      | data-name-2 <    ¯¯¯¯¯    > < literal-2          > |   |
    |  AND <             | IS =        | |                    | >   |...
    |  ¯¯¯ |             \    ¯        / \ arith-expression-2 / |   |
    |      |                                                    |   |
    |      \ condition-name-2                                   /   |
    `--                                                           --'

579.18.1   Ricerca sequenziale

La ricerca sequenziale con l'istruzione SEARCH, inizia dal valore che si trova già ad avere l'indice, proseguendo fino a soddisfare una delle condizioni, oppure fino alla fine degli elementi. Pertanto, se l'indice dovesse avere un valore maggiore del numero degli elementi della tabella, l'istruzione terminerebbe immediatamente.

L'istruzione SEARCH, usata per una ricerca sequenziale, esegue un ciclo di verifiche delle condizioni poste, quindi incrementa l'indice della tabella e ricomincia i confronti, fino a quando si avvera una delle condizioni, oppure quando la tabella non ha più elementi. Oltre a incrementare l'indice della tabella, può incrementare un altro indice, di un'altra tabella, o semplicemente una variabile numerica, attraverso l'uso dell'opzione VARYING.

Tradizionalmente, il funzionamento dell'istruzione SEARCH, quando si usa per una scansione sequenziale di una tabella, lo si descrive attraverso un diagramma di flusso, nel quale si immagina di utilizzare due condizioni controllate dalla parola WHEN, come si vede nella figura 579.81.

Figura 579.81. Esecuzione dell'istruzione SEARCH secondo il formato per la scansione sequenziale. Si mette in evidenza l'uso di due parole WHEN e si può comprendere come sarebbe con l'aggiunta di altre condizioni del genere.

diagramma di flusso

Viene mostrato l'esempio di un programma completo che inizia con l'inserimento di dati all'interno di una tabella, quindi esegue una ricerca sequenziale al suo interno:

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-SEARCH.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-12.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 01  RECORD-UTENTI.
001400     02  UTENTE           OCCURS 60 TIMES
001500                          INDEXED BY IND-UTENTE.
001600         03  COGNOME      PIC X(30).
001700         03  NOME         PIC X(30).
001800         03  NOTA         PIC X(200).
001900 77  EOJ                  PIC 9   VALUE ZERO.
002000 77  RISPOSTA             PIC XX.
002100 77  RICERCA              PIC X(30).
002200*
002300 PROCEDURE DIVISION.
002400*------------------------- LIVELLO 0 -----------------------------
002500 MAIN.
002600     PERFORM INSERIMENTO-DATI
002700             VARYING IND-UTENTE FROM 1 BY 1
002800             UNTIL EOJ = 1.
002900     MOVE 0 TO EOJ.
003000     PERFORM SCANSIONE UNTIL EOJ = 1.
003100*
003200     STOP RUN.
003300*------------------------- LIVELLO 1 -----------------------------
003400 INSERIMENTO-DATI.
003500     DISPLAY IND-UTENTE, " INSERISCI IL COGNOME: ".
003600     ACCEPT COGNOME (IND-UTENTE).
003700     DISPLAY IND-UTENTE, " INSERISCI IL NOME: ".
003800     ACCEPT NOME (IND-UTENTE).
003900     DISPLAY IND-UTENTE, " INSERISCI UNA NOTA DESCRITTIVA: ".
004000     ACCEPT NOTA (IND-UTENTE).
004100*
004200     IF IND-UTENTE >= 60
004300       THEN
004400           MOVE 1 TO EOJ;
004500       ELSE
004600           DISPLAY "VUOI CONTINUARE? SI O NO",
004700           ACCEPT RISPOSTA;
004800           IF RISPOSTA = "SI"
004900             THEN
005000                 NEXT SENTENCE;
005100             ELSE
005200                 MOVE 1 TO EOJ.
005300*-----------------------------------------------------------------
005400 SCANSIONE.
005500     DISPLAY "INSERISCI IL COGNOME DA CERCARE:".
005600     ACCEPT RICERCA.
005700     IF RICERCA = SPACES
005800       THEN
005900           MOVE 1 TO EOJ;
006000       ELSE
006100           SET IND-UTENTE TO 1,
006200           SEARCH UTENTE
006300                 AT END
006400                       DISPLAY "IL COGNOME CERCATO ",
006500                               "NON SI TROVA NELLA TABELLA: ",
006600                               QUOTE RICERCA QUOTE;
006700                 WHEN COGNOME (IND-UTENTE) = RICERCA
006800                     DISPLAY "IL COGNOME ", RICERCA,
006900                             "SI TROVA NELLA POSIZIONE ",
007000                             IND-UTENTE.
007100*

Nell'esempio sono evidenziate le righe in cui si dichiara la tabella e quelle che eseguono la scansione. Si deve osservare che prima dell'istruzione SEARCH, l'indice deve essere collocato manualmente nella posizione iniziale.

579.18.2   Ricerca in una tabella ordinata

La ricerca che si esegue con l'istruzione SEARCH ALL richiede che si rispettino alcune condizioni:

È importante considerare correttamente il problema dei dati validi: quando una tabella deve ricevere una quantità imprecisata di dati in elementi separati, questa deve essere stata dichiarata in modo abbastanza grande da poter contenere tutto, ma così facendo si ha la certezza di avere una serie di celle vuote alla fine della tabella stessa. Per evitare che la scansione di ricerca tenga conto anche delle celle vuote, si dichiara la tabella con una quantità «variabile» di celle (con l'opzione OCCURS m TO n TIMES, DEPENDING ON identifier). In realtà, più che trattarsi di una tabella che ha veramente una quantità variabile di celle, si fa in modo di stabilire qual è la dimensione massima, attraverso il controllo di una variabile apposita.

Dato il tipo di ricerca, non fa alcuna differenza il valore iniziale dell'indice della tabella.

L'esempio seguente mostra una variante del programma già descritto a proposito della ricerca sequenziale, modificato in modo da sfruttare una ricerca binaria. Si osservi che non è più necessario impostare il valore iniziale dell'indice, prima della scansione.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-SEARCH-KEY.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-12.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 01  RECORD-UTENTI.
001400     02  UTENTE           OCCURS 1 TO 60 TIMES
001500                          DEPENDING ON UTENTI-MAX
001600                          ASCENDING KEY IS COGNOME
001700                          INDEXED BY IND-UTENTE.
001800         03  COGNOME      PIC X(30).
001900         03  NOME         PIC X(30).
002000         03  NOTA         PIC X(200).
002100 77  UTENTI-MAX           USAGE IS INDEX.
002200 77  EOJ                  PIC 9   VALUE ZERO.
002300 77  RISPOSTA             PIC XX.
002400 77  RICERCA              PIC X(30).
002500*
002600 PROCEDURE DIVISION.
002700*------------------------- LIVELLO 0 -----------------------------
002800 MAIN.
002900     PERFORM INSERIMENTO-DATI
003000             VARYING IND-UTENTE FROM 1 BY 1
003100             UNTIL EOJ = 1.
003200     MOVE 0 TO EOJ.
003300     PERFORM SCANSIONE UNTIL EOJ = 1.
003400*
003500     STOP RUN.
003600*------------------------- LIVELLO 1 -----------------------------
003700 INSERIMENTO-DATI.
003800     MOVE IND-UTENTE TO UTENTI-MAX.
003900     DISPLAY IND-UTENTE, " INSERISCI IL COGNOME: ".
004000     ACCEPT COGNOME (IND-UTENTE).
004100     DISPLAY IND-UTENTE, " INSERISCI IL NOME: ".
004200     ACCEPT NOME (IND-UTENTE).
004300     DISPLAY IND-UTENTE, " INSERISCI UNA NOTA DESCRITTIVA: ".
004400     ACCEPT NOTA (IND-UTENTE).
004500*
004600     IF IND-UTENTE >= 60
004700       THEN
004800           MOVE 1 TO EOJ;
004900       ELSE
005000           DISPLAY "VUOI CONTINUARE? SI O NO",
005100           ACCEPT RISPOSTA;
005200           IF RISPOSTA = "SI"
005300             THEN
005400                 NEXT SENTENCE;
005500             ELSE
005600                 MOVE 1 TO EOJ.
005700*-----------------------------------------------------------------
005800 SCANSIONE.
005900     DISPLAY "INSERISCI IL COGNOME DA CERCARE:".
006000     ACCEPT RICERCA.
006100     IF RICERCA = SPACES
006200       THEN
006300           MOVE 1 TO EOJ;
006400       ELSE
006600           SEARCH ALL UTENTE
006700                 AT END
006800                       DISPLAY "IL COGNOME CERCATO ",
006900                               "NON SI TROVA NELLA TABELLA: ",
007000                               QUOTE RICERCA QUOTE;
007100                 WHEN COGNOME (IND-UTENTE) = RICERCA
007200                     DISPLAY "IL COGNOME ", RICERCA,
007300                             "SI TROVA NELLA POSIZIONE ",
007400                             IND-UTENTE.
007500*

579.19   Istruzione «SET»

L'istruzione SET permette di attribuire un valore all'indice di una tabella; valore inteso come la posizione all'interno della stessa. Sono disponibili due schemi sintattici: attraverso il primo si attribuisce una posizione determinata; con il secondo si incrementa o si decrementa l'indice di una certa quantità di posizioni.

     /              \         / index-name-2 \
     | index-name-1 |         |              |
SET  <              >...  TO  < identifier-2 >
¯¯¯  | identifier-1 |     ¯¯  |              |
     \              /         \ integer-1    /

Oppure:

                          /        \      /              \
                          |  UP    |      | identifier-3 |
SET  { index-name-3 }...  <  ¯¯    >  BY  <              >
¯¯¯                       |  DOWN  |  ¯¯  | integer-2    |
                          \  ¯¯¯¯  /      \              /

In entrambi gli schemi sintattici, la variabile o le variabili indicate subito dopo la parola chiave SET, sono quelle che rappresentano l'indice di una tabella e devono essere modificate. Nel primo caso, si intende assegnare loro il valore indicato o rappresentato dopo la parola chiave TO, mentre nel secondo caso, l'indice viene incrementato (UP) o diminuito (DOWN) del valore posto dopo la parola chiave BY.

Quando nell'istruzione si usa una costante numerica, o una variabile numerica normale, deve trattarsi di un valore intero, che può essere senza segno, oppure può avere un segno positivo, con l'eccezione del caso dell'incremento o decremento dell'indice (nel secondo schema), dove può avere senso anche un segno negativo.

Nel primo schema sintattico, non sono ammesse tutte le combinazioni, rispetto a quando sembrerebbe dallo schema stesso. Per prima cosa, il valore che si attribuisce all'indice, deve essere valido nell'ambito della tabella a cui si riferisce; inoltre, valgono gli abbinamenti dello schema successivo. Nello schema si distingue tra variabili intere normali, variabili di tipo indice associate a una tabella e variabili di tipo indice indipendenti.

Tabella 579.86. Combinazioni degli operandi nell'istruzione SET.

Variabile ricevente di tipo numerico intero
(integer data item)
Variabile ricevente di tipo indice associata a una tabella
(index name)
Variabile ricevente di tipo indice non associata ad alcuna tabella
(index data item)
Valore assegnato costituito da una costante numerica intera non ammesso assegnamento valido non ammesso
Valore assegnato costituito da una variabile numerica intera non ammesso assegnamento valido non ammesso
Valore assegnato costituito da una variabile di tipo indice associata a una tabella assegnamento valido assegnamento valido assegnamento valido
Valore assegnato costituito da una variabile di tipo indice non associata ad alcuna tabella non ammesso assegnamento valido assegnamento valido

A seconda delle caratteristiche del compilatore, l'assegnamento di un valore a un indice può richiedere l'esecuzione di una conversione numerica appropriata.

579.20   Istruzione «START»

L'istruzione START consente di posizionare il puntatore del record logico di un file relativo o a indice, per il quale sia stato previsto un accesso sequenziale o dinamico.

                  .--                                        --.
                  |        /  IS EQUAL TO        \             |
                  |        |     ¯¯¯¯¯           |             |
                  |        |  IS =               |             |
                  |        |     ¯               |             |
                  |        |  IS GREATER THAN    |             |
START  file-name  |  KEY   <     ¯¯¯¯¯¯¯         >  data-name  |
¯¯¯¯¯             |  ¯¯¯   |  IS >               |             |
                  |        |     ¯               |             |
                  |        |  IS NOT LESS THAN   |             |
                  |        |     ¯¯¯ ¯¯¯¯        |             |
                  |        \  IS NOT <           /             |
                  `--            ¯¯¯ ¯                       --'

        [ INVALID KEY { imperative-statement }... ]
          ¯¯¯¯¯¯¯

Il file indicato dopo la parola chiave START è quello all'interno del quale si vuole posizionare il puntatore del record logico. Come accennato, il file deve essere organizzato in modo relativo o a indice; inoltre, deve essere stato aperto in lettura (INPUT) o in lettura e scrittura (I-O).

La variabile che appare alla fine dello schema sintattico (data-name), può avere due significati differenti: se si tratta di un file organizzato in modo relativo, questa deve individuare la variabile definita con la dichiarazione RELATIVE KEY dell'istruzione SELECT del file stesso; se si tratta di un file organizzato a indice, deve trattarsi della chiave di ordinamento (dichiarata come RECORD KEY o ALTERNATE RECORD KEY nell'istruzione SELECT), tenendo conto che può trattarsi di una porzione inferiore della chiave stessa, purché questa porzione si trovi a partire dall'inizio (a sinistra) della chiave.

L'opzione INVALID KEY introduce una o più istruzioni che vengono eseguite nel caso l'istruzione START fallisca a causa dell'indicazione di una chiave che con combacia secondo il tipo di confronto richiesto.

Nello schema sintattico, la parola chiave KEY precede un gruppo di parole che servono a stabilire la condizione di ricerca. La corrispondenza con la chiave (costituita dal numero del record o dalla chiave di ordinamento vera e propria) può essere richiesta in modo esatto, oppure attraverso un altro tipo di relazione. Il record che per primo soddisfa la condizione di ricerca, è quello che viene selezionato. Una volta eseguita la selezione, il record potrebbe essere letto con l'istruzione READ.

Tabella 579.88. Condizione di ricerca.

Operatore Descrizione
KEY IS EQUAL TO data-name
¯¯¯    ¯¯¯¯¯
KEY IS = data-name
¯¯¯    ¯
la chiave, o la sua porzione, corrisponde esattamente
KEY IS GREATER THAN data-name
¯¯¯    ¯¯¯¯¯¯¯
KEY IS > data-name
¯¯¯    ¯
la chiave del record è superiore al valore specificato
KEY IS NOT LESS THEN data-name
¯¯¯    ¯¯¯ ¯¯¯¯
KEY IS NOT < THEN data-name
¯¯¯    ¯¯¯ ¯
la chiave del record non è inferiore (è maggiore o uguale) al valore specificato

La condizione di ricerca (assieme alla parola chiave KEY) e il nome della variabile che ha il ruolo di chiave, possono essere omessi. In tal caso, la ricerca avviene in base alla corrispondenza esatta con il valore che ha la variabile che costituisce la chiave relativa del file, oppure con quello che ha il campo della chiave primaria dello stesso.

Quando la chiave indicata nell'istruzione START corrisponde a una porzione iniziale della chiave primaria o secondaria del file, il confronto si basa solo su quella porzione di chiave, ignorando il resto; nello stesso modo, se la chiave indicata nell'istruzione è più grande della chiave primaria o di quella secondaria, il confronto si basa solo sulla dimensione della chiave che ha il file effettivamente (che risulta essere più breve).

Comunque sia l'esito della ricerca, l'esecuzione dell'istruzione START, provoca l'aggiornamento della variabile che rappresenta lo stato del file (FILE STATUS).

Listato 579.89. Programma elementare che legge un file relativo, ad accesso sequenziale, partendo dal terzo record.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-READ-SEQ-START.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 DATE-WRITTEN. 2005-03-13.
000500*
000600 ENVIRONMENT DIVISION.
000700*
000800 INPUT-OUTPUT SECTION.
000900*
001000 FILE-CONTROL.
001100*
001200     SELECT FILE-DA-LEGGERE ASSIGN TO "input.rel"
001300                            ORGANIZATION IS RELATIVE
001400                            RELATIVE KEY IS N-RECORD
001500                            ACCESS MODE IS SEQUENTIAL.
001600*
001700 DATA DIVISION.
001800*
001900 FILE SECTION.
002000*
002100 FD  FILE-DA-LEGGERE
002200     LABEL RECORD IS STANDARD.
002300*
002400 01  RECORD-DA-LEGGERE PIC X(30).
002500*
002600 WORKING-STORAGE SECTION.
002700 77  EOF               PIC 9   VALUE ZERO.
002800 77  N-RECORD          PIC 999 VALUE ZERO.
002900*
003000 PROCEDURE DIVISION.
003100*------------------------- LIVELLO 0 -----------------------------
003200 MAIN.
003300     OPEN INPUT FILE-DA-LEGGERE.
003400     MOVE 3 TO N-RECORD.
003500     START FILE-DA-LEGGERE KEY IS EQUAL TO N-RECORD
003600           INVALID KEY
003700                      MOVE 1 TO EOF.
003800     READ FILE-DA-LEGGERE
003900          AT END
004000                MOVE 1 TO EOF.
004100     PERFORM LETTURA UNTIL EOF = 1.
004200     CLOSE FILE-DA-LEGGERE.
004300*
004400     STOP RUN.
004500*------------------------- LIVELLO 1 -----------------------------
004600 LETTURA.
004700     DISPLAY RECORD-DA-LEGGERE.
004800     READ FILE-DA-LEGGERE
004900          AT END
005000                MOVE 1 TO EOF.
005100*

579.21   Istruzione «STOP RUN»

L'istruzione STOP RUN conclude il funzionamento del programma; pertanto, può trovarsi soltanto alla fine di un gruppo di istruzioni.

STOP RUN.
¯¯¯¯ ¯¯¯

Storicamente esiste una versione alternativa, ma obsoleta, dell'istruzione STOP, alla quale si associa una costante, allo scopo di mostrare tale valore attraverso il terminale principale. In quella situazione, l'esecuzione del programma veniva sospesa e poteva essere fatta riprendere dall'utente.

Considerato che esiste la possibilità di usare istruzioni come DISPLAY e ACCEPT, è meglio utilizzare esclusivamente l'istruzione STOP RUN per l'arresto del programma, senza altre varianti.

579.22   Istruzione «STRING»

L'istruzione STRING consente di riempire delle variabili alfanumeriche specificando un punto di inizio, espresso in caratteri.

        /  /                \                  /  identifier-2  \  \
        |  |  identifier-1  |                  |                |  |
STRING  <  <                >... DELIMITED BY  <  literal-2     >  >...
¯¯¯¯¯¯  |  |  literal-1     |    ¯¯¯¯¯¯¯¯¯     |                |  |
        \  \                /                  \  SIZE          /  /
                                                  ¯¯¯¯
    INTO  identifier-3
    ¯¯¯¯
    [ WITH POINTER  identifier-4 ]
           ¯¯¯¯¯¯¯
    [ ON OVERFLOW  { imperative-statement-1 }... ]
         ¯¯¯¯¯¯¯¯

Quello che si mette dopo la parola chiave STRING è un elenco di valori che si traducono in informazioni alfanumeriche, che vengono considerati come se fossero concatenati tra di loro. Dopo la parola DELIMITED si deve specificare un modo per delimitare la stringa complessiva indicata a sinistra. Se si usa la parola chiave SIZE, si intende considerare tutta la stringa alfanumerica complessiva, altrimenti, si seleziona solo la parte che si trova a sinistra, prima di ciò che viene indicato come riferimento.

La stringa complessiva, eventualmente ridotta a destra in qualche modo, viene copiata all'interno della variabile indicata dopo la parola INTO. La stringa viene copiata a partire dalla prima posizione, oppure dalla posizione specificata dal numero indicato dopo la parola POINTER. Dopo la parola POINTER va indicata una variabile numerica, che, oltre a indicare la posizione iniziale dell'inserimento della stringa, viene incrementata di conseguenza, per i caratteri che vengono inseriti effettivamente.

Se si utilizza la parola OVERFLOW, le istruzioni che appaiono subito dopo tale parola vengono eseguite se l'inserimento nella variabile di destinazione va oltre la fine della variabile stessa.

L'esempio successivo mostra un piccolo programma completo che compila in più fasi una variabile ricevente. La variabile ricevente contiene inizialmente una serie di simboli #, per consentire di vedere facilmente cosa succede al suo interno, durante le varie fasi.

Listato 579.92. Programma elementare che dimostra il funzionamento di STRING.

000100 IDENTIFICATION DIVISION.
000200 PROGRAM-ID.   TEST-STRING.
000300 AUTHOR.       DANIELE GIACOMINI.
000400 INSTALLATION. NANOLINUX IV,
000500               OPENCOBOL 0.31,
000600 DATE-WRITTEN. 2005-03-16.
000700*
000800 ENVIRONMENT DIVISION.
000900*
001000 DATA DIVISION.
001100*
001200 WORKING-STORAGE SECTION.
001300 77  TESTO-RICEVENTE      PIC X(40) VALUE ALL "#".
001400 77  PUNTATORE            PIC 99.
001500*
001600 PROCEDURE DIVISION.
001700*------------------------- LIVELLO 0 -----------------------------
001800 MAIN.
001900     MOVE 1 TO PUNTATORE.
002000     DISPLAY PUNTATORE, " ", TESTO-RICEVENTE.
002100     STRING "CIAO", SPACE, DELIMITED BY SIZE
002200            INTO TESTO-RICEVENTE WITH POINTER PUNTATORE.
002300     DISPLAY PUNTATORE, " ", TESTO-RICEVENTE.
002400     STRING "COME STAI?" DELIMITED BY SIZE
002500            INTO TESTO-RICEVENTE WITH POINTER PUNTATORE.
002600     DISPLAY PUNTATORE, " ", TESTO-RICEVENTE.
002700     MOVE 11 TO PUNTATORE.
002800     STRING "VA LA VITA?" DELIMITED BY SIZE
002900            INTO TESTO-RICEVENTE WITH POINTER PUNTATORE.
003000     DISPLAY PUNTATORE, " ", TESTO-RICEVENTE.
003100*
003200     STOP RUN.
003300*

Dopo aver compilato il programma, eseguendo ciò che si ottiene, di dovrebbe vedere il risultato seguente attraverso il terminale:

01 ########################################
06 CIAO ###################################
16 CIAO COME STAI?#########################
22 CIAO COME VA LA VITA?###################

Come si può vedere leggendo il sorgente del programma, dopo l'inserimento della stringa CIAO , la variabile usata come puntatore all'interno della variabile di destinazione, si trova a essere già posizionata sulla sesta colonna, in modo che un inserimento ulteriore si trovi già nella posizione necessaria. Dopo, viene riposizionato il puntatore per sovrascrivere la parola «STAI».

579.23   Istruzione «SUBTRACT»

L'istruzione SUBTRACT consente di eseguire delle sottrazioni. Sono previsti diversi formati per l'utilizzo di questa istruzione.

          /              \  .--            --.
          | identifier-1 |  |  identifier-2  |
SUBTRACT  <              >  |                |...
¯¯¯¯¯¯¯¯  | literal-1    |  |  literal-2     |
          \              /  `--            --'

    FROM { identifier-m  [ROUNDED] }...
    ¯¯¯¯                  ¯¯¯¯¯¯¯
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Nello schema sintattico appena mostrato, si vede che dopo la parola chiave SUBTRACT si elencano una serie di costanti o variabili con valore numerico, che vengono sommate assieme inizialmente, per poi sottrarre tale valore dal contenuto delle variabili specificate dopo la parola chiave FROM. L'opzione ROUNDED richiede di eseguire un arrotondamento se la variabile ricevente non può rappresentare in modo esatto il valore; l'opzione SIZE ERROR serve a eseguire un'istruzione nel caso una delle variabili riceventi non possa accogliere la porzione più significativa del valore ottenuto dalla somma. Si osservi l'esempio seguente:

000000     SUBTRACT 1, 2, 3, FROM A.

Supponendo che la variabile A, prima della somma contenga il valore 10, dopo la somma contiene il valore 4 (10-1-2-3).

         /              \  .-- /              \ --.
         | identifier-1 |  |   | identifier-2 |   |
SUBTRACT <              >  |   <              >   |...
¯¯¯¯¯¯¯¯ | literal-1    |  |   | literal-2    |   |
         \              /  `-- \              / --'

         /              \
         | identifier-3 |
    FROM <              >  [ROUNDED] ...
    ¯¯¯¯ | identifier-3 |   ¯¯¯¯¯¯¯
         \              /

    GIVING { identifier-n  [ROUNDED] }...
    ¯¯¯¯¯¯                  ¯¯¯¯¯¯¯
    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

Quando si utilizza la parola chiave GIVING, si può indicare un solo valore dopo la parola chiave FROM e il risultato della sottrazione viene assegnato alle variabili che sono elencate dopo la parola GIVING, senza tenere in considerazione il loro valore iniziale. Valgono le stesse considerazioni già fatte a proposito delle opzioni ROUNDED e SIZE ERROR. Si osservi l'esempio seguente:

000000     SUBTRACT 1, 2, 3, FROM 10 GIVING A.

Qualunque sia il valore iniziale della variabile A, dopo la somma questa contiene il valore 4 (10-1-2-3).

          /               \
          | CORR          |
SUBTRACT  < ¯¯¯¯          >  identifier-1  FROM  identifier-2  [ROUNDED]
¯¯¯¯¯¯¯¯  | CORRESPONDING |                ¯¯¯¯                 ¯¯¯¯¯¯¯
          \ ¯¯¯¯¯¯¯¯¯¯¯¯¯ /

    [ ON SIZE ERROR  imperative-statement ]
         ¯¯¯¯ ¯¯¯¯¯

In questo ultimo caso, la sottrazione fa riferimento a variabili strutturate, dove i campi della prima variabile devono essere sottratti ai campi della seconda variabile che hanno lo stesso nome della prima. Valgono le stesse considerazioni già fatte a proposito delle opzioni ROUNDED e SIZE ERROR.

579.24   Istruzione «WRITE»

L'istruzione WRITE scrive un record logico in un file, aperto in modo appropriato. Nel caso di un file organizzato in modo sequenziale, il file può essere aperto in scrittura (OUTPUT) o in estensione (EXTEND); nel caso di un file ad accesso diretto, organizzato in modo relativo o a indice, questo può essere stato aperto in scrittura (OUTPUT) o in lettura e scrittura (I-O), inoltre, se si usa l'accesso sequenziale, è consentito anche in caso di apertura in estensione (EXTEND).

L'istruzione WRITE viene usata con due schemi sintattici alternativi: uno per i file organizzati in modo sequenziale e l'altro per tutti gli altri casi. Il formato adatto ai file sequenziali contiene, in particolare, opzioni specifiche per l'avanzamento della carta di una stampante.

File organizzati in modo sequenziale
WRITE  record-name  [ FROM identifier-1 ]
¯¯¯¯¯                 ¯¯¯¯
     .--                                                            --.
     |                         /  /              \  .--     --.  \    |
     |                         |  | identifier-2 |  |  LINE   |  |    |
     |                         |  <              >  |         |  |    |
     |  /        \             |  | integer-1    |  |  LINES  |  |    |
     |  | AFTER  |             |  \              /  `--     --'  |    |
     |  < ¯¯¯¯¯  >  ADVANCING  <                                 >    |
     |  | BEFORE |             |  /               \              |    |
     |  \ ¯¯¯¯¯¯ /             |  | mnemonic-name |              |    |
     |                         |  <               >              |    |
     |                         |  | PAGE          |              |    |
     |                         \  \ ¯¯¯¯          /              /    |
     `--                                                            --'
File organizzati in modo relativo e a indice
WRITE  record-name  [ FROM identifier-1 ]
¯¯¯¯¯                 ¯¯¯¯
    [ INVALID KEY { imperative-statement }... ]
      ¯¯¯¯¯¯¯

Gli schemi sintattici mostrati hanno in comune la prima parte: il nome della variabile che fa riferimento al record, serve a individuare implicitamente il file; la variabile indicata dopo la parola opzionale FROM, permette di copiare tale variabile su quella del record, prima di procedere alla scrittura, come se si usasse l'istruzione MOVE prima di WRITE:

MOVE identifier-1 TO record-name;
¯¯¯¯
WRITE record-name
¯¯¯¯¯
    [ omissis ]

Quando la scrittura avviene con successo, il contenuto del record non è più disponibile in memoria, a meno di averne una copia per altri motivi (per esempio a causa dell'utilizzo dell'opzione FROM).

La scrittura di un file organizzato in modo sequenziale implica l'utilizzo del primo schema sintattico. Nello schema sintattico non è previsto il controllo di alcuna condizione di errore, che comunque potrebbe verificarsi, quando per qualche ragione non è possibile scrivere nel file. Le opzioni AFTER ADVANCING e BEFORE ADVANCING, servono rispettivamente per richiedere un avanzamento preventivo o successivo alla scrittura. Per un file di dati, non ha significato l'uso di tali opzioni, che invece servono precisamente per la stampa, o per la creazione di file di testo (destinati eventualmente alla stampa). L'avanzamento può essere specificato in un numero intero di righe (identifier-2 o integer-1), oppure richiedendo un salto pagina, con la parola chiave PAGE. Il nome mnemonico che può essere indicato in alternativa alla parola chiave PAGE può servire per attribuire un nome alternativo proprio alla parola PAGE, oppure a fare riferimento a un'altra parola chiave (alternativa a PAGE), che si riferisce a caratteristiche speciali, legate alla stampa, che il proprio compilatore può gestire.

Si osservi che un file organizzato in modo sequenziale, per il quale abbiano senso le opzioni di avanzamento, è bene che sia stato dichiarato con l'opzione LINE SEQUENTIAL.

Il secondo schema sintattico può essere usato per i file che non hanno un'organizzazione sequenziale. In questo caso vengono a mancare i controlli di avanzamento della riga o della pagina, ma si aggiunge la verifica di un errore di scrittura, attraverso l'opzione INVALID KEY, dopo la quale appaiono le istruzioni da eseguire in caso di problemi.

Nel caso di file organizzati in modo relativo, ad accesso sequenziale, il comportamento è lo stesso che si avrebbe con un file sequenziale puro e semplice, con la differenza che la variabile designata a contenere il numero del record viene impostata automaticamente e che si può verificare la condizione controllata dall'opzione INVALID KEY se si tenta di espandere il file oltre i limiti imposti esternamente al programma. Se invece questo tipo di file viene usato con un accesso diretto o dinamico, il numero del record (inserito nella variabile definita con la dichiarazione RELATIVE KEY dell'istruzione SELECT del file stesso) deve essere indicato espressamente: se il numero indicato corrisponde a un record già esistente, oppure se si tenta di scrivere oltre i limiti stabiliti esternamente al programma, si ottiene la condizione di errore controllata dall'opzione INVALID KEY.

Nel caso di file organizzati a indice, l'inserimento dei record avviene tenendo conto delle chiavi previste per questo. In linea di principio, le chiavi non devono essere doppie; pertanto, il tentativo di inserire un record che contiene una chiave già esistente nel file (primaria o secondaria che sia), provoca un errore che può essere controllato attraverso l'opzione INVALID KEY. Naturalmente, se nella dichiarazione delle chiavi è stato stabilito che possono anche essere doppie, tale errore non si verifica e la scrittura avviene con successo.

Un file organizzato a indice può essere scritto utilizzando un accesso sequenziale, ma in tal caso, la scrittura deve avvenire rispettando l'ordine crescente della chiave primaria, altrimenti si verifica un errore che si può controllare con l'opzione INVALID KEY.

L'utilizzo dell'istruzione WRITE implica l'aggiornamento della variabile che rappresenta lo stato del file (FILE STATUS).

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome istruzioni_della_divisione_171_procedure_division_187.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!