[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [translators] [docinfo] [indice analitico] [volume] [parte]
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.
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 464.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).
|
L'esempio seguente dimostra il funzionamento e l'utilizzo di queste parole chiave standard:
|
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 |
|
L'esempio successivo dimostra l'uso di un nome mnemonico per dichiarare l'origine dei dati. Sono evidenziate le righe più significative:
|
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:
|
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:
|
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.
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.
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.
|
L'esempio mostra che si vuole assegnare alla variabile D il risultato dell'espressione A * B + C (A moltiplicato B, sommato a C).
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.
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 464.3).
|
L'esempio successivo mostra un uso abbastanza comune dell'istruzione DISPLAY:
|
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.
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:
|
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:
|
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:
|
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:
|
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 |
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.
|
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.
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.xahlee.org/UnixResource_dir/_/1968_dijkstra_goto_harm.html>, <http://www.cs.utsa.edu/~wagner/CS3723/nogoto/harm2.html> e altri indirizzi. |
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:
|
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):
|
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.
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 | | | | \ \ \ ¯¯¯¯¯¯¯ / \ / `-- \ ¯¯¯¯¯ / \ / --' / / |
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 / \ / \ / `-- \ ¯¯¯¯¯ / \ / --' / ¯¯¯¯¯ |
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.
|
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»:
|
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.
|
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».
|
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.
|
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.
|
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 |
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).
|
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. |
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:
|
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:
|
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:
|
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.
|
|
|
|
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.
.-- / \ --. | | 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.
|
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.
.-- / \ --. | | 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.
.-- / \ --. | | 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.
|
.-- / \ --. | | 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.
|
|
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.
|
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 |
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.
READ file-name [NEXT] RECORD [ INTO identifier ] ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ [ AT END { imperative-statement }... ] ¯¯¯ |
READ file-name RECORD [ INTO identifier ] ¯¯¯¯ ¯¯¯¯ [ INVALID KEY { imperative-statement }... ] ¯¯¯¯¯¯¯ |
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.
|
|
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.
REWRITE record-name [ FROM identifier ] ¯¯¯¯¯¯¯ ¯¯¯¯ |
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.
|
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.
.-- / \ --. | | identifier-2 | | SEARCH identifier-1 | VARYING < > | ¯¯¯¯¯¯ | ¯¯¯¯¯¯¯ | index-name-1 | | `-- \ / --' [ AT END { imperative-statement-1 }... ] ¯¯¯ / / \ \ | | { imperative-statement-2 }... | | < WHEN condition-1 < > >... | ¯¯¯¯ | NEXT SENTENCE | | \ \ ¯¯¯¯ ¯¯¯¯¯¯¯¯ / / |
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 / | `-- --' |
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 472.81.
|
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:
|
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.
La ricerca che si esegue con l'istruzione SEARCH ALL richiede che si rispettino alcune condizioni:
i dati contenuti nella tabella devono risultare ordinati come previsto dalle chiavi già dichiarate;
i dati contenuti nella tabella devono risultare tutti validi;
le chiavi a cui si fa riferimento nella condizione di ricerca devono essere sufficienti a raggiungere l'informazione in modo univoco.
È 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.
|
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.
|
A seconda delle caratteristiche del compilatore, l'assegnamento di un valore a un indice può richiedere l'esecuzione di una conversione numerica appropriata.
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.
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).
|
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.
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.
|
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».
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:
|
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:
|
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.
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.
WRITE record-name [ FROM identifier-1 ] ¯¯¯¯¯ ¯¯¯¯ .-- --. | / / \ .-- --. \ | | | | identifier-2 | | LINE | | | | | < > | | | | | / \ | | integer-1 | | LINES | | | | | AFTER | | \ / `-- --' | | | < ¯¯¯¯¯ > ADVANCING < > | | | BEFORE | | / \ | | | \ ¯¯¯¯¯¯ / | | mnemonic-name | | | | | < > | | | | | PAGE | | | | \ \ ¯¯¯¯ / / | `-- --' |
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 2006.07.01 --- Copyright © 2000-2006 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] [translators] [docinfo] [indice analitico]