72.6 Descrizione delle variabili
72.10 Note sull'utilizzo dell'insieme di caratteri universale con il COBOL
72.12 Istruzioni della divisione «PROCEDURE DIVISION»
01
72.5.1.9
66
72.8.2
88
72.8.1
ACCEPT
72.12.1
ADD
72.12.2
BLANK WHEN ZERO
72.6.9
BLOCK CONTAINS
72.5.1.3
CLOSE
72.12.3
CODE-SET
72.5.1.7
COMPUTE
72.12.4
CONFIGURATION SECTION
72.4.2
DATA DIVISION
72.5
DATA RECORD
72.5.1.4
DECLARATIVES
72.11.1
DELETE
72.12.5
DEPENDING ON
72.7.4
DISPLAY
72.12.6
DIVIDE
72.12.7
ENVIRONMENT DIVISION
72.4
EXIT
72.12.8
FD
72.5.1.1
FILE SECTION
72.5.1
FILE-CONTROL
72.4.3.1
FILLER
72.6.1
GO TO
72.12.9
I-O-CONTROL
72.4.3.7
IDENTIFICATION DIVISION
72.3
IF
72.12.10
INPUT-OUTPUT SECTION
72.4.3
INSPECT
72.12.11
JUSTIFIED RIGHT
72.6.8
LABEL RECORD
72.5.1.5
MERGE
72.13.2
MOVE
72.12.12
MULTIPLY
72.12.13
OBJECT-COMPUTER
72.4.2.2
OCCURS
72.6.6 72.7
OPEN
72.12.14
PERFORM
72.12.15
PICTURE
72.9
PROCEDURE DIVISION
72.11 72.12 72.13
READ
72.12.16
RECORD CONTAINS
72.5.1.6
REDEFINES
72.5.1.9 72.6.2
RELEASE
72.13.5
RENAMES
72.8.2
RETURN
72.13.4
REWRITE
72.12.17
SD
72.5.1.2
SEARCH
72.12.18
SELECT
72.4.3.3 72.4.3.4 72.4.3.5
SET
72.12.19
SIGN IS
72.6.5
SORT
72.4.3.6 72.13.1
SOURCE-COMPUTER
72.4.2.1
SPECIAL-NAMES
72.4.2.3
START
72.12.20
STOP RUN
72.12.21
STRING
72.12.22
SUBTRACT
72.12.23
SYNCHRONIZED
72.6.7
USAGE
72.6.4
VALUE
72.6.10
VALUE OF
72.5.1.8
WORKING-STORAGE SECTION
72.5.2
WRITE
72.12.24
Ogni manuale COBOL tradizionale riporta una premessa che cita le origini del linguaggio e le fonti a cui si fa riferimento. Questo tipo di premessa ha soprattutto un valore storico e con tale spirito viene inserita qui.
Il testo seguente è una traduzione tratta dalla pubblicazione COBOL, edita dalla Conferenza sui linguaggi dei sistemi di elaborazione dati, CODASYL (Conference on data system languages), stampata a cura dell'ufficio stampa del governo degli Stati Uniti d'America.
«Questa pubblicazione si basa sul sistema COBOL sviluppato nel 1959 da un comitato composto da utenti governativi e costruttori di elaboratori. Le organizzazioni che hanno preso parte ai lavori iniziali sono state:
Air Material Command, U.S. Air Force;
Bureau of Standards, U.S. Department of Commerce;
Burroughs Corporation;
David Tylor Model Basin, Bureau of Ships, U.S. Navy;
Electronic Data processing Division, Minneapolis-Honeywell Regulator Company;
International Business Machines Corporation;
Radio Corporation of America;
Sylvania Electric Products, Inc.;
UNIVAC Division of Sperry Rand Corporation.Oltre alle suddette organizzazioni, le seguenti altre partecipano ai lavori del Gruppo di revisione:
Allstate Insurance Company;
The Bendix Corporation, Computer Division;
Control Data Corporation;
E.I. du Pont de Nemours and Company;
General Electric Company;
General Motors Corporation;
The National Cash Register Company;
Philco Corporation;
Standard Oil Company (New Jersey);
United States Steel Corporation.Questo manuale COBOL è risultato dalla collaborazione fra tutte le organizzazioni citate.
Nessuna garanzia, espressa o tacita, è fornita dal comitato o dai singoli collaboratori, circa l'esattezza e il buon funzionamento del sistema di programmazione e del linguaggio. Inoltre, sia il comitato, sia i suoi collaboratori, non si assumono alcuna responsabilità in ordine a quanto esposto.
È ragionevole attendersi che molti perfezionamenti e aggiunte vengano fatte al COBOL. Si farà ogni sforzo per assicurare che miglioramenti e correzioni siano apportate con criteri di continuità, tenendo debito conto degli investimenti effettuati dagli utenti nel settore della programmazione. Tuttavia, tali garanzie potranno essere efficacemente mantenute soltanto da coloro che apporteranno perfezionamento o correzioni.
Sono state predisposte apposite procedure per l'aggiornamento del COBOL. Le richieste di informazioni circa tali procedure e sulle modalità per proporre modifiche dovranno essere inoltrate al comitato esecutivo della Conferenza sui linguaggi dei sistemi di elaborazione dati.
Gli autori e i titolari dei diritti di autore e di riproduzione del materiale così protetto, usato nel presente manuale: FLOW-MATIC (marchio depositato dalla Sperry Rand Corporation) - Sistema di programmazione per i calcolatori UNIVAC® I e II, Data Automation Systems © 1958, 1959 Sperry Rand Corporation; IBM Commercial Traslator, Codice F28-8013, © 1959 IBM; FACT, DSI 27A5260-2760 © 1960 della Minneapolis-Honeywell; hanno esplicitamente autorizzato l'uso di tale materiale, in tutto o in parte, nelle specifiche del COBOL. Tale autorizzazione si estende alla riproduzione e all'uso delle specifiche COBOL in manuali di programmazione o in pubblicazioni analoghe.
Qualsiasi organizzazione che intenda riprodurre il rapporto COBOL e le specifiche iniziali in tutto o in parte, usando idee ricavate da tale rapporto o utilizzando il rapporto stesso come elemento base per un manuale di istruzione o per qualsiasi altro scopo, è libera di farlo. Tuttavia, si richiede a tutte queste organizzazioni di riprodurre la presente sezione, come parte dell'introduzione. Coloro che invece utilizzano brevi citazioni, come nelle rassegne dei nuovi libri, sono pregati di citare la fonte ma non di riprodurre l'intera sezione.»
Successivamente alla Conferenza sui linguaggi dei sistemi di elaborazione dati, CODASYL, il compito di definire lo standard del linguaggio COBOL è stato preso dall'istituto ANSI (American national standards institute), che chiede nuovamente di citare la fonte nei manuali di tale linguaggio. Il testo seguente è citato in lingua originale.
«Any organization interested in reproducing the COBOL standard and specifications in whole or in part, using ideas from this document as the basis for an instruction manual or for any other purpose, is free to do so. However, all such organizations are requested to reproduce the following acknowledgement paragraphs in their entirety as part of the preface to any such publication (any organization using a short passage from this document, such as in a book review, is requested to mention "COBOL" in acknowledgement of the source, but need not quote the acknowledgement):
COBOL is an industry language and is not the property of any company or group of companies, or of any organization or group of organizations.
No warranty, expressed or implied, is made by any contributor or by the CODASYL Programming Language Committee as to the accuracy and functioning of the programming system and language. Moreover, no responsibility is assumed by any contributor, or by the committee, in connection therewith.
The authors and copyright holders of the copyrighted material used herein have specifically authorized the use of this material in whole or in part, in the COBOL specifications. Such authorization extends to the reproduction and use of COBOL specifications in programming manuals or similar publications.
FLOW-MATIC (trademark of Sperry Rand Corporation), Programming for the Univac++ I and II, Data Automation Systems copyrighted 1958, 1959, by Sperry Rand Corporation; IBM Commercial Translator Form No. F 28-8013, copyrighted 1959 by IBM, FACT, DSI 27A5260-2760, copyrighted 1960 by Minneapolis-Honeywell.»
Il linguaggio COBOL nasce nel 1959, come linguaggio standard per l'amministrazione degli uffici e il nome sta per Common business oriented language, descrivendo precisamente il suo obiettivo.
L'origine così lontana del linguaggio COBOL è responsabile della prolissità della sua sintassi e dei vincoli di forma nella struttura che il programma sorgente deve avere. Tuttavia, questo linguaggio è eccezionale nella gestione dei dati, avvicinandosi alle funzionalità di un sistema di gestione di basi di dati (ovvero un DBMS).
Il linguaggio COBOL è nato da un comitato di utenti e di produttori di elaboratori, con lo scopo di rimanere uniforme, il più possibile, su tutte le piattaforme. Generalmente si considera, correttamente, che il C rappresenti l'esempio di linguaggio di programmazione standard per definizione, ma i contesti sono differenti: il linguaggio C serve a consentire la migrazione di un sistema operativo da una macchina all'altra, mentre il linguaggio COBOL è fatto per consentire la migrazione di programmi applicativi su architetture fisiche e sistemi operativi differenti.
Il linguaggio COBOL è fatto per poter funzionare su sistemi operativi che possono anche essere privi di qualunque astrazione dell'hardware; pertanto, una porzione apposita nella struttura del sorgente è riservata alla dichiarazione delle unità fisiche per lo scambio dei dati (la divisione ENVIRONMENT DIVISION). Utilizzando il COBOL in un ambiente abbastanza evoluto, quanto può esserlo un sistema Unix, molte informazioni diventano inutili e implicite, ma il fatto che con questo linguaggio ci sia la possibilità di operare con la maggior parte degli elaboratori fabbricati dal 1959 in poi, lo rende speciale e degno di apprezzamento per lungo tempo.
Il linguaggio COBOL ha subito nel tempo diverse revisioni, indicate generalmente attraverso l'anno di edizione; un punto di riferimento abbastanza comune è lo standard del 1985, a cui questo manuale, in parte, cerca di adeguarsi. Per quanto riguarda l'ente ISO, è disponibile lo standard ISO 1989.
Il linguaggio COBOL si basa convenzionalmente sulla lingua inglese ed è composto sommariamente da parole, istruzioni, gruppi di istruzioni, paragrafi e sezioni.
Ogni programma COBOL deve contenere quattro divisioni, anche se queste dovessero essere vuote, rispettando l'ordine seguente:
IDENTIFICATION DIVISION
ENVIRONMENT DIVISION
DATA DIVISION
PROCEDURE DIVISION
La divisione IDENTIFICATION DIVISION serve a identificare il programma. Vi si possono includere informazioni generali, come il nome del programma stesso, la data di edizione, la data di compilazione, il nome dell'elaboratore per il quale è stato scritto e altre annotazioni.
La divisione ENVIRONMENT DIVISION specifica le apparecchiature usata e i file che servono al programma.
La divisione DATA DIVISION contiene la descrizione dei file e dei record relativi, creati o utilizzati dal programma, assieme a tutte le altre variabili e costanti che servono al programma.
La divisione PROCEDURE DIVISION specifica il procedimento elaborativo da applicare ai dati.
Le «azioni» descritte nel programma COBOL sono espresse in termini di istruzioni, che possono essere riunite in gruppi di istruzioni e poi in paragrafi.
I compilatori tradizionali del linguaggio COBOL adottano, a seconda dei casi, il codice ASCII o il codice EBCDIC per la rappresentazione interna dei caratteri; inoltre, in un programma sorgente si può usare soltanto un insieme ristretto di simboli, con l'eccezione del contenuto delle costanti alfanumeriche, che invece è abbastanza libero.
|
Si osservi che il segno di valuta, rappresentato normalmente dal dollaro, può essere ridefinito e rappresentato da un altro simbolo.
|
|
|
|
Si osservi che, al contrario di tanti altri linguaggi, nati però in momenti successivi, il COBOL non prevede l'uso del trattino basso (_).
Il testo di un programma sorgente COBOL è costruito con stringhe di caratteri e separatori, secondo le regole descritte nelle sezioni successive.
Un separatore è una stringa composta da uno o più caratteri di interpunzione, rispettando le regole seguenti. Si osservi che queste regole non si applicano al contenuto delle costanti non numeriche (le stringhe letterali) e naturalmente non si applicano ai commenti.
La virgola e il punto e virgola sono separatori, tranne quando appaiono nel modello di definizione di una variabile (PICTURE), dove invece sono trattati come parte del modello stesso. La virgola e il punto e virgola, se usati come separatori, possono essere impiegati al posto dello spazio.
Un punto fermo, seguito da uno spazio, è un separatore. Il punto fermo può apparire soltanto dove ciò è permesso esplicitamente dalle regole grammaticali del linguaggio.
Le parentesi tonde, usate in coppia, aperta e chiusa, sono separatori. Possono essere usate per delimitare indici, espressioni aritmetiche e condizioni.
Le virgolette sono separatori. Le virgolette di apertura devono essere precedute da uno spazio o da una parentesi aperta; le virgolette di chiusura devono essere seguite, alternativamente da: uno spazio, una virgola, un punto e virgola, un punto fermo oppure una parentesi chiusa.
Le virgolette possono apparire solo in coppia, per delimitare costanti alfanumeriche, tranne quando le costanti continuano nella riga successiva.
Lo spazio usato come separatore può precedere o seguire tutti gli altri separatori, tranne nei casi previsti dalle altre regole grammaticali del linguaggio. Uno spazio compreso tra una coppia di virgolette è una costante alfanumerica e non costituisce un separatore.
I caratteri di interpunzione che appaiono all'interno di un modello di definizione di una variabile (PICTURE) o di una costante numerica, non sono considerati caratteri di interpunzione, piuttosto sono simboli usati per caratterizzare il modello relativo o la costante (le regole per la dichiarazione di un modello di definizione di una variabile sono descritte nella sezione 72.9).
I modelli di definizione delle variabili sono delimitati solo dallo spazio, dalla virgola, dal punto e virgola o dal punto fermo. |
Nei modelli sintattici, una stringa di caratteri (character-string) può essere: un carattere o una sequenza di caratteri contigui, che forma una parola per il linguaggio COBOL; il modello di definizione di una variabili (PICTURE); un commento. Una stringa di caratteri di questi contesti è delimitata da separatori.
Una «parola» per il linguaggio COBOL è una stringa composta al massimo da 30 caratteri, che può essere:
una parola definita dall'utente, ovvero user-defined word;
un nome di sistema, ovvero system-name;
una parola riservata, ovvero reserved word.
Le parole riservate o di sistema non possono essere utilizzate per fini diversi, pertanto non possono essere ridefinite dall'utente.
Una parola definita dall'utente è una parola COBOL che deve essere fornita per soddisfare la sintassi di un'istruzione. Tale parola può essere composta utilizzando soltanto le lettere alfabetiche maiuscole, le cifre numeriche e il trattino (-), tenendo conto che il trattino non può trovarsi all'inizio o alla fine di tali parole. Si osservi che in alcuni casi le parole sono costituite esclusivamente da cifre numeriche, mentre in tutti gli altri, le parole devono iniziare con una lettera alfabetica.
|
Tutte le parole definite dall'utente, a esclusione dei numeri di livello, possono appartenere soltanto a uno dei vari raggruppamenti previsti e devono essere uniche; tuttavia, in alcuni casi è prevista la possibilità di «qualificare» dei nomi, che non sono univoci, in modo da attribuirli al loro contesto preciso (sezione 72.8.3).
Le parole riservate sono quelle parole del linguaggio che fanno parte di un elenco prestabilito e che hanno un significato speciale. Queste parole sono classificate in gruppi in base al loro utilizzo.
|
Per fare riferimento a valori costanti specifici si possono usare alcune parole riservate, note come costanti figurative. Di queste parole chiave esistono sia versioni al singolare, sia al plurale, ma rappresentano sempre la stessa cosa, ovvero un valore singolo o un valore ripetuto, in base al contesto.
|
Gli operatori di relazione si possono rappresentare con i simboli previsti (<, >, e =) oppure attraverso parole speciali, ovvero «parole di caratteri speciali», note come special character word. La tabella successiva riepiloga l'uso degli operatori di relazione, in tutte le loro forme.
|
Le costanti possono essere stringhe di caratteri, il cui valore è implicito in base ai caratteri di cui sono composte, oppure sono costanti figurative, che rappresentano un valore in base al significato verbale che hanno. Una costante può essere di tipo numerico o alfanumerico e non sono previsti altri tipi.
Una costante numerica letterale è una stringa composta da cifre numeriche ed eventualmente anche dai segni +, - e dal punto per la separazione tra la parte intera e la parte decimale (a meno che il punto sia da sostituire con la virgola, avendone scambiato le funzionalità con un'istruzione apposita). Una costante numerica deve contenere almeno una cifra e ha una dimensione massima di cifre che dipende dal compilatore.
Una costante numerica non può contenere più di un segno. Se viene usato il segno, questo deve collocarsi nella posizione più a sinistra; se non appare alcun segno, il valore si intende positivo.
Una costante numerica non può contenere più di un punto decimale e può apparire in qualunque posizione. Se non viene usato il punto decimale, la costante rappresenta un numero intero.
Se nel paragrafo SPECIAL-NAMES della divisione ENVIRONMENT DIVISION è specificata la dichiarazione DECIMAL-POINT IS COMMA, la rappresentazione dei valori numerici avviene scambiando il significato del punto e della virgola (in pratica secondo le convenzioni europee).
Una costante alfanumerica è una stringa di caratteri delimitata da virgolette. La stringa può contenere qualsiasi carattere previsto dalla codifica utilizzata dal compilatore; in generale è ammesso almeno l'uso delle lettere minuscole dell'alfabeto latino.
Per rappresentare le virgolette (") all'interno di una stringa si usa il concatenamento con la costante figurativa QUOTE, come nell'esempio seguente:
|
Una costante alfanumerica deve contenere almeno un carattere all'interno delle virgolette. La lunghezza massima di un valore alfanumerico dipende dal compilatore, ma in generale dovrebbe essere garantita la rappresentazione di almeno 200 caratteri.
I manuali COBOL adottano generalmente una forma particolare di notazione per descriverne la sintassi, a cui si adegua anche questo.
Nella sintassi le «parole chiave», secondo la definizione del COBOL, sono rappresentate sottolineate, a indicare la loro obbligatorietà, mentre le parole facoltative non sono sottolineate. Nell'esempio seguente, le parole IF, NOT, NUMERIC e ALPHABETIC sono parole chiave, mentre la parola IS è facoltativa:
/ \ | NUMERIC | IF identifier IS [NOT] < ¯¯¯¯¯¯¯ > ¯¯ ¯¯¯ | ALPHABETIC | \ ¯¯¯¯¯¯¯¯¯¯ / |
Tutte le parole scritte con lettere minuscole rappresentano delle metavariabili sintattiche che devono essere espresse dal programmatore in quella posizione. Nell'esempio precedente appare una sola metavariabile denominata identifier.
Le parentesi graffe servono a rappresentare la scelta tra alternative differenti. Nell'esempio precedente si deve scegliere tra due parole chiave: NUMERIC o ALPHABETIC.
Le parentesi quadre rappresentano parti opzionali di un'istruzione; tuttavia si osservi che non si tratta di «parole facoltative», secondo la definizione del linguaggio COBOL, perché l'uso o meno di tali porzioni di codice implica un risultato differente dell'istruzione.
La presenza di tre punti consecutivi indica che i dati che precedono la notazione possono essere ripetuti successivamente, in funzione delle esigenze del problema che si intende risolvere.
MOVE identifier-1 TO identifier-2 ... ¯¯¯¯ ¯¯ |
Nell'esempio mostrato, i puntini di sospensione indicano che si possono inserire più variabili (precisamente ciò che è rappresentato come identifier-2). In questo caso, il contenuto della prima variabile viene copiato all'interno di tutte quelle che sono annotate dopo la parola chiave TO.
Quando appare il punto fermo nello schema sintattico, l'istruzione reale deve contenerlo nella stessa posizione relativa.
Il linguaggio COBOL nasce quando l'inserimento dei dati in un elaboratore avveniva principalmente attraverso schede perforate, pertanto, da questo derivano delle limitazioni nel modo in cui vanno scritte le sue istruzioni.
Il modulo di programmazione (coding form) era un foglio quadrettato che conteneva la guida per la scrittura di un programma, da passare poi a una persona che si incaricava di perforare le schede, copiando il testo di tale modulo. Attualmente strumenti del genere non si usano più, tuttavia occorre sapere che le direttive vanno scritte in uno spazio di colonne prestabilito.
In pratica, il codice COBOL si scrive in un file di testo di 80 colonne, rispettando le convenzioni descritte nella tabella successiva.
|
La settima colonna serve per diverse funzioni, distinte in base alla presenza di un simbolo speciale; se in questa colonna si trova uno spazio, la riga viene usata per le funzioni normali. La tabella successiva riepiloga i simboli che possono apparire nella settima colonna e come questi dovrebbero essere interpretati dal compilatore.
|
Per quanto riguarda la continuazione di parole e di costanti numeriche su più righe, il troncamento può avvenire in qualsiasi punto, mettendo un trattino nella settima colonna della riga successiva, continuando lì la parola o la costante, a partire dalla colonna 12 fino alla colonna 72 (area B). Gli spazi finali nella riga interrotta e quelli iniziali della riga che riprende, vengono ignorati.
Le costanti alfanumeriche delimitate da virgolette, si separano in modo differente. Sulla riga spezzata, si considerano tutte le informazioni dalle virgolette di apertura fino alla colonna 72 inclusa, mentre nella riga successiva, la costante deve riprendere aggiungendo altre virgolette di apertura.
Si osservi che ci sono compilatori che si limitano a riconosce solo l'asterisco per i commenti, ignorando tutto il resto. Per questo motivo, è bene evitare l'uso di ogni altro simbolo in questa colonna, quando si vuole scrivere un programma abbastanza compatibile, tenendo conto che si può evitare la continuazione nella riga successiva, perché le istruzioni possono collocarsi su più righe senza spezzare le parole, mentre le costanti alfanumeriche si possono dividere in porzioni più piccole da concatenare. |
Le intestazioni dei paragrafi, delle sezioni e delle divisioni devono iniziare nell'area A. L'intestazione di una divisione consiste nel nome della divisione (IDENTIFICATION, ENVIRONMENT, DATA o PROCEDURE), seguito da uno spazio bianco e dalla parola DIVISION, seguita a sua volta da un punto fermo. L'intestazione di una sezione consiste di un nome di sezione seguito da uno spazio bianco e dalla parola SECTION, seguita a sua volta da un punto fermo. L'intestazione di un paragrafo consiste di un nome di paragrafo seguito da un punto fermo e da uno spazio bianco; il primo gruppo di istruzioni del paragrafo può apparire anche sulla stessa riga.
All'interno delle divisioni IDENTIFICATION DIVISION e ENVIRONMENT DIVISION, le sezioni e i paragrafi sono fissi e sono ammessi solo i nomi previsti espressamente, mentre nella divisione PROCEDURE DIVISION i nomi dei paragrafi e delle sezioni sono stabiliti liberamente.
All'interno della divisione DATA DIVISION, le sezioni FD e SD, così come i numeri di livello 01 e 77, devono iniziare nell'area A, mentre gli altri numeri di livello devono iniziare nell'area B.
Nell'area B inizia tutto quello che non può iniziare nell'area A.
La scrittura di un programma COBOL è sottoposta alle regole seguenti che riguardano l'uso dei caratteri di interpunzione.
Un gruppo di istruzioni termina con un punto seguito da uno spazio bianco. Un punto può apparire in un'altra posizione solo se fa parte di una costante alfanumerica, se si tratta del punto decimale di una costante numerica o se viene usato in un modello di definizione di una variabile (PICTURE).
Una virgola può essere usata fra le istruzioni per facilitare la leggibilità del programma; diversamente, una virgola può apparire solo dove indicato nello schema sintattico. L'uso delle virgole non è obbligatorio.
Il punto e virgola può essere usato al posto della virgola.
Uno spazio delimita sempre una parola o una costante, a meno che tale spazio sia parte di una costante alfanumerica. Lo spazio inteso come delimitatore può essere ridondante; inoltre, quando il testo di un'istruzione termina esattamente alla fine dell'area B (colonna 72), lo spazio successivo viene a mancare.
|
La divisione IDENTIFICATION DIVISION costituisce la prima parte di un programma COBOL. Il suo scopo è quello di contenere delle informazioni sul programma, secondo una classificazione ben stabilita. Le informazioni tipiche che si inseriscono in questa divisione sono il nome del programma (nome che non coincide necessariamente con il nome del file che contiene il sorgente), il nome dell'autore, la data di scrittura del programma, la data di compilazione.
La struttura della divisione IDENTIFICATION DIVISION è sintetizzabile nello schema sintattico seguente:
IDENTIFICATION DIVISION. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ [PROGRAM-ID. program-name]. ¯¯¯¯¯¯¯¯¯¯ [AUTHOR. [comment-entry]...]. ¯¯¯¯¯¯ [INSTALLATION. [comment-entry]...]. ¯¯¯¯¯¯¯¯¯¯¯¯ [DATE-WRITTEN. [comment-entry]...]. ¯¯¯¯¯¯¯¯¯¯¯¯ [DATE-COMPILED. [comment-entry]...]. ¯¯¯¯¯¯¯¯¯¯¯¯¯ [SECURITY. [comment-entry]...]. ¯¯¯¯¯¯¯¯ |
La divisione deve iniziare scrivendo IDENTIFICATION DIVISION a partire dall'area A, ricordando di aggiungere il punto fermo finale.
Tutti i nomi di paragrafo di questa divisione devono iniziare nell'area A e devono terminare con un punto fermo.
Il nome del programma (program-name) deve essere una parola COBOL e serve a identificare il programma sorgente, ma non corrisponde necessariamente al nome del file su disco che contiene il sorgente.
Le voci di commento (comment-entry), secondo lo schema sintattico, possono essere costituite da una sequenza qualunque di caratteri e possono occupare anche più righe, senza bisogno di indicare il simbolo di continuazione nella settima colonna, avendo cura però di utilizzare per tali voci solo l'area B e di terminarle comunque con un punto fermo.
La data di compilazione è, o dovrebbe essere, posta automaticamente dal compilatore, quando è prevista la stampa del sorgente da parte di questo strumento.
A parte il caso della data di compilazione, che dovrebbe essere fornita dal compilatore, tutte le altre informazioni rimangono invariate.
Il listato successivo dà un'idea di come può essere codificata la divisione IDENTIFICATION DIVISION.
|
La divisione ENVIRONMENT DIVISION costituisce la seconda parte di un programma COBOL. La divisione si compone di due sezioni: CONFIGURATION SECTION e INPUT-OUTPUT SECTION.
La sezione CONFIGURATION SECTION serve per indicare delle informazioni relative all'elaboratore usato per la compilazione del programma sorgente e a quello nel quale deve essere eseguito il programma, una volta compilato; inoltre, questa sezione permette di stabilire delle sostituzioni, come nel caso della virgola al posto del punto per separare la parte intera di un numero dalla parte decimale.
La sezione INPUT-OUTPUT SECTION serve per associare i file usati dal programma con le unità fisiche relative, a indicare le caratteristiche di tali file e a stabilire altri aspetti dello scambio di dati.
La struttura della divisione ENVIRONMENT DIVISION è sintetizzabile nello schema sintattico seguente:
ENVIRONMENT DIVISION. .--¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ --. | CONFIGURATION SECTION. | | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | | [SOURCE-COMPUTER. source-computer-entry]. | | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | | [OBJECT-COMPUTER. object-computer-entry]. | | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | | [SPECIAL-NAMES. special-names-entry]. | `--¯¯¯¯¯¯¯¯¯¯¯¯¯ --' .-- --. | INPUT-OUTPUT SECTION. | | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ | | FILE-CONTROL. file-control-entry... | | ¯¯¯¯¯¯¯¯¯¯¯¯ | | [I-O-CONTROL. input-output-control-entry...]. | `--¯¯¯¯¯¯¯¯¯¯¯ --' |
La sezione CONFIGURATION SECTION contiene le informazioni sul sistema usato per la compilazione del programma (nel paragrafo SOURCE-COMPUTER), il sistema nel quale il programma deve essere eseguito (nel paragrafo OBJECT-COMPUTER) e il paragrafo SPECIAL-NAMES che consente di effettuare alcune sostituzioni a dei valori che altrimenti resterebbero al loro stato predefinito.
CONFIGURATION SECTION. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ [SOURCE-COMPUTER. source-computer-entry]. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ [OBJECT-COMPUTER. object-computer-entry]. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ [SPECIAL-NAMES. special-names-entry]. ¯¯¯¯¯¯¯¯¯¯¯¯¯ |
Il paragrafo SOURCE-COMPUTER identifica l'elaboratore presso il quale si intende compilare il programma. Si utilizza secondo lo schema sintattico seguente:
SOURCE-COMPUTER. computer-name [WITH DEBUGGING MODE]. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯¯¯ |
Al posto della metavariabile computer-name deve essere indicata una parola COBOL, che serve solamente a titolo informativo nel sorgente. Se si specifica l'opzione DEBUGGING MODE si richiede al compilatore di prendere in considerazione, nel sorgente, tutte le righe annotate con la lettera D nella settima colonna e le istruzioni USE FOR DEBUGGING, che altrimenti verrebbero semplicemente ignorate.
Il paragrafo OBJECT COMPUTER identifica l'elaboratore presso il quale deve essere utilizzato il programma, una volta compilato. Lo schema sintattico per l'utilizzo di questo paragrafo è quello seguente:
OBJECT-COMPUTER. computer-name... . ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ |
Il nome dell'elaboratore (computer name) deve essere una parola COBOL e ha un significato puramente informativo. Alla fine dell'indicazione dell'ultimo nome, deve apparire un punto fermo.
Il paragrafo SPECIAL-NAMES serve ad associare un valore a dei nomi prestabiliti, quando si vuole che la funzione loro associata sia diversa da quella predefinita, oppure ad attribuire un «nome mnemonico» a un nome usato dal compilatore, che però non fa parte dello standard. Le dichiarazioni che possono apparire in questo paragrafo dipendono molto dalle caratteristiche del compilatore; quello che si vede nello schema sintattico seguente è il minimo che dovrebbe essere disponibile nella maggior parte dei casi:
SPECIAL-NAMES. ¯¯¯¯¯¯¯¯¯¯¯¯¯ implementor-name IS mnemonic-name ¯¯ [CURRENCY SIGN IS literal] ¯¯¯¯¯¯¯¯ ¯¯ [DECIMAL-POINT IS COMMA]. ¯¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯ |
Si utilizza la dichiarazione CURRENTY SIGN IS per fissare il simbolo predefinito da usare come segno di valuta; si usa la dichiarazione DECIMAL-POINT IS COMMA per rappresentare i valori numerici secondo la forma europea, dove la virgola indica la separazione tra la parte intera e quella decimale.
Il segno di valuta può essere costituito da un solo carattere e sono molto pochi i simboli che si possono usare. Per la precisione, sono esclusi tutti i simboli che invece possono essere usati nei modelli di definizione delle variabili oltre a quelli che si usano come delimitatori. In linea di massima sono da escludere: tutte le cifre numeriche (da 0 a 9); lo spazio; le lettere alfabetiche A, B, C, D, J, L, N, P, R, S, V, X, Z; i caratteri speciali *, +, -, ,, ., ;, %, (, ), ", ?. |
Si osservi che anche nel modello di definizione di una variabile (PICTURE), quando si usa la dichiarazione DECIMAL-POINT IS COMMA, il punto e la virgola si scambiano i ruoli. |
L'esempio seguente mostra un pezzo di programma in cui si vede l'uso di queste opzioni. Per la precisione, si assegna la lettera «E» per rappresentare la valuta:
|
L'attribuzione di un nome mnemonico a una parola non standard che però fa parte delle funzionalità specifiche del compilatore utilizzato, consente di limitare a questa sezione le modifiche per l'adattamento del programma a un compilatore che ha funzioni simili, ma descritte da parole diverse. Nell'esempio seguente, compilabile con OpenCOBOL, si sostituisce la parola CONSOLE con STANDARD-INPUT, per identificare la fonte dei dati in ingresso per l'istruzione ACCEPT:
|
Nell'esempio appena mostrato sono evidenziate le righe più importanti per la comprensione del meccanismo; si può comprendere che l'istruzione ACCEPT avrebbe potuto essere scritta semplicemente così:
|
Tuttavia, avendo utilizzato il nome mnemonico STANDARD-INPUT, se con un altro compilatore la console fosse identificata dalla sigla SPO (Supervisory printer output, come avveniva nel COBOL CMS (Computer management system della Burroughs negli anni 1980), basterebbe modificare la dichiarazione iniziale:
|
Per chiarezza, è il caso di sottolineare che STANDARD-INPUT ha valore per il compilatore solo in quanto viene dichiarato come nome mnemonico, dal momento che il linguaggio, nella sua veste ufficiale, non prevede la gestione dei flussi standard dei sistemi Unix.
La sezione INPUT-OUTPUT SECTION si suddivide in due paragrafi: FILE-CONTROL e I-O-CONTROL. Il paragrafo FILE-CONTROL specifica l'organizzazione e l'accesso dei file che vengono usati dal programma e le informazioni correlate a tali file; il paragrafo I-O-CONTROL serve a specificare informazioni aggiuntive sui file già dichiarati nell'altro paragrafo.
INPUT-OUTPUT SECTION. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ FILE-CONTROL. file-control-entry... ¯¯¯¯¯¯¯¯¯¯¯¯ [I-O-CONTROL. input-output-control-entry...]. ¯¯¯¯¯¯¯¯¯¯¯ |
Il paragrafo FILE-CONTROL serve a dichiarare i file utilizzati dal programma e a definire alcune loro caratteristiche. Tutti i file dichiarati nel paragrafo FILE-CONTROL devono essere descritti nella divisione DATA DIVISION; nello stesso modo, tutti i file descritti nella divisione DATA DIVISION, devono essere dichiarati nel paragrafo FILE-CONTROL.
Il linguaggio COBOL prevede una gestione dei file molto sofisticata, anche se non è detto che i compilatori mettano a disposizione sempre tutte le funzionalità standard. Si distinguono generalmente i tipi, in base alla loro «organizzazione», come sintetizzato nella tabella successiva.
Per il linguaggio COBOL i file sono sempre composti da record, pertanto l'accesso a un file si riferisce sempre a dei record. |
|
L'organizzazione del file definisce le potenzialità di accesso, ma in generale sono disponibili diverse varianti nel modo particolare di accedere ai record.
Il paragrafo FILE CONTROL si articola in dichiarazioni SELECT, una per ogni file, secondo lo schema sintattico sintetico seguente:
FILE-CONTROL. ¯¯¯¯¯¯¯¯¯¯¯¯ SELECT file-name ASSIGN TO hardware-name [altre-opzioni]. ¯¯¯¯¯¯ ¯¯¯¯¯¯ ... |
Il modo in cui l'istruzione SELECT si articola, dipende dall'organizzazione del file e dal metodo di accesso specifico che si vuole attuare sullo stesso. Nella logica originale del linguaggio, in questa fase non viene ancora indicato il nome del file reale, secondo il sistema operativo, perché generalmente per questa informazione si agisce nella divisione DATA DIVISION; tuttavia, spesso il compilatore permette, o richiede, di specificare il nome del file reale proprio nell'istruzione SELECT.
L'organizzazione di un file è una caratteristica immutabile, che stabilisce, oltre che le potenzialità di accesso, anche la sua forma fisica «reale», ovvero quella che viene gestita attraverso l'astrazione del sistema operativo.
L'organizzazione sequenziale è quella più semplice, dove normalmente i record logici del linguaggio corrispondono esattamente al contenuto del file fisico che li contiene.
L'organizzazione relativa richiede la capacità di abbinare delle informazioni ai record logici, per esempio per poter annotare che un record è stato cancellato. Per fare questo, il compilatore può inserire tutte le informazioni necessarie in un file solo, oppure può avvalersi di due file reali: uno per i dati, l'altro per le informazioni sui record.
L'organizzazione a indice richiede tutte le funzionalità di quella relativa, con l'aggiunta di poter gestire l'accesso in base a una o più chiavi. Nei compilatori COBOL attuali, è molto probabile che tutte le informazioni necessarie vengano gestite in un file fisico soltanto, ma originariamente era frequente l'uso di un file per i dati e di altri file per le chiavi (uno per ogni chiave).
In base a questa premessa, si deve intendere che un file che viene creato con una certa organizzazione, può essere usato solo con quella; inoltre, si può contare sul fatto che un file creato con un programma realizzato attraverso un certo compilatore COBOL, non può essere utilizzato con un programma generato con un altro.
Di fronte a questo problema di compatibilità dei dati, i file organizzati in modo sequenziale sono sempre l'unica garanzia per un trasferimento dei dati. D'altra parte, negli anni in cui il linguaggio COBOL aveva il suo massimo splendore, i nastri magnetici rappresentavano l'unità di memorizzazione «standard» tra le varie architetture proprietarie.
Lo schema sintattico semplificato per l'istruzione SELECT, da usare nel paragrafo FILE-CONTROL, per dichiarare un file sequenziale è quello che si può vedere nella figura successiva:
/ \ | hardware-name | SELECT file-name ASSIGN TO < > ¯¯¯¯¯¯ ¯¯¯¯¯¯ | literal-file-name | \ / .-- .-- --. --. | | AREA | | | RESERVE integer | | | | ¯¯¯¯¯¯¯ | AREAS | | `-- `-- --' --' [ ORGANIZATION IS [LINE] SEQUENTIAL ] ¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ [ ACCESS MODE IS SEQUENTIAL ] ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ [ FILE STATUS IS data-name ]. ¯¯¯¯¯¯ |
Il file sequenziale può essere letto o scritto soltanto in modo sequenziale, a partire dall'inizio. Se l'unità di memorizzazione che lo contiene è sequenziale per sua natura, come avviene per un nastro o un lettore di schede perforate, si può avere solo una fase di lettura o una fase di scrittura, senza la possibilità di mescolare le due operazioni, mentre se si dispone di un'unità di memorizzazione ad accesso diretto, come nel caso di un disco, si può leggere e poi sovrascrivere lo stesso record.
Nello schema sintattico, la metavariabile file-name deve essere sostituita con il nome che si vuole attribuire al file nell'ambito del programma (non si tratta del nome che questo ha eventualmente per il sistema operativo). La metavariabile hardware-name va sostituita con un nome che serve a identificare l'unità di memorizzazione che contiene il file; questo nome dipende dal compilatore ma generalmente si mette DISK per indicare un file su disco. Altri nomi per la metavariabile hardware-name potrebbero essere: TAPE, PRINTER, PUNCH, READER (gli ultimi due sarebbero un perforatore e un lettore di schede).
Il linguaggio COBOL è fatto per poter essere adattato a sistemi operativi molto diversi. In un sistema Unix, l'accesso alle unità di memorizzazione avviene attraverso dei file di dispositivo, pertanto, a seconda del compilatore, potrebbe anche essere superfluo dichiarare il tipo di unità di memorizzazione in questo modo, anche se in passato il linguaggio obbligava a farlo. Proprio per questo motivo, ci sono compilatori che, al posto di indicare il tipo di unità fisica attraverso un nome prestabilito, richiedono di mettere subito il percorso del file a cui si vuole fare riferimento, nonostante il linguaggio preveda per questo una dichiarazione separata nella divisione DATA DIVISION. In questo senso, nello schema sintattico appare la possibilità di indicare una stringa alfanumerica con il percorso del file (literal-file-name). |
Nella dichiarazione RESERVE integer, la metavariabile integer rappresenta un numero intero di record da usare come memoria tampone. Se non si usa questa dichiarazione che, come si vede dallo schema sintattico, è facoltativa, viene usata la dimensione predefinita.
La dichiarazione ORGANIZATION IS SEQUENTIAL è facoltativa e sottintesa; tuttavia va osservato il significato che assume quando si aggiunge la parola LINE. In generale, il linguaggio COBOL considera i file come composti da record di dimensione uniforme. Quando però si vuole lavorare con i file di testo, le righe di questi file sono suddivise in base alla presenza del codice di interruzione di riga (che può cambiare da un sistema operativo all'altro). Volendo considerare in COBOL le righe di un file di testo pari a dei record di dimensione variabile, occorre aggiungere l'opzione LINE, così da chiarire che si tratta sì di un'organizzazione sequenziale, ma di un file suddiviso in «righe».
La dichiarazione ACCESS MODE IS SEQUENTIAL è facoltativa, perché l'accesso a un file organizzato in modo sequenziale può essere solo sequenziale.
La dichiarazione FILE STATUS IS data-name consente di indicare una variabile (da specificare nella sezione WORKING-STORAGE SECTION della divisione DATA DIVISION) da usare eventualmente per conoscere lo stato dell'ultima operazione svolta sul file. Questa variabile deve poter rappresentare un valore di due caratteri (il modello di definizione della variabile deve essere XX) e quando contiene il valore zero indica che l'ultima operazione è stata eseguita con successo (si vedano le tabelle 72.48 e 72.49, che appaiono alla fine del capitolo).
Il punto fermo che conclude l'istruzione SELECT appare una volta sola, alla fine; tutta l'istruzione deve risiedere nell'area B.
Viene mostrato un esempio completo di un programma COBOL che legge un file sequenziale:
|
Il file indicato come FILE-NAME è associato in pratica al file input.seq
. Si può supporre che questo file abbia il contenuto seguente, senza alcun codice di interruzione di riga:
|
Eseguendo il programma dell'esempio si potrebbe ottenere il testo seguente attraverso lo schermo:
FILE STATUS: 00 FILE STATUS: 00 RECORD: aaaaaaaaaaaaaaaaaaaa FILE STATUS: 00 RECORD: bbbbbbbbbbbbbbbbbbbb FILE STATUS: 00 RECORD: cccccccccccccccccccc FILE STATUS: 00 RECORD: dddddddddddddddddddd FILE STATUS: 10 RECORD: dddddddddddddddddddd |
Lo schema sintattico semplificato per l'istruzione SELECT, da usare nel paragrafo FILE-CONTROL, per dichiarare un file organizzato in modo «relativo» è quello che si può vedere nella figura successiva:
/ \ | DISK | SELECT file-name ASSIGN TO < ¯¯¯¯ > ¯¯¯¯¯¯ ¯¯¯¯¯¯ | literal-file-name | \ / .-- .-- --. --. | | AREA | | | RESERVE integer | | | | ¯¯¯¯¯¯¯ | AREAS | | `-- `-- --' --' [ ORGANIZATION IS ] RELATIVE ¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯ .-- / SEQUENTIAL \ --. | | ¯¯¯¯¯¯¯¯¯¯ | | | | / \ | | | ACCESS MODE IS < | RANDOM | > | | ¯¯¯¯¯¯ | < ¯¯¯¯¯¯ > RELATIVE KEY IS data-name-1 | | | | | DYNAMIC | ¯¯¯¯¯¯¯¯ | | `-- \ \ ¯¯¯¯¯¯¯ / / --' [ FILE STATUS IS data-name-2 ]. ¯¯¯¯¯¯ |
Il file organizzato in modo relativo può essere utilizzato secondo un accesso sequenziale, oppure facendo riferimento ai record per numero, considerando che il primo ha proprio il numero uno. Quando si individuano i record per numero, si distinguono due modalità di accesso: diretto (RANDOM) e dinamico (DYNAMIC). L'accesso diretto richiede che per ogni operazione l'indicazione del numero del record a cui si vuole fare riferimento, mentre con l'accesso dinamico è anche possibile eseguire delle operazioni di lettura sequenziali (READ NEXT).
L'organizzazione relativa, oltre alle operazioni di lettura e scrittura, prevede la cancellazione dei record, che comunque possono essere rimpiazzati successivamente attraverso un'operazione di scrittura. Si osservi comunque che un record che risulta essere stato cancellato, non può essere letto.
Osservando lo schema sintattico si può intuire che la prima parte dell'istruzione SELECT funzioni nello stesso modo di un file organizzato sequenzialmente; la differenza più importante riguarda la definizione del tipo di unità di memorizzazione che, date le caratteristiche dei file organizzati in modo relativo, deve consentire un accesso diretto ai dati.
La dichiarazione RESERVE integer si usa nello stesso modo del file organizzato sequenzialmente.
L'indicazione dell'organizzazione, attraverso la dichiarazione ORGANIZATION IS RELATIVE è obbligatoria, anche se probabilmente è sufficiente scrivere soltanto RELATIVE.
Se non viene specificata la dichiarazione ACCESS MODE, si intende che l'accesso debba avvenire in modo sequenziale, altrimenti vale quanto indicato espressamente. Se l'accesso richiesto è diretto o dinamico, è necessario indicare quale variabile usare per specificare il numero del record, nella posizione occupata nello schema sintattico dalla metavariabile data-name-1 (da specificare ulteriormente nella sezione WORKING-STORAGE SECTION della divisione DATA DIVISION).
La dichiarazione FILE STATUS IS data-name-2 funziona nello stesso modo descritto a proposito dei file organizzati in modo sequenziale.
Il punto fermo che conclude l'istruzione SELECT appare una volta sola, alla fine; tutta l'istruzione deve risiedere nell'area B.
Viene mostrato un esempio completo di un programma COBOL che legge un file relativo, ad accesso diretto, scandendo sequenzialmente il numero del record:
|
Il file indicato come MIO-FILE è associato in pratica al file input.rel
. Si può supporre che questo file sia composto dall'elenco seguente di record logici:
aaaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccc
dddddddddddddddddddd
Eseguendo il programma dell'esempio si potrebbe ottenere il testo seguente attraverso lo schermo:
FILE STATUS: 00 FILE STATUS: 00 RECORD: 0001 aaaaaaaaaaaaaaaaaaaa FILE STATUS: 00 RECORD: 0002 bbbbbbbbbbbbbbbbbbbb FILE STATUS: 00 RECORD: 0003 cccccccccccccccccccc FILE STATUS: 00 RECORD: 0004 dddddddddddddddddddd INVALID KEY! FILE STATUS: 23 RECORD: 0005 dddddddddddddddddddd |
Segue un altro esempio completo per la lettura di un file relativo, utilizzando un accesso dinamico, partendo dal primo record e selezionando i successivi attraverso la richiesta del prossimo:
|
Il file che viene letto è lo stesso dell'esempio precedente e il risultato si dovrebbe ottenere, si può vedere così:
FILE STATUS: 00 FILE STATUS: 00 RECORD: 0001 aaaaaaaaaaaaaaaaaaaa FILE STATUS: 00 RECORD: 0002 bbbbbbbbbbbbbbbbbbbb FILE STATUS: 00 RECORD: 0003 cccccccccccccccccccc FILE STATUS: 00 RECORD: 0004 dddddddddddddddddddd END OF FILE! |
Lo schema sintattico semplificato per l'istruzione SELECT, da usare nel paragrafo FILE-CONTROL, per dichiarare un file organizzato a indici è quello che si può vedere nella figura successiva:
/ \ | DISK | SELECT file-name ASSIGN TO < ¯¯¯¯ > ¯¯¯¯¯¯ ¯¯¯¯¯¯ | literal-file-name | \ / .-- .-- --. --. | | AREA | | | RESERVE integer | | | | ¯¯¯¯¯¯¯ | AREAS | | `-- `-- --' --' [ ORGANIZATION IS ] INDEXED ¯¯¯¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯ .-- / \ --. | | SEQUENTIAL | | | | | | | ACCESS MODE IS < RANDOM > | | ¯¯¯¯¯¯ | ¯¯¯¯¯¯ | | | | DYNAMIC | | `-- \ ¯¯¯¯¯¯¯ / --' RECORD KEY IS data-name-1 [WITH DUPLICATES] ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ [ ALTERNATE RECORD KEY IS data-name-2 [WITH DUPLICATES] ]... ¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ [ FILE STATUS IS data-name-3 ]. ¯¯¯¯¯¯ |
Un file organizzato a indice è un file che consente un accesso diretto ai record in base a una chiave costituita da una porzione del record stesso. A titolo di esempio, si può immaginare un file contenente i dati anagrafici dei dipendenti di un'azienda, che in una posizione precisa dei record riporta il numero di matricola di ognuno; in tal modo, il numero di matricola può essere usato per definire la chiave di accesso ai record.
Il file organizzato a indice può disporre di una o più chiavi di accesso e può essere consentita o meno la presenza di record con chiavi uguali.
Rispetto ai file organizzati sequenzialmente o in modo relativo, lo schema sintattico per i file organizzati a indice ha le dichiarazioni RECORD KEY e ALTERNATE RECORD KEY per poter specificare la chiave o le chiavi di accesso. Le metavariabili data-name-1 e data-name-2 devono essere nomi di porzioni di record, come dichiarato nella divisione DATA DIVISION, in corrispondenza della descrizione del record stesso. Naturalmente, l'opzione WITH DUPLICATES serve a dichiarare l'intenzione di gestire chiavi uguali su più record.
Oltre ai file comuni, per i quali si stabilisce un'organizzazione e un tipo di accesso, sono previsti dei file da usare soltanto per ottenere un riordino o una fusione (sort, merge). Per questi file occorre una dichiarazione apposita con l'istruzione SELECT, secondo lo schema sintattico seguente:
/ \ | DISK | SELECT sort-merge-file-name ASSIGN TO < ¯¯¯¯ > . ¯¯¯¯¯¯ ¯¯¯¯¯¯ | literal-file-name | \ / |
Viene proposto un esempio di riordino di file, nel quale, in particolare, si dichiarano i nomi dei file su disco, direttamente nell'istruzione SELECT:
|
Come si può vedere, si vuole ordinare il file input.seq
per generare il file output.seq
, ordinato. Per fare questo, si usa un file intermedio, denominato sort.tmp
. Al termine dell'operazione, non dovrebbe rimanere traccia del file intermedio.
Si osservi che non si rende necessaria l'apertura dei file coinvolti per portare a termine l'operazione. |
L'esempio seguente riguarda la fusione: si hanno i file input-1.seq
e input-2.seq
ordinati e si vuole ottenere il file output.seq
con la somma dei record, mantenendo l'ordinamento:
|
Si osservi che esistono compilatori COBOL, di buona qualità, che però non offrono le funzionalità di riordino e di fusione, oppure non in modo completo. È frequente l'assenza della funzione per la fusione dei file ordinati.
Il paragrafo I-O-CONTROL è opzionale e il suo scopo è quello di specificare l'utilizzo comune delle aree di memoria centrale adibite alla gestione dei file.
I-O-CONTROL. ¯¯¯¯¯¯¯¯¯¯¯ .-- .-- --. --. | | RECORD | | | | ¯¯¯¯¯¯ | | | SAME | SORT | AREA FOR file-name-1 [file-name-2]... |... . | ¯¯¯¯ | ¯¯¯¯ | | | | SORT-MERGE | | `-- `--¯¯¯¯¯¯¯¯¯¯--' --' |
L'utilità dell'utilizzo del paragrafo I-O-CONTROL dipende molto dal compilatore, che potrebbe anche limitarsi a ignorare l'istruzione SAME...AREA, in tutto o solo in parte. Tuttavia, quando l'istruzione SAME...AREA viene presa in considerazione, ci sono delle conseguenze nell'accesso ai file, che bisogna conoscere.
Per cominciare: si intuisce dallo schema sintattico che l'istruzione SAME...AREA inizia nell'area B del modulo di programmazione, si vede che il punto fermo è richiesto solo alla fine del gruppo di istruzioni SAME...AREA, inoltre sono evidenti quattro possibilità:
SAME AREA FOR file-name-1 [file-name-2]... . ¯¯¯¯ SAME RECORD AREA FOR file-name-1 [file-name-2]... . ¯¯¯¯ ¯¯¯¯¯¯ SAME SORT AREA FOR file-name-1 [file-name-2]... . ¯¯¯¯ ¯¯¯¯ SAME SORT-MERGE AREA FOR file-name-1 [file-name-2]... . ¯¯¯¯ ¯¯¯¯¯¯¯¯¯¯ |
Utilizzando la prima forma dell'istruzione SAME AREA, si intende richiedere al compilatore che la gestione dei file elencati sia fatta condividendo tutto quello che si può condividere nella memoria centrale. Così facendo, nell'ambito del gruppo specificato, solo un file può essere aperto simultaneamente; inoltre, se si utilizzano più istruzioni SAME AREA, un file può appartenere soltanto a uno di questi raggruppamenti.
Utilizzando l'istruzione SAME RECORD AREA si richiede al compilatore di gestire lo spazio della memoria tampone (dei record) di un gruppo di file in modo comune. Così facendo, la lettura di un record di un file del gruppo, comporta il fatto che gli stessi dati siano disponibili come se fossero stati letti da tutti gli altri file del gruppo. I file di un gruppo definito con questa istruzione possono essere aperti simultaneamente, ma le operazioni di accesso ai dati non possono essere simultanee; inoltre, un file può appartenere a un solo raggruppamento di questo tipo.
Teoricamente, i file indicati in un raggruppamento con l'istruzione SAME AREA possono apparire anche in un raggruppamento con l'istruzione SAME RECORD AREA, ma in tal caso deve trattarsi di tutti quelli che appartengono al primo di questi due (tutti quelli in SAME AREA devono essere parte di quello in SAME RECORD AREA). Inoltre, questo fatto comporta che i file che si trovano anche in SAME AREA non possono essere aperti simultaneamente.
Nei manuali COBOL classici si sottolinea il fatto che la condivisione dei record offra dei vantaggi in velocità e in risparmio di memoria; in particolare si suggerisce in tali manuali la possibilità di dichiarare nel dettaglio uno solo dei record del gruppo, oppure la possibilità di ridefinire i record cambiando il punto di vista (il record rispetto a quello di un altro). Tuttavia, considerata la potenza elaborativa degli elaboratori attuali, dal momento che esiste comunque la possibilità di ridefinire la suddivisione di un record, l'uso di questo paragrafo diventa sconsigliabile, se non altro per le complicazioni che si creano nell'interpretazione umana del programma sorgente. |
Le istruzioni SAME SORT AREA e SAME SORT-MERGE AREA sono equivalenti e consentono di condividere la memoria utilizzata per i file che servono specificatamente per il riordino o la fusione. Premesso che in questi raggruppamenti non possono apparire file che appartengono a un gruppo definito come SAME AREA, è invece possibile inserire anche nomi di file che non sono stati dichiarati per l'ordinamento o la fusione, ma la loro presenza fa sì che questi file non possano essere aperti quando invece lo sono quelli che si utilizzano proprio per tale scopo.
I file dichiarati con l'indicatore SD nella sezione FILE SECTION servono per portare a termine le operazioni di riordino e di fusione, ma si avvalgono di file in ingresso e di file in uscita, che vengono dichiarati normalmente con l'indicatore FD. Tutti i file coinvolti in un procedimento di riordino e di fusione, non devono essere aperti esplicitamente durante questa fase. |
|
|
La divisione DATA DIVISION costituisce la terza parte, la più complessa, di un programma COBOL e ha lo scopo di descrivere tutti i dati (variabili e costanti) utilizzati nel programma. Si distinguono in particolare: i record dei file a cui si vuole accedere, altre variabili e valori costanti creati o utilizzati dal programma.
La divisione si articola normalmente in tre sezioni: FILE SECTION, per tutte le informazioni riguardanti i file dichiarati nella divisione ENVIRONMENT DIVISION, soprattutto per quanto riguarda la struttura del record; WORKING-STORAGE SECTION per tutte le variabili (che possono essere sia scalari, sia strutturate, ma in questo secondo caso vengono chiamate ugualmente record, anche se non sono associate direttamente ad alcun file) e le costanti necessarie per l'elaborazione; LINKAGE SECTION, per la dichiarazione dei dati condivisi con altri programmi.
In questo manuale la descrizione della sezione LINKAGE SECTION viene omessa del tutto; pertanto, lo schema sintattico seguente non la riporta:
DATA-DIVISION. ¯¯¯¯¯¯¯¯¯¯¯¯¯ .-- | | FILE SECTION. | ¯¯¯¯¯¯¯¯¯¯¯¯ `-- .-- --. --. | file-description-entry record-description-entry... | | | |... | | sort-merge-description-entry record-description-entry... | | `-- --' --' .-- | | WORKING-STORAGE SECTION. | ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ `-- .-- --. --. | 77-level-description-entry | | | |... | | record-description-entry | | `-- --' --' |
Sulla base della terminologia usata nello schema sintattico, si può intuire il fatto che per il linguaggio COBOL, il termine record ha un significato particolare: si tratta di una variabile strutturata, che pertanto può essere scomposta in campi, in modo più o meno articolato. In questo senso, il contenuto della sezione WORKING-STORAGE SECTION viene suddiviso in due tipi di dichiarazioni: variabili scalari non suddivisibili (la metavariabile 77-level-description-entry) e variabili strutturate, ovvero record. Naturalmente, una variabile strutturata (dichiarata come record) può essere gestita e usata tranquillamente come se fosse uno scalare puro e semplice, ma questo fatto ha eventualmente delle ripercussioni nell'efficienza del programma che si ottiene dalla compilazione.
La sezione FILE SECTION ha lo scopo di definire le caratteristiche fisiche dei file e la struttura dei record. Tradizionalmente sarebbe in questa sezione che si specifica il nome o il percorso dei file in base al sistema operativo in cui si deve utilizzare il programma, salvo il caso in cui il compilatore voglia questa indicazione direttamente nella divisione ENVIRONMENT DIVISION, precisamente nell'istruzione SELECT della sezione FILE CONTROL.
La descrizione di un file nella sezione FILE SECTION inizia con l'indicatore di livello FD o SD, a seconda che si tratti di un file «normale» o di un file da usare per le operazioni di riordino e fusione. Si osservi che queste due istruzioni iniziano nell'area A del modulo di programmazione, continuando poi nell'area B, ma è importante sottolineare che già il nome del file, evidenziato nello schema sintattico con il nome file-name, deve iniziare nell'area B:
/ \ | FD file-name | < ¯¯ > | SD file-name | \ ¯¯ / .-- --. | entry-item |... . `-- --' |
Dopo ogni indicatore di livello FD o SD deve apparire la dichiarazione della variabile strutturata che rappresenta il record del file; tale dichiarazione inizia con il livello 01.
I file comuni, ovvero quelli che non sono stati dichiarati esplicitamente per eseguire delle operazioni di riordino o di fusione, si descrivono nella sezione FILE SECTION con l'indicatore di livello FD (File description), che in pratica è un'istruzione singola. Si ricordi che il nome del file che segue la parola chiave FD deve iniziare nell'area B del modulo di programmazione:
FD file-name ¯¯ .-- / \ --. | | RECORDS | | | BLOCK CONTAINS [integer-1 TO] integer-2 < ¯¯¯¯¯¯¯ > | | ¯¯¯¯¯ ¯¯ | CHARACTERS | | `-- \ / --' [ RECORD CONTAINS [integer-3 TO] integer-4 CHARACTERS ] ¯¯¯¯¯¯ ¯¯ .-- / \ / \ --. | | RECORD IS | | OMITTED | | | LABEL < ¯¯¯¯¯¯ > < ¯¯¯¯¯¯¯ > | | ¯¯¯¯¯ | RECORDS ARE | | STANDARD | | `-- \ ¯¯¯¯¯¯¯ / \ ¯¯¯¯¯¯¯¯ / --' .-- / / \ \ --. | | | data-name-1 | | | | VALUE OF < label-info-1 IS < > >... | | ¯¯¯¯¯ | | literal-1 | | | `-- \ \ / / --' .-- / \ --. | | RECORD IS | | | DATA < ¯¯¯¯¯¯ > data-name-2 [data-name-3]... | | ¯¯¯¯ | RECORDS ARE | | `-- \ ¯¯¯¯¯¯¯ / --' [ CODE-SET IS alphabet-name ]. ¯¯¯¯¯¯¯¯ |
Si osservi che, a seconda del compilatore e del sistema operativo per il quale il programma viene compilato, diverse dichiarazioni inserite nell'indicatore di livello FD potrebbero essere ignorate in pratica. |
I file da usare specificatamente per il riordino o la fusione, si descrivono nella sezione FILE SECTION con l'indicatore di livello SD (Sort description), che in pratica è un'istruzione singola. Si ricordi che il nome del file che segue la parola chiave SD deve iniziare nell'area B:
SD file-name ¯¯ [ RECORD CONTAINS [integer-3 TO] integer-4 CHARACTERS ] ¯¯¯¯¯¯ ¯¯ .-- / / \ \ --. | | | data-name-1 | | | | VALUE OF < label-info-1 IS < > >... | | ¯¯¯¯¯ | | literal-1 | | | `-- \ \ / / --' .-- / \ --. | | RECORD IS | | | DATA < ¯¯¯¯¯¯ > data-name-2 [data-name-3]... | . | ¯¯¯¯ | RECORDS ARE | | `-- \ ¯¯¯¯¯¯¯ / --' |
All'interno dell'indicatore di livello FD è possibile dichiarare la dimensione di un blocco fisico per l'accesso ai record del file a cui si sta facendo riferimento.
In generale, si può contare sul fatto che il sistema operativo sia in grado di gestire in modo trasparente il problema dei blocchi fisici dei dati, rispetto ai record «logici» utilizzati dai programmi; tuttavia, ci possono essere contesti in cui il programma che si genera deve provvedere da solo ad accedere all'unità di memorizzazione, pertanto in questi casi conviene dichiarare nel programma la dimensione del blocco di dati da usare per la comunicazione con l'unità stessa. Storicamente la definizione del blocco consente di gestire meglio l'utilizzo di un'unità a nastro; in altre situazioni, come per esempio con un lettore o perforatore di schede, il blocco può contenere un solo record.
/ \ | RECORDS | BLOCK CONTAINS [integer-1 TO] integer-2 < ¯¯¯¯¯¯¯ > ¯¯¯¯¯ ¯¯ | CHARACTERS | \ / |
Omettendo questa dichiarazione, si intende lasciare al compilatore o al sistema operativo il compito di determinare un valore predefinito valido.
L'unità di misura del blocco dipende dalla parola usata, o non usata, alla fine della dichiarazione: la parola chiave RECORDS indica che i valori numerici si riferiscono a quantità di record, mentre diversamente si intendono dei «caratteri». Generalmente è da considerare che per caratteri si intendano byte.
Se viene indicato un valore solo (integer-2), si intende che il blocco possa avere soltanto quella dimensione, altrimenti, si intende dire al compilatore che c'è la possibilità di usare blocchi che hanno una dimensione minima (integer-1) e una massima (integer-2).
La dichiarazione DATA RECORD, che riguarda sia l'indicatore di livello FD, sia SD, è superata e generalmente viene ignorata dai compilatori. Il suo scopo è quello di dichiarare il nome di una o più variabili strutturate che descrivono il record del file. Questa dichiarazione è superata soprattutto perché il record viene comunque indicato successivamente attraverso la dichiarazione di una variabile strutturata apposita.
/ \ | RECORD IS | DATA < ¯¯¯¯¯¯ > data-name-2 [data-name-3]... ¯¯¯¯ | RECORDS ARE | \ ¯¯¯¯¯¯¯ / |
I nomi da inserire al posto delle metavariabili data-name dello schema sintattico devono corrispondere a nomi di record (variabili strutturate) descritti con il numero di livello 01. La presenza di più di uno di questi nomi nella dichiarazione DATA implica che i record del file possono avere una struttura e una dimensione differente.
A seconda del tipo di unità di memorizzazione dei dati, ci può essere la necessità di aggiungere ai record delle informazioni per poterne poi gestire l'accesso. Il linguaggio COBOL prevede la possibilità di dover gestire direttamente questo meccanismo di etichettatura dei record, ma generalmente i sistemi operativi attuali dovrebbero rendere questo meccanismo trasparente, togliendo al programma COBOL l'onere di doversene occupare.
La dichiarazione LABEL RECORD servirebbe per stabilire se siano da gestire le «etichette» dei record, oppure se questa funzione non debba essere considerata dal programma. Attualmente, tale dichiarazione è superata e generalmente i compilatori si limitano a ignorarla:
/ \ / \ | RECORD IS | | OMITTED | LABEL < ¯¯¯¯¯¯ > < ¯¯¯¯¯¯¯ > ¯¯¯¯¯ | RECORDS ARE | | STANDARD | \ ¯¯¯¯¯¯¯ / \ ¯¯¯¯¯¯¯¯ / |
Dovendo o volendo inserire questa dichiarazione, in caso di dubbio la forma LABEL RECORD IS STANDARD dovrebbe essere quella più adatta, anche se non è più compito del programma occuparsi delle etichette. Di per sé, l'omissione di questa dichiarazione comporta, per il compilatore che dovesse volerla, proprio l'utilizzo della forma standard.
La dichiarazione RECORD CONTAINS, che riguarda sia l'indicatore di livello FD, sia SD, permette di specificare la dimensione del record:
RECORD CONTAINS [integer-3 TO] integer-4 CHARACTERS ¯¯¯¯¯¯ ¯¯ |
Come si può intuire, se si indica un valore solo, si intende che il record abbia una dimensione fissa, altrimenti si prevede un intervallo di valori: da un minimo a un massimo.
Generalmente, i compilatori si limitano a ignorare questa dichiarazione, perché le informazioni che porta sono già incluse nella variabile strutturata che descrive il record stesso, pertanto è sufficiente associare più variabili strutturate nella dichiarazione DATA RECORD.
La dichiarazione CODE-SET riguarda i file a organizzazione sequenziale e serve a specificare l'insieme di caratteri con cui tale file è codificato. Tradizionalmente, questa istruzione è servita per gestire dati in formato EBCDIC, in contrapposizione al codice ASCII, o viceversa.
CODE-SET IS alphabet-name ¯¯¯¯¯¯¯¯ |
Al posto della metavariabile alphabet-name va inserita una parola che definisce l'insieme di caratteri del file, secondo le aspettative del compilatore utilizzato.
La dichiarazione VALUE OF consente, in un certo senso, di assegnare dei valori a delle voci legate alle caratteristiche del file. La cosa più importante che si potrebbe fare è di specificare il file da utilizzare secondo ciò che richiede il sistema operativo. Per esempio, se si tratta di un file su disco e il sistema operativo richiede di indicare anche i dischi per nome, il compilatore dovrebbe prevedere qui una voce appropriata.
/ / \ \ | | data-name-1 | | VALUE OF < label-info-1 IS < > >... ¯¯¯¯¯ | | literal-1 | | \ \ / / |
Le voci che si possono dichiarare qui possono essere di ogni tipo, con la possibilità di abbinare un valore costante (una stringa alfanumerica), oppure una variabile il cui contenuto viene poi modificato in fase elaborativa.
L'estratto seguente di un programma COBOL, scritto per il compilatore TinyCOBOL, mostra l'uso della voce FILE-ID per dichiarare il nome del file da utilizzare:
|
Dopo ogni indicatore di livello (FD o SD) si deve descrivere il record attraverso una variabile strutturata, che si dichiara con quelli che sono noti come livelli. I livelli sono in pratica delle dichiarazioni che costituiscono ognuna delle istruzioni singole, ma in tal caso, a differenza delle istruzioni comuni, iniziano con un numero: il numero di livello.
Il livello 01 è obbligatorio e dichiara il nome della variabile strutturata che descrive il record nella sua interezza; qualunque numero superiore serve a descrivere una porzione inferiore del record, con la possibilità di scomposizioni successive. I numeri di livello che possono essere usati per questo scopo sono limitati all'intervallo da 01 a 49, tenendo conto che, a parte l'obbligo di iniziare da 01, i livelli inferiori possono utilizzare incrementi superiori all'unità. Si osservi l'esempio seguente che contiene un estratto dalla sezione FILE SECTION:
|
Il file individuato dal nome SALES-FILE si compone di record a cui si può fare riferimento con la variabile strutturata SALES-RECORD. Il record si suddivide in sette campi con caratteristiche diverse. Il record nella sua interezza corrisponde al livello 01, evidenziato dalla sigla 01 che si trova nell'area A del modulo di programmazione. Come si vede nel livello 01 dell'esempio, la variabile strutturata che rappresenta tutto il record viene solo nominata, senza altre indicazioni, perché la sua dimensione si determina dalla somma dei campi che contiene.
I numeri di livello, mano a mano che si annidano in sottolivelli successivi, devono crescere: non è importante se il numero cresce di una o di più unità. Tradizionalmente, i livelli vengono incrementati con un passo maggiore di uno, per facilitare la modifica del sorgente quando dovesse presentarsi l'esigenza di ristrutturare i livelli. |
Per comprendere meglio il senso della descrizione del record attraverso il sistema dei livelli, conviene dare un'occhiata allo schema successivo:
Quello che appare nello schema vuole rappresentare il record di un file da usare per memorizzare carichi e scarichi di un magazzino. Si può osservare inizialmente un campo per numerare le registrazioni (ogni registrazione occupa un record), successivamente, appare la data dell'operazione suddivisa in tre parti (anno, mese e giorno), quindi viene indicato il carico, suddividendo la quantità caricata e il costo unitario di carico, quindi lo scarico, anche questo diviso in quantità scaricata e valore unitario di scarico, infine appare un campo descrittivo dell'operazione. Un record di questo tipo potrebbe essere descritto utilizzando i livelli nel modo seguente:
|
Come si può comprendere dall'esempio e come già accennato in precedenza, per le porzioni di record che non si scompongono ulteriormente, si devono specificare le dimensioni, sommando le quali si ottiene la dimensione dei vari raggruppamenti e infine del record complessivo. La sintassi per rappresentare i livelli si potrebbe semplificare in questa fase nel modo seguente, dove però non si usa la notazione standard del linguaggio COBOL:
nn nome-campo [PIC[TURE] [IS] modello_della_variabile [opzioni]]. |
Ciò che non è stato descritto fino a questo punto è la parte di dichiarazione successiva al nome del campo, che inizia con la parola chiave PICTURE, spesso abbreviata soltanto con PIC. Ciò che appare qui serve a definire il modello della variabile, ovvero la sua dimensione e le sue caratteristiche.
Il modello di definizione della variabile è una stringa che va composta seguendo regole precise. Con questo modello si specifica se la variabile è di tipo numerico o alfanumerico, la sua dimensione, la presenza eventuale di una virgola (ovviamente per i valori numerici), il segno ed eventualmente una maschera di trasformazione. Dopo il modello di definizione della variabile possono apparire delle opzioni, in forma di dichiarazioni ulteriori, che servono a precisare la modalità con cui la variabile deve essere rappresentata internamente alla memoria centrale.
Quando si dichiara una variabile numerica, è importante chiarire quale rappresentazione deve avere. A seconda del compilatore, la variabile numerica potrebbe essere gestita in forma binaria oppure in forma BCD (Binary coded decimal), che a sua volta può essere «normale», dove ogni cifra occupa un byte, oppure packed, dove ogni cifra occupa mezzo byte (4 bit, noto anche come nibble). Questa caratteristica della variabile si definisce con le dichiarazioni opzionali che seguono il modello di definizione della variabile. |
Il modo in cui si dichiara il modello di definizione della variabile è descritto nella sezione 72.9, mentre per una visione complessiva del modo in cui si dichiara una variable, si deve consultare la sezione 72.6; tuttavia, in questa fase si può cominciare ugualmente a interpretare l'esempio mostrato in precedenza, osservando in particolare i campi seguenti:
il campo MM-NUMERO-REGISTRAZIONE può contenere un numero intero senza segno di cinque cifre: da zero a 99 999;
il campo MM-CARICO-QUANTITA può contenere un numero senza segno con otto cifre per la parte intera e tre cifre per la parte decimale;
il campo MM-COSTO-UNITARIO può contenere un numero senza segno con sei cifre per la parte intera e due cifre per la parte decimale;
il campo MM-DESCRIZIONE può contenere caratteri alfanumerici di qualunque tipo (nell'ambito di una rappresentazione in byte), per una dimensione di 200 caratteri.
Nell'esempio del magazzino si può notare che tutti i nomi usati per individuare le varie componenti del record sono unici, ma oltre a questo è stata usata l'accortezza di mettere un prefisso (MM-) per distinguerli rispetto a campi di altri file che potrebbero avere una struttura del record simile. Tuttavia, non è strettamente necessario che tali nomi siano univoci per tutto il programma, perché è prevista la possibilità di qualificarli in modo gerarchico. La qualificazione è descritta nella sezione 72.8.3.
Esiste anche la possibilità di ridefinire la struttura di un record, assegnando un nome alternativo a un certo livello che si vuole descrivere diversamente. Si osservi l'esempio seguente:
|
Nell'esempio si vede un record denominato MIO-RECORD, che inizialmente è composto dal campo CAMPO-A fatto per contenere 20 caratteri. Questo campo viene ridefinito nella riga successiva con il nome RIDEFINITO-A, che si articola in sottocampi, con i quali si vuole descrivere in modo alternativo la variabile CAMPO-A. In base al contesto si intende che i primi otto caratteri possano essere interpretati come le cifre numeriche di una data (anno, mese e giorno), individuando il resto come una descrizione non meglio qualificabile.
Generalmente, la ridefinizione di un campo che non è suddiviso è di scarsa utilità, mentre è più interessante quando si applica a campi che hanno già una suddivisione, che però si vuole gestire anche in modo differente:
|
In questo caso, il campo A è composto complessivamente da 20 caratteri, a cui si accede con i campi B e C per i primi 10 e gli ultimi 10 rispettivamente. La ridefinizione successiva, consente di accedere a una porzione centrale, a cavallo dei campi B e C, con il campo F.
La sezione WORKING-STORAGE SECTION serve a dichiarare le variabili, strutturate o scalari, utilizzate dal programma, che non si riferiscono direttamente alla descrizione dei record dei file:
WORKING-STORAGE SECTION. ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ .-- --. | 77-level-description-entry | | |... | record-description-entry | `-- --' |
A differenza della sezione FILE SECTION, oltre alla dichiarazione di variabili strutturate, è possibile dichiarare delle variabili scalari (non suddivisibili), utilizzando il livello speciale numero 77.
|
Il livello 77 viene dichiarato mettendo il numero relativo nella colonna dell'area A del modulo di programmazione, così come si fa per il livello 01; nello stesso modo, il nome della variabile scalare si scrive nell'area B. L'esempio che appare sopra dovrebbe essere sufficiente a comprendere l'uso della sezione WORKING-STORAGE SECTION, tenendo conto che vale quanto descritto a proposito delle variabili strutturate che descrivono i record nella sezione FILE SECTION, compresa la ridefinizione.
La dichiarazione di una variabile scalare con il livello 77 consente di specificare dei tipi numerici binari (come USAGE IS INDEX), per i quali non si può prevedere la dimensione in modo standard. L'uso di questi tipi numerici speciali non è ammesso nei campi di una variabile scalare descrittiva di un record. |
Oltre ai livelli che servono a descrivere le variabili strutturate (da 01 a 49) e le variabili scalari (77), sono previsti due livelli speciali: 66 e 88. Questi livelli speciali servono a definire dei raggruppamenti di variabili appartenenti alla stessa struttura o a definire dei «nomi di condizione».
La descrizione di questi ulteriori livelli speciali viene fatta nella sezione 72.8.
Lo schema sintattico seguente descrive la dichiarazione delle variabili per i livelli da 01 a 49 e per il livello 77:
.-- --. | data-name-1 | level-number | | [REDEFINES data-name-2] | FILLER | ¯¯¯¯¯¯¯¯¯ `--¯¯¯¯¯¯ --' .-- / \ --. | | PICTURE | | | < ¯¯¯¯¯¯¯ > IS character-string | | | PIC | | `-- \ ¯¯¯ / --' .-- / \ --. | | COMPUTATIONAL | | | | ¯¯¯¯¯¯¯¯¯¯¯¯¯ | | | | COMP | | | [USAGE IS] < ¯¯¯¯ > | | ¯¯¯¯¯ | DISPLAY | | | | ¯¯¯¯¯¯¯ | | | | INDEX | | `-- \ ¯¯¯¯¯ / --' .-- / \ --. | | LEADING | | | [SIGN IS] < ¯¯¯¯¯¯¯ > [SEPARATE CHARACTER] | | ¯¯¯¯ | TRAILING | ¯¯¯¯¯¯¯¯ | `-- \ ¯¯¯¯¯¯¯¯ / --' .-- / \ --. | | integer-2 TIMES | | | OCCURS < > | | ¯¯¯¯¯¯ | integer-1 TO integer-2 TIMES [DEPENDING ON data-name-3] | | | \ ¯¯ ¯¯¯¯¯¯¯¯¯ / | | | | .-- / \ --. | | | | ASCENDING | | | | | < ¯¯¯¯¯¯¯¯¯ > KEY IS {data-name-4}... |... | | | | DESCENDING | | | | `-- \ ¯¯¯¯¯¯¯¯¯¯ / --' | | | | [ INDEXED BY {index-name-1}... ] | `-- ¯¯¯¯¯¯¯ --' .-- / \ .-- --. --. | | SYNCHRONIZED | | LEFT | | | < ¯¯¯¯¯¯¯¯¯¯¯¯ > | ¯¯¯¯ | | | | SYNC | | RIGHT | | `-- \ ¯¯¯¯ / `--¯¯¯¯¯--' --' .-- / \ --. | | JUSTIFIED | | | < ¯¯¯¯¯¯¯¯¯ > RIGHT | | | JUST | ¯¯¯¯¯ | `-- \ ¯¯¯¯ / --' [ BLANK WHEN ZERO ] ¯¯¯¯¯ ¯¯¯¯ [ VALUE IS literal-1 ]. ¯¯¯¯¯ |
Formato per il livello 66:
.-- / \ --. | | THROUGH | | 66 data-name-1 RENAMES data-name-2 | < ¯¯¯¯¯¯¯ > data-name-3 | . ¯¯¯¯¯¯¯ | | THRU | | `-- \ ¯¯¯¯ / --' |
Formato per il livello 88:
/ \ / .-- / \ --. \ | VALUE IS | | | | THROUGH | | | 88 condition-name < ¯¯¯¯¯ > < literal-1 | < ¯¯¯¯¯¯¯ > literal-2 | >... . | VALUES ARE | | | | THRU | | | \ ¯¯¯¯¯¯ / \ `-- \ ¯¯¯¯ / --' / |
Nel primo schema, l'indicazione del nome della variabile, che si vede in alternativa alla parola chiave FILLER, deve apparire immediatamente dopo il numero di livello; inoltre, se si usa la parola chiave REDEFINES, questa segue immediatamente il nome della variabile. Il resto delle opzioni può essere inserito con l'ordine che si preferisce, rispettando comunque lo schema e l'eventuale interdipendenza che c'è tra le opzioni stesse.
L'opzione che definisce il modello di definizione della variabile, introdotta dalla parola chiave PICTURE, deve essere usata per ogni variabile che non si scompone ulteriormente e non può essere usata negli altri casi. Nello stesso modo, le opzioni SYNCHRONIZED, JUSTIFIED e BLANK WHEN ZERO si possono usare solo per le variabili terminali (che non si scompongono).
Il valore iniziale delle variabili, a eccezione del tipo INDEX, si specifica con l'opzione introdotta dalla parola chiave VALUE. Quando non si usa o non si può usare questa opzione, il valore iniziale delle variabile non è conosciuto e non può essere previsto.
La descrizione dei nomi di condizione (livello 88) è disponibile nella sezione 72.8.1, mentre per quella riguardante i raggruppamenti alternativi di variabili si può consultare la sezione 72.8.2.
La dichiarazione di una variabile (livelli da 01 a 49 e 77) avviene indicandone il nome subito dopo il numero di livello. Il nome della variable può non essere univoco nell'ambito del programma, purché sia possibile individuare correttamente la variabile attraverso la qualificazione (sezione 72.8.3).
Al posto di indicare il nome di una variabile, è possibile mettere la parola chiave FILLER, quando il campo a cui fa riferimento non viene mai utilizzato direttamente. Naturalmente, l'uso di questa parola chiave non è ammissibile in un livello 77, perché non avrebbe senso dichiarare una variabile scalare, indipendente da qualunque altra, senza avere poi la possibilità di utilizzarla nel programma.
L'opzione REDEFINES può essere usata soltanto quando si dichiara una variabile, nell'ambito dei livelli da 01 a 49 (si esclude quindi l'uso della parola chiave FILLER), allo scopo di ridefinire la struttura di un'altra variabile che si trova allo stesso livello.
|
L'esempio mostra che il campo A è composto complessivamente da 20 caratteri, a cui si accede con i campi B e C per i primi 10 e gli ultimi 10 rispettivamente. La ridefinizione successiva, consente di accedere a una porzione centrale, a cavallo dei campi B e C, con il campo F.
|
Questo esempio ulteriore mostra un piccolo programma completo, che dimostra il funzionamento della ridefinizione, visualizzando le variabili associate a RECORD-A e a RECORD-B, che ridefinisce il primo. Se si compila questo programma con TinyCOBOL o con OpenCOBOL, l'avvio dell'eseguibile che si ottiene genera il risultato seguente:
P: ABCDEFGHIJ Q: KLMNOPQRST R: ABCDE S: FGHIJKLMNO T: PQRST |
Attraverso l'opzione PICTURE, che è obbligatoria per le variabili che non si scompongono in livelli inferiori, si definisce precisamente come è fatta la variabile. L'argomento dell'opzione è una stringa che descrive la variabile.
Il modo in cui si rappresenta il modello delle variabili è molto articolato e viene descritto nella sezione 72.9.
Per il linguaggio COBOL, il contenuto di una variabile può essere solo di due tipi: alfanumerico o numerico. Una variabile dichiarata per contenere valori alfanumerici, utilizza un formato a «caratteri» (normalmente si tratta della codifica ASCII o EBCDIC), mentre per i valori numerici ci sono delle alternative. Attraverso l'opzione USAGE si può specificare il modo in cui la variabile deve contenere i dati.
/ \ | COMPUTATIONAL | | ¯¯¯¯¯¯¯¯¯¯¯¯¯ | | COMP | [USAGE IS] < ¯¯¯¯ > ¯¯¯¯¯ | DISPLAY | | ¯¯¯¯¯¯¯ | | INDEX | \ ¯¯¯¯¯ / |
Il formato definito dalla parola chiave DISPLAY corrisponde a un'organizzazione a caratteri. Si tratta evidentemente del formato necessario per le variabili alfanumeriche, ma può essere usato anche per le variabili numeriche.
Il formato COMPUTATIONAL, abbreviabile anche con COMP, può essere usato soltanto per i valori numerici e richiede l'utilizzo di 4 bit per ogni cifra numerica e anche per il segno (se specificato).
Il formato INDEX serve a dichiarare una variabile speciale, che può rappresentare soltanto numeri interi senza segno. Non si può associare un modello di definizione della variabile quando il formato è INDEX, perché la dimensione è fissa e dipende dal compilatore. L'uso di queste variabili è anche limitato a certi contesti (di solito ne è ammesso l'uso negli indici delle tabelle ed eventualmente nei contatori delle iterazioni) e queste variabili si dichiarano soltanto con un livello 77 in modo da essere svincolati da una struttura a record.
L'opzione USAGE va usata principalmente nella descrizione di variabili terminali; teoricamente si può indicare l'opzione USAGE anche per variabili che si suddividono in campi e in tal caso i livelli gerarchicamente inferiori ereditano l'opzione da quelli superiori. Tuttavia, è meglio evitare questa pratica e limitarsi a usare l'opzione USAGE soltanto sui campi terminali, tenendo conto che così i campi composti sono implicitamente di tipo DISPLAY.
Volendo assegnare a una variabile strutturata un tipo diverso da DISPLAY, si otterrebbe solamente di assegnare questa caratteristica ai livelli inferiori, perché una variabile strutturata, ammesso che la si voglia utilizzare direttamente, nel suo complesso, può funzionare soltanto come DISPLAY. Come conseguenza, una variabile strutturata può essere formata soltanto da un numero intero di byte. |
Quando si dichiarano su uno stesso livello variabili numeriche organizzate secondo il formato COMPUTATIONAL, queste utilizzano regolarmente lo spazio loro assegnato, condividendo eventualmente i byte che dovessero trovarsi a cavallo tra una e l'altra; tuttavia, nell'ambito della variabile composta che contiene questi livelli, i dati devono occupare una quantità di byte intera, pertanto si può perdere eventualmente un po' di spazio alla fine.
01 A. 01 B. 02 I. 03 P PIC 9 COMP. 03 P PIC 9 COMP. 02 J. 03 Q PIC S99 COMP. 03 Q PIC S99 COMP. 02 K. 03 R PIC 999 COMP. 03 R PIC 999 COMP. 02 L. 03 S PIC 9 COMP. 03 S PIC 9 COMP. 03 T PIC X. 03 T PIC X. ------------------------------------------------------------ 9 S 9 9 9 9 9 9 0 -X- 9 0 S 9 9 0 9 9 9 0 9 0 -X- |_|_____|_____|_| |___| |_| |_____| |_____| |_| |___| | | | | | | | | | | P Q R S T P Q R S T |_____________________| |___|_______|_______|_______| | | | | | A I J K L |___________________________| | B |
La figura dovrebbe essere sufficiente per capire come viene utilizzata la memoria per la rappresentazione delle variabili COMPUTATIONAL. Si può osservare che la variabile strutturata A ha soltanto uno spreco di una cifra COMPUTATIONAL, ovvero 4 bit, mentre la variabile B, avendo un'organizzazione differente nella sua struttura sottostante, spreca più spazio.
Molti compilatori COBOL considerano anche il tipo numerico COMPUTATIONAL-3, o COMP-3. Si tratta di una variante del tipo COMPUTATIONAL, con l'indicazione del segno o dell'assenza del segno, nel gruppo di 4 bit meno significativo (quello più a destra). |
La maggior parte dei manuali COBOL sottolinea il fatto che per eseguire delle elaborazioni numeriche (dei calcoli matematici di qualunque tipo) è bene che le variabili utilizzate siano di tipo COMPUTATIONAL, perché se le variabili sono ti tipo DISPLAY, prima di poter essere utilizzate devono essere convertite.
Quando si dichiarano variabili numeriche che prevedono l'indicazione del segno, è possibile stabilire in che modo e in quale posizione deve trovarsi, con l'opzione SIGN:
/ \ | LEADING | [SIGN IS] < ¯¯¯¯¯¯¯ > [SEPARATE CHARACTER] ¯¯¯¯ | TRAILING | ¯¯¯¯¯¯¯¯ \ ¯¯¯¯¯¯¯¯ / |
Le variabili numeriche di tipo DISPLAY, in condizioni normali, incorporano il segno nel byte più significativo. Quando si vuole richiedere che il segno occupi un byte tutto per sé, è necessario usare la parola chiave SEPARATE. Per le variabili di tipo COMPUTATIONAL il segno occupa sempre uno spazio separato rispetto a quello delle cifre numeriche.
Se si utilizza la parola chiave LEADING, il segno viene collocato a sinistra (e di norma questo è il comportamento predefinito); se invece si usa la parola chiave TRAILING, il segno viene collocato nella posizione più a destra.
La parola chiave OCCURS introduce un gruppo di informazioni che consentono di indicare che la variabile a cui fanno riferimento viene ripetuta secondo certe modalità. Attraverso questo meccanismo si creano quelle che per il COBOL sono delle tabelle.
/ \ | integer-2 TIMES | OCCURS < > ¯¯¯¯¯¯ | integer-1 TO integer-2 TIMES [DEPENDING ON data-name-3] | \ ¯¯ ¯¯¯¯¯¯¯¯¯ / .-- / \ --. | | ASCENDING | | | < ¯¯¯¯¯¯¯¯¯ > KEY IS {data-name-4}... |... | | DESCENDING | | `-- \ ¯¯¯¯¯¯¯¯¯¯ / --' [ INDEXED BY {index-name-1}... ] ¯¯¯¯¯¯¯ |
La variabile ricorrente di una tabella può ripetersi per un numero fisso di elementi (integer-2), oppure per un numero variabile, nell'ambito di un intervallo stabilito (da integer-1 a integer-2), sotto il controllo di un'altra variabile (data-name-3).
Se l'insieme degli elementi della tabella dichiarata con l'opzione OCCURS è ordinato in base a una chiave, questa può essere specificata (index-name-1); inoltre, l'indice per accedere agli elementi può essere dichiarato contestualmente già in questa fase (index-name-1).
Per maggiori dettagli, si veda la sezione 72.7, dedicata solo alle tabelle del COBOL.
L'opzione SYNCHRONIZED avrebbe lo scopo di allineare una variabile numerica nei limiti di una o più «parole», secondo l'architettura usata nell'elaboratore per il quale è realizzato il compilatore.
/ \ .-- --. | SYNCHRONIZED | | LEFT | < ¯¯¯¯¯¯¯¯¯¯¯¯ > | ¯¯¯¯ | | SYNC | | RIGHT | \ ¯¯¯¯ / `--¯¯¯¯¯--' |
Teoricamente, l'uso dell'opzione SYNCHRONIZED avrebbe lo scopo di facilitare le elaborazioni numeriche, ma si creano delle complicazioni nel modo di determinare la dimensione effettiva delle variabili, soprattutto quando si vogliono ridefinire.
Per scrivere programmi COBOL compatibili tra i vari sistemi operativi e le architetture fisiche degli elaboratori, è meglio evitare l'uso di variabili SYNCHRONIZED; tuttavia, se si preferisce comunque usare questa funzione, diventa necessario consultare il manuale specifico del proprio compilatore.
Le variabili alfabetiche o alfanumeriche possono essere dichiarate con l'opzione JUSTIFIED, per fare in modo che ricevano i dati allineandoli a destra:
/ \ | JUSTIFIED | < ¯¯¯¯¯¯¯¯¯ > RIGHT | JUST | ¯¯¯¯¯ \ ¯¯¯¯ / |
Normalmente, quando si «invia» una stringa in una variabile alfanumerica, la copia inizia da sinistra a destra: se la variabile ricevente è più piccola della stringa, questa viene troncata alla destra; se la variabile ricevente è più grande, si aggiungono alla destra degli spazi.
Quando si usa l'opzione JUSTIFIED per una variabile (ricevente), la copia di una stringa avviene con un allineamento opposto, pertanto il troncamento avviene a sinistra e così anche l'aggiunta degli spazi ulteriori.
L'opzione BLANK WHEN ZERO si può utilizzare solo per le variabili numeriche scalari, dove ogni cifra utilizza un byte intero. L'opzione fa sì che gli zeri anteriori vengano sostituiti da spazi, a meno che il modello di definizione della variabile preveda diversamente.
[ BLANK WHEN ZERO ] ¯¯¯¯¯ ¯¯¯¯ |
Convenzionalmente, una variabile che viene dichiarata nei livelli da 01 a 49 e 77, non ha inizialmente un valore prestabilito, ma solo casuale. Per stabilire un valore da attribuire a una variabile nel momento della sua creazione, si usa l'opzione VALUE:
VALUE IS literal-1 ¯¯¯¯¯ |
La costante che nello schema sintattico è indicata come literal-1, è il valore che viene attribuito inizialmente.
Si osservi che è possibile stabilire un valore iniziale per una variabile, soltanto quando si tratta di qualcosa che viene dichiarato nella sezione WORKING-STORAGE SECTION, perché nella sezione FILE SECTION ciò non è possibile e non avrebbe senso.
L'opzione VALUE si usa anche per la dichiarazione dei nomi di condizione, ma in tal caso la funzione di questa opzione ha un valore differente e non c'è più la discriminazione tra le sezioni in cui si può utilizzare.
L'opzione RENAMES, che si usa nel livello 66, permette di dichiarare delle variabili che rappresentano un raggruppamento di altre variabili, appartenenti alla stessa struttura, purché queste siano adiacenti. Nella sezione 72.8.2 viene mostrata la dichiarazione dei raggruppamenti.
Il linguaggio COBOL offre la gestione di array attraverso la definizione di variabili multiple, all'interno di variabili strutturate (record); tuttavia, la denominazione usata nel COBOL per queste rappresentazioni dei dati è di tabella.
level-number data-name-1 [omissis] / \ | integer-2 TIMES | OCCURS < > ¯¯¯¯¯¯ | integer-1 TO integer-2 TIMES [DEPENDING ON data-name-3] | \ ¯¯ ¯¯¯¯¯¯¯¯¯ / .-- / \ --. | | ASCENDING | | | < ¯¯¯¯¯¯¯¯¯ > KEY IS {data-name-4}... |... | | DESCENDING | | `-- \ ¯¯¯¯¯¯¯¯¯¯ / --' [ INDEXED BY {index-name-1}... ] ¯¯¯¯¯¯¯ [omissis] . |
Si dichiara che un campo è composto da più elementi dello stesso tipo aggiungendo in coda l'opzione OCCURS n TIMES. Lo schema sintattico completo dell'opzione è il seguente:
/ \ | integer-2 TIMES | OCCURS < > ¯¯¯¯¯¯ | integer-1 TO integer-2 TIMES [DEPENDING ON data-name-3] | \ ¯¯ ¯¯¯¯¯¯¯¯¯ / .-- / \ --. | | ASCENDING | | | < ¯¯¯¯¯¯¯¯¯ > KEY IS {data-name-4}... |... | | DESCENDING | | `-- \ ¯¯¯¯¯¯¯¯¯¯ / --' [ INDEXED BY {index-name-1}... ] ¯¯¯¯¯¯¯ |
Le tabelle più semplici sono quelle che hanno un numero fisso di elementi. Si osservi l'esempio seguente:
|
Nell'esempio viene dichiarata una variabile strutturata denominata A, che si articola nelle variabili B, C e F. La variabile C è ripetuta per 10 volte e si articola ogni volta nella variabile D e nella variabile E. La variabile E si ripete per sette volte.
La variabile E è una tabella a due dimensioni, perché è inclusa nelle ripetizioni della variabile C, mentre la variabile C è una tabella a una sola dimensione.
È evidente che per fare riferimento ai valori contenuti nelle tabelle sia necessario utilizzare un indice.
L'opzione OCCURS si può utilizzare per tutte le variabili dichiarate con un numero di livello da 02 a 49. In pratica vengono esclusi i livelli 01 (record), 66 (usato per il raggruppamento delle variabili), 77 (usato esclusivamente per le variabili scalari) e 88 (nomi di condizione). |
Lo standard del 1974 del linguaggio COBOL pone come limite un massimo di tre dimensioni per le tabelle. |
Per fare riferimento a un elemento di una tabella, nelle istruzioni della divisione PROCEDURE DIVISION si usa una forma descritta dallo schema sintattico seguente:
data-name (subscript-1 [subscript-2 [subscript-3...]]) |
In pratica, si scrive il nome della variabile ripetuta, seguita dall'indice o dagli indici tra parentesi tonde. Il primo indice riguarda la prima dimensione, intesa come quella più esterna; l'ultimo riguarda l'annidamento più interno.
L'indice è un numero intero positivo che va da uno fino al massimo della dimensione che lo riguarda. Seguendo l'esempio apparso nella sezione precedente, E (1 7) rappresenta la settima occorrenza della variabile E nell'ambito della prima della variabile C. Pertanto, il nome da usare per indicare l'elemento è quello della variabile più interna che si vuole individuare, mentre gli indici partono dalla posizione più esterna.
Si noti che è convenzione comune inserire delle virgole per separare gli indici, anche se si tratta di una forma di rappresentazione facoltativa. |
Viene mostrato un altro esempio di tabella a tre dimensioni:
|
Si tratta di una variabile strutturata che serve a contenere delle informazioni su un'enciclopedia. L'elemento VOLUME (5) contiene le informazioni su tutto il volume quinto; l'elemento TITOLO-VOLUME (5) contiene il titolo del volume quinto; l'elemento TITOLO-PARTE (5, 3) contiene il titolo della terza parte del volume quinto; l'elemento TESTO (5, 3, 25) contiene il testo del venticinquesimo capitolo contenuto nella terza parte del quinto volume. Naturalmente, in questo esempio si intende che la numerazione delle parti ricominci da uno all'inizio di ogni volume; così si intende che all'inizio di ogni parte la numerazione dei capitoli riprenda da uno.
L'indice di una tabella può essere indicato attraverso una costante numerica, una variabile numerica a cui sia stato attribuito preventivamente un valore appropriato o attraverso un'espressione elementare che risulta in un numero intero appropriato.
Quando si usa una variabile per la gestione di un indice, è possibile ed è consigliabile che il tipo numerico di tale variabile sia INDEX. In pratica, nella sezione WORKING-STORAGE SECTION un indice potrebbe essere dichiarato come nell'esempio seguente, dove se ne vedono due, il primo, denominato INDICE, è dichiarato come variabile scalare di livello 77, il secondo, denominato INDICE-C, è sempre costituito da una variabile scalare, che però fa parte di una variabile strutturata:
|
Si può osservare che questo tipo di variabile numerica non prevede la definizione della sua dimensione che è stabilita invece dal compilatore, in base alle caratteristiche dell'elaboratore e del sistema operativo.
In alternativa, l'indice può essere dichiarato contestualmente alla dichiarazione della variabile ricorrente; in tal caso, il compilatore può aggiungere dei controlli tali da impedire che si possa assegnare alla variabile un valore al di fuori dell'ambito della dimensione della tabella:
|
Qui viene ripreso e modificato un esempio già apparso in una sezione precedente. La differenza consiste nell'assegnare l'indice a ogni variabile ricorrente. Pertanto si hanno gli indici: IND-VOLUME, IND-PARTE e IND-CAPITOLO.
Come accennato, si può fare riferimento a un elemento di una tabella indicando un indice costituito da un'espressione matematica elementare. Si parla in questo caso di indici relativi, perché si possono sommare o sottrarre dei valori a partire da una posizione di partenza. Per esempio, supponendo che la variabile I contenga il numero 15, l'elemento indicato come ELE (I - 4) corrisponderebbe alla notazione ELE (11); nello stesso modo, l'elemento indicato come ELE (I + 4) corrisponderebbe alla notazione ELE (19).
Teoricamente, è possibile dichiarare l'occorrenza di una variabile per una quantità variabile di elementi; si usa in tal caso la forma OCCURS m TO n TIMES. A seconda del compilatore, può essere obbligatorio, o facoltativo, specificare il nome di una variabile che controlla dinamicamente la quantità massima di elementi:
OCCURS integer-1 TO integer-2 TIMES [DEPENDING ON data-name-3] ¯¯¯¯¯¯ ¯¯ ¯¯¯¯¯¯¯¯¯ |
Viene mostrato l'esempio di un programma completo, che serve ad accumulare in una tabella alcuni dati personali. Sono previsti un massimo di 60 elementi e la quantità effettiva di elementi è controllata dalla variabile UTENTI-MAX:
|
Se si devono utilizzare i dati in una tabella per eseguire una ricerca al suo interno (utilizzando l'istruzione SEARCH nella divisione PROCEDURE DIVISION), se si può essere certi che le informazioni contenute siano ordinate secondo una certa chiave, lo si può specificare nella dichiarazione:
/ \ | integer-2 TIMES | OCCURS < > ¯¯¯¯¯¯ | integer-1 TO integer-2 TIMES [DEPENDING ON data-name-3] | \ ¯¯ ¯¯¯¯¯¯¯¯¯ / .-- / \ --. | | ASCENDING | | | < ¯¯¯¯¯¯¯¯¯ > KEY IS {data-name-4}... |... | | DESCENDING | | `-- \ ¯¯¯¯¯¯¯¯¯¯ / --' [ INDEXED BY {index-name-1}... ] ¯¯¯¯¯¯¯ |
La metavariabile data-name-4 dello schema sintattico rappresenta una variabile contenuta nell'elemento ricorrente; attraverso la parola chiave ASCENDING si intende dichiarare che la tabella è ordinata, lessicograficamente, in modo ascendente, secondo il contenuto di quella variabile, se invece si usa la parola chiave DESCENDING, si intende un ordinamento decrescente.
È possibile specificare più chiavi di ordinamento successive, nel caso si vogliano abbinare chiavi secondarie di ordinamento.
Sia chiaro che la tabella deve già risultare ordinata secondo le chiavi specificate, altrimenti le istruzioni SEARCH della divisione PROCEDURE DIVISION danno risultati errati o falliscono semplicemente. Naturalmente, all'interno del programma è possibile prevedere un procedimento di riordino, da eseguire prima di utilizzare delle istruzioni SEARCH. |
L'esempio seguente mostra l'indicazione della chiave di ordinamento, costituita precisamente dalla variabile COGNOME, che deve risultare ascendente in fase di ricerca:
|
Il linguaggio COBOL prevede un'istruzione apposita per facilitare la scansione delle tabelle. Si tratta di SEARCH, che ha due modalità di funzionamento, a seconda che si voglia eseguire una ricerca sequenziale o una ricerca binaria. Naturalmente, la ricerca sequenziale si presta alla scansione di una tabella i cui dati non sono ordinati, mentre nel secondo caso devono esserlo.
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.
L'esempio seguente mostra una variante del programma già descritto, in cui si vuole eseguire una ricerca binaria. Perché la ricerca possa avere successo, la tabella deve essere dichiarata con una dimensione variabile di elementi, inoltre non è più necessario impostare il valore iniziale dell'indice, prima della scansione.
|
La ricerca binaria richiede che gli elementi della tabella siano ordinati in base alla chiave primaria; pertanto, si presume che l'inserimento dei cognomi avvenga tenendo conto dell'ordine lessicografico.
Per rappresentare in modo immediato una condizione che verifichi la corrispondenza tra il contenuto di una variabile e un insieme di valori prestabiliti, si possono utilizzare i nomi di condizione. I nomi di condizione sono sostanzialmente delle costanti che descrivono un'espressione condizionale di questo tipo e si dichiarano con il numero di livello 88.
Le variabili possono essere raggruppate diversamente, purché si trovino all'interno di una stessa variabile strutturata e siano adiacenti.
I nomi dati alle variabili e ad altri oggetti del programma, in certe situazioni possono richiedere la «qualificazione», che consiste nello specificare il contesto al quale si riferiscono.
Attraverso il livello 88 è possibile definire una costante speciale, con lo scopo di rappresentare la possibilità che una variabile contenga certi valori. In pratica, si dichiara una variabile, con i livelli da 01 a 49 o anche con il livello 77. A questa variabile si abbinano una o più costanti dichiarate con il livello 88, che descrivono la possibilità che la variabile a cui si riferiscono contenga un certo insieme di valori. Le costanti di livello 88, dichiarate in questo modo, si usano poi nelle condizioni.
/ \ / .-- / \ --. \ | VALUE IS | | | | THROUGH | | | 88 condition-name < ¯¯¯¯¯ > < literal-1 | < ¯¯¯¯¯¯¯ > literal-2 | >... . | VALUES ARE | | | | THRU | | | \ ¯¯¯¯¯¯ / \ `-- \ ¯¯¯¯ / --' / |
Nello schema sintattico si vede soltanto la dichiarazione del livello 88, ma si deve tenere conto che si tratta di istruzioni che vanno collocate a partire dalla riga successiva a quella di dichiarazione della variabile a cui si riferiscono.
La parola chiave THROUGH o THRU si usa per specificare un intervallo di valori (dalla costante literal-1 a literal-2. Il valore singolo o l'intervallo di valori, può essere seguito da altri.
Nell'esempio seguente, si vede la dichiarazione della variabile VERNICIATURA che può contenere una stringa di 30 caratteri (alfanumerica). A questa variabile si associano due nomi di condizione, VERNICE-CHIARA e VERNICE-SCURA, che servono a definire due gruppi di colori, descritti per nome. Da questo si intuisce che nella parte procedurale del programma venga attribuito alla variabile VERNICIATURA il nome di un colore (scritto con lettere maiuscole); poi, per verificare il tipo di colore si può usare uno di questi nomi di condizione, per esprimere il fatto che la variabile contenga uno dei nomi del gruppo a cui quel nome fa riferimento.
|
L'esempio seguente descrive la variabile CODICE che può contenere una sola cifra numerica. A questa variabile si associano dei nomi di condizione, che descrivono raggruppamenti diversi delle cifre che possono essere contenute nella variabile.
|
Nella divisione PROCEDURE DIVISION potrebbero apparire righe come quelle successive, per verificare che la variabile CODICE contenga certi valori:
|
In questo modo si evita di scrivere un'espressione condizionale complessa come nell'esempio seguente:
|
I nomi di condizione si possono associare a variabili che hanno un contenuto alfabetico, alfanumerico e numerico, ma nell'ultimo caso, deve trattarsi di valori rappresentati in forma di stringhe di caratteri (pertanto sono escluse le rappresentazioni compatte che usano quattro bit per cifra e quelle binarie).
Un nome di condizione può essere associato a una variabile che costituisce una tabella, oppure che è contenuta in una struttura tabellare. Per fare riferimento al nome di condizione nella cella giusta, occorre utilizzare gli indici, così come si fa normalmente in queste situazioni.
Il livello 66 permette di raggruppare alcune variabili adiacenti di una stessa struttura:
.-- / \ --. | | THROUGH | | 66 data-name-1 RENAMES data-name-2 | < ¯¯¯¯¯¯¯ > data-name-3 | . ¯¯¯¯¯¯¯ | | THRU | | `-- \ ¯¯¯¯ / --' |
Dopo il numero di livello 66 viene dichiarato il nome del raggruppamento, quindi, dopo la parola chiave RENAMES, si indica il nome della prima variabile; eventualmente, dopo la parola chiave THROUGH o THRU, si indica l'ultima variabile da riunire. Si osservi l'esempio seguente:
|
Il disegno successivo, anche se non proporzionato rispetto alla dimensione delle variabili, mostra a cosa si riferiscono i vari raggruppamenti:
I nomi usati nel linguaggio COBOL, per le variabili e per la delimitazione di porzioni del contenuto della divisione PROCEDURE DIVISION hanno valore per tutta l'estensione del programma; pertanto, in generale, non possono esserci nomi doppi e non si creano ambiti di funzionamento ristretti.
Dal momento che i nomi usati per dichiarare le variabili, oltre che le sezioni e i paragrafi della divisione PROCEDURE DIVISION, hanno una lunghezza limitata normalmente a soli 30 caratteri, in un programma abbastanza grande si può porre il problema di riuscire a scrivere nomi abbastanza mnemonici e univoci. Per questo, è possibile riutilizzare certi nomi, purché poi questi siano indicati attraverso la qualificazione del loro contesto. Il contesto che consente di qualificare un nome è costituito da una gerarchia di nomi; si osservi l'esempio seguente:
|
Si vede la dichiarazione del file SALES-FILE, dove la variabile SALES-RECORD ne rappresenta il record, che a sua volta è suddiviso in una serie di campi. La variabile TYPE, di questo esempio, appartiene gerarchicamente alla variabile SALES-RECORD, che a sua volta appartiene al file SALES-FILE.
Supponendo che in questo programma, per qualche ragione, ci sia un'altra variabile con il nome TYPE, si potrebbe individuare quella abbinata all'esempio specificando che si tratta della variabile TYPE di SALES-RECORD; ma volendo supporre che ci siano anche diverse variabili SALES-RECORD, contenenti un campo denominato TYPE, occorrerebbe indicare la variabile TYPE di SALES-RECORD di SALES-FILE.
Nella divisione PROCEDURE DIVISION può succedere qualcosa di simile, quando si usa una suddivisione delle procedure in sezioni e paragrafi. In questo modo, i nomi dei paragrafi potrebbero richiedere una qualificazione, specificando a quale sezione appartengono.
Ciò che consente di qualificare un nome, in modo sufficiente a renderlo univoco, è il qualificatore. |
Quando nella divisione PROCEDURE DIVISION si usa un nome che richiede una qualificazione, si usa indifferentemente la parola chiave IN oppure OF:
... TYPE IN SALES-RECORDS ... ... TYPE IN SALES-RECORDS OF SALES-FILE ... |
Segue lo schema sintattico per qualificare una variabile:
/ \ .-- / \ --. .-- / \ --. | data-name-1 | | | IN | | | | IN | | < > | < ¯¯ > data-name-2 |... | < ¯¯ > file-name | | condition-name | | | OF | | | | OF | | \ / `-- \ ¯¯ / --' `-- \ ¯¯ / --' |
Segue lo schema sintattico per qualificare un paragrafo all'interno della divisione PROCEDURE DIVISION:
.-- / \ --. | | IN | | paragraph-name | < ¯¯ > section-name | | | OF | | `-- \ ¯¯ / --' |
Quando si deve usare la qualificazione per individuare un elemento di una tabella, gli indici tra parentesi tonde appaiono alla fine, dopo la qualificazione stessa. Seguono due modelli sintattici alternativi che descrivono il modo di rappresentare un elemento di una tabella, tenendo conto anche della qualificazione:
.-- / \ --. | | IN | | data-name-1 | < ¯¯ > data-name-2 |... [(subscript...)] | | OF | | `-- \ ¯¯ / --' |
.-- / \ --. / literal-1 \ | | IN | | | | data-name-1 | < ¯¯ > data-name-2 |... ( < index-name-1 [+ literal-2] >... ) | | OF | | | | `-- \ ¯¯ / --' \ index-name-1 [- literal-2] / |
Le regole che stabiliscono la possibilità o meno di usare la qualificazione, sono intuitive e non vengono descritte qui nel dettaglio. La regola generale da seguire è quella della leggibilità del programma sorgente, che richiede di usare la qualificazione solo quando questa è utile per migliorare la chiarezza dello stesso.
Il COBOL gestisce soltanto due tipi di dati: numerici e alfanumerici. Tuttavia, nel momento in cui si dichiara il modello, le variabili scalari (ovvero quelle che non sono scomposte) si distinguono in classi e in categorie, secondo lo schema che si vede nella tabella successiva.
Le variabili che appartengono alla classe numerica contengono dei valori che si possono utilizzare per eseguire delle espressioni numeriche; mentre quelle che appartengono alla classe alfanumerica si possono utilizzare soltanto come sequenze di caratteri.
Si osservi che una variabile strutturata è sempre solo alfanumerica, ma ciò non toglie che i campi che contiene possano essere di una classe e di una categoria differente. |
Le variabili che appartengono alle categorie edited, «modificate», servono per ricevere un valore, numerico o alfanumerico, da modificare nell'atto stesso della ricezione. Una variabile di questo tipo fa sempre parte della classe alfanumerica, perché, una volta ricevuti i dati e modificati in base al modello di definizione della variabile, questi sono semplicemente una sequenza di caratteri senza più alcun valore numerico.
Il modello di definizione della variabile è introdotto dalla parola chiave PICTURE, o PIC:
/ \ | PICTURE | < ¯¯¯¯¯¯¯ > IS character-string | PIC | \ ¯¯¯ / |
La metavariabile character-string costituisce il modello vero e proprio, che può essere composto da un massimo di 30 caratteri, anche se in questo modo si possono comunque rappresentare modelli di variabili di dimensioni molto più grandi.
Si osservi che l'indicazione di un modello di definizione della variabile è obbligatorio per tutte le variabili scalari (pertanto riguarda potenzialmente i livelli da 01 a 49 e 77), mentre non è consentito per le variabili strutturate.
La stringa che costituisce il modello di definizione della variabile è composta di simboli che descrivono ognuno le caratteristiche di un carattere, di una cifra, di un segno, oppure inseriscono qualcosa che nel dato originale è assente, ma spesso possono essere ripetuti mettendo un numero intero tra parentesi:
x(n) |
Una notazione del genere indica che il simbolo x va inteso come se fosse ripetuto n volte; per esempio, 9(3)V9(5) è equivalente a 999V99999. È attraverso questo meccanismo che si possono descrivere variabili molto grandi, pur avendo un modello di definizione limitato a soli 30 caratteri.
Nelle sezioni successive vengono descritti i simboli utilizzabili per i modelli di definizione delle variabili scalari. Lo standard del linguaggio COBOL annovera più possibilità di quelle indicate, ma i compilatori non si comportano sempre allo stesso modo e spesso ignorano l'uso di alcuni simboli, oppure li interpretano in modo inesatto. Questo problema riguarda soprattutto i simboli che applicano delle modifiche al valore ricevuto nella variabile, che quindi vanno usati con estrema parsimonia se si vuole scrivere un programma abbastanza compatibile. |
Sono variabili alfanumeriche pure e semplici quelle che possono rappresentare qualunque carattere delle codifica prevista, limitatamente al fatto che la rappresentazione è in forma di byte (ASCII o EBCDIC). Il modello di definizione di una variabile alfanumerica prevede simboli speciali che limitano l'inserimento di soli caratteri alfabetici o di sole cifre numeriche, ma in pratica, succede spesso che i compilatori non applichino alcuna restrizione.
|
Si osservi che il modello di definizione di una variabile alfanumerica deve contenere almeno un simbolo X o A, altrimenti, un modello che contenga soltanto il simbolo 9 verrebbe interpretato come numerico.
A titolo di esempio, viene mostrato un piccolo programma con tre variabili scalari alfanumeriche, aventi modelli diversi, abbinate ognuna a una variabile strutturata. Alle variabili scalari viene assegnato lo stesso valore, in modo da poter confrontare come questo valore viene inteso e rappresentato.
|
Compilando il programma con TinyCOBOL, l'avvio dell'eseguibile che si ottiene genera il risultato seguente, dove si può vedere che l'uso dei simboli A e 9 non comporta alcuna differenza di funzionamento rispetto a X; tuttavia, un compilatore più sofisticato potrebbe segnalare qualche tipo di errore:
SOURCE VALUE IS: " 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" PICTURE: X(15) VALUE: " 1234567890ABCD" DATA: " 1234567890ABCD" PICTURE: 9(5)A(5)X(5) VALUE: " 1234567890ABCD" DATA: " 1234567890ABCD" PICTURE: A(5)X(5)9(5) VALUE: " 1234567890ABCD" DATA: " 1234567890ABCD" |
Il modello di definizione di una variabile alfanumerica può contenere simboli che applicano una modifica al valore stesso. La modifica avviene nel momento in cui il valore viene ricevuto.
|
A titolo di esempio, viene mostrato un piccolo programma con due variabili scalari alfanumeriche, aventi modelli diversi, abbinate ognuna a una variabile strutturata. Alle variabili scalari viene assegnato lo stesso valore, in modo da poter confrontare come questo viene inteso e rappresentato. Nell'esempio si tenta in particolare di inserire in un modello una barra obliqua e una virgola.
|
Compilando il programma con TinyCOBOL, l'avvio dell'eseguibile che si ottiene genera il risultato seguente:
SOURCE VALUE IS: "ABCDEFGHIJKLMNOPQRSTUVWXYZ" PICTURE: X(15) VALUE: "ABCDEFGHIJKLMNO" DATA: "ABCDEFGHIJKLMNO" PICTURE: ABX09,/X(8) VALUE: "A B0C,/DEFGHIJK" DATA: "A B0C,/DEFGHIJK" |
Le variabili numeriche pure e semplici, sono quelle il cui valore può essere usato per calcolare delle espressioni numeriche. Nel modello di definizione di una variabile di questo tipo, possono apparire solo simboli che descrivono la dimensione del valore rappresentabile, distinguendo la parte intera da quella decimale e specificando eventualmente la presenza del segno.
|
A titolo di esempio, viene mostrato un piccolo programma con cinque variabili scalari numeriche, aventi modelli diversi, abbinate ognuna a una variabile strutturata. Alle variabili scalari viene assegnato lo stesso valore, in modo da poter confrontare come questo valore viene inteso e rappresentato. Nell'esempio appare anche l'uso del simbolo P a dimostrazione della difficoltà che questo comporta nell'interpretare l'esito degli assegnamenti alle variabili.
|
Compilando il programma con TinyCOBOL, l'avvio dell'eseguibile che si ottiene genera il risultato seguente:
SOURCE VALUE IS -1234.5678 PICTURE: 99999 VALUE: 01234 DATA: 01234 PICTURE: 999V99 VALUE: 234.56 DATA: 23456 PICTURE: S999V99 VALUE: -234.56 DATA: 2345O PICTURE: 999V99PP VALUE: 004.5678 DATA: 45678 PICTURE: PP99999 VALUE: .0078000 DATA: 78000 |
Facendo la stessa cosa con OpenCOBOL:
SOURCE VALUE IS -1234.5678 PICTURE: 99999 VALUE: 01234 DATA: 01234 PICTURE: 999V99 VALUE: 234.56 DATA: 23456 PICTURE: S999V99 VALUE: -234.56 DATA: 2345v PICTURE: 999V99PP VALUE: 004.5678 DATA: 45678 PICTURE: PP99999 VALUE: .0078000 DATA: 78000 |
Si osservi che nell'esempio le variabili scalari numeriche sono state dichiarate con l'opzione USAGE IS DISPLAY, che comunque sarebbe stata implicita, in modo da assicurare la visibilità del contenuto leggendo il livello 01.
Il modello di definizione di una variabile fatta per ricevere un valore numerico, può contenere simboli che applicano una modifica all'apparenza del valore. Nel momento in cui una variabile del genere riceve un valore, ciò che la variabile fornisce è un'informazione alfanumerica, che non può più essere usata per elaborazioni matematiche; al massimo, una variabile del genere può ricevere il risultato di un'espressione che generi un valore numerico.
|
Come esempio viene mostrato un piccolo programma con alcune variabili scalari numeriche modificate (edited), aventi modelli diversi, abbinate ognuna a una variabile strutturata. Alle variabili scalari viene assegnato lo stesso valore, in modo da poter confrontare come questo valore viene inteso e rappresentato.
|
Compilando il programma con TinyCOBOL, l'avvio dell'eseguibile che si ottiene genera il risultato seguente:
SOURCE VALUE IS: +123456.789 PICTURE: S9(10)V9(5) VALUE: 0000123456.78900 DATA: 00001234567890{ PICTURE: +Z(9)9.9(5) VALUE: + 123456.78900 DATA: + 123456.78900 PICTURE: CR+Z(7)9.9(5) VALUE: CR+ 123456.78900 DATA: CR+ 123456.78900 PICTURE: +Z(7)9.9(5)DB VALUE: + 123456.78900DB DATA: + 123456.78900DB PICTURE: *(9)9.9(5)+ VALUE: ****123456.78900+ DATA: ****123456.78900+ PICTURE: +*(9)9.9(4)$ VALUE: +****123456.7890$ DATA: +****123456.7890$ PICTURE: +*(9)9,9(4)$ VALUE: +********12,3456$ DATA: +********12,3456$ PICTURE: -(10)9,9(4)$ VALUE: 12,3456$ DATA: 12,3456$ PICTURE: +(10)9,9(4)$ VALUE: +12,3456$ DATA: +12,3456$ |
Tra i vari risultati, si può osservare che la virgola è stata interpretata come un segno senza un ruolo preciso, pertanto si colloca semplicemente prima delle ultime quattro cifre, secondo la previsione del modello.
Intervenendo nella sezione CONFIGURATION SECTION è possibile invertire il ruolo del punto e della virgola, nella rappresentazione dei numeri; nello stesso modo, è possibile attribuire un simbolo differente per la valuta. L'esempio seguente è una variante di quello appena mostrato, con le modifiche necessarie per questo scopo. Si osservi che come simbolo di valuta è stata scelta la lettera «E».
|
Compilando il programma con TinyCOBOL, l'avvio dell'eseguibile che si ottiene genera il risultato seguente:
SOURCE VALUE IS: +123456.789 PICTURE: S9(10)V9(5) VALUE: 0000123456,78900 DATA: 00001234567890{ PICTURE: +Z(9)9.9(5) VALUE: + 1.23456 DATA: + 1.23456 PICTURE: CR+Z(7)9.9(5) VALUE: CR+ 1.23456 DATA: CR+ 1.23456 PICTURE: +Z(7)9.9(5)DB VALUE: + 1.23456DB DATA: + 1.23456DB PICTURE: *(9)9.9(5)+ VALUE: *********1.23456+ DATA: *********1.23456+ PICTURE: +*(9)9.9(4)E VALUE: +********12.3456E DATA: +********12.3456E PICTURE: +*(9)9,9(4)E VALUE: +****123456,7890E DATA: +****123456,7890E PICTURE: -(10)9,9(4)E VALUE: 123456,7890E DATA: 123456,7890E PICTURE: +(10)9,9(4)E VALUE: +123456,7890E DATA: +123456,7890E |
Questa volta si può osservare che nel modello è il punto che perde il suo significato, apparendo nel risultato soltanto nella posizione prevista, allineando le cifre numeriche originali alla destra.
Lo standard COBOL del 1985 prevede sostanzialmente che si possano gestire informazioni alfanumeriche composte di simboli rappresentabili in byte, appartenenti pertanto al codice ASCII o EBCDIC. Con l'introduzione dell'insieme di caratteri universale, si pone il problema di gestire codifiche di tipo diverso, ben più grandi del solito byte. Purtroppo, però, le soluzioni adottate non sono semplici e lineari come nel passato.
Le stringhe letterali che devono contenere simboli al di fuori del codice ASCII o EBCDIC, devono essere delimitate in modo differente. Generalmente sono disponibili due forme:
N"stringa_letterale" |
NX"stringa_esadecimale" |
La prima forma riguarda una stringa letterale composta secondo la forma codificata del carattere prevista dal compilatore (UTF-8, UTF-16 o altro); la seconda, ammesso che sia disponibile, viene espressa attraverso cifre esadecimali. Si osservi, però, che per poter esprimere una stringa del genere in forma esadecimale, occorre sapere in che modo il compilatore la interpreta, dato che dipende dalla forma codificata del carattere adottata.
Nel modello di definizione di una variabile, la lettera N rappresenta un carattere espresso secondo la codifica universale; la lettera «N» sta per National. Pertanto, si aggiunge anche una voce nuova all'opzione USAGE: USAGE IS NATIONAL.
A seconda della forma codificata del carattere adottata dal compilatore, cambia la dimensione di una variabile del genere. Se si utilizzano codifiche del tipo UTF-8, che hanno una lunghezza variabile, può diventare impossibile stabilire in anticipo la dimensione in byte corrispondente. Anche per questo motivo, è improbabile che si possa usare lo standard UTF-8 con il COBOL.
Tra le costanti figurative, HIGH-VALUES e LOW-VALUES perdono di significato, se associate a una variabile dichiarata come USAGE IS NATIONAL.
La divisione PROCEDURE DIVISION costituisce la quarta e ultima parte di un programma sorgente COBOL. La divisione si può suddividere in paragrafi, oppure in sezioni contenenti eventualmente dei paragrafi. All'interno delle sezioni o dei paragrafi, si inseriscono le istruzioni che descrivono la procedura del programma.
Le istruzioni sono inserite a gruppi, terminanti con un punto fermo, seguito da uno spazio; le istruzioni singole, che non costituiscono un gruppo autonomo, possono essere separate graficamente attraverso dei separatori (la virgola, il punto e virgola, la parola THEN).
Alcune istruzioni, quando non costituiscono un gruppo autonomo, possono collocarsi solo alla fine del gruppo. Si tratta precisamente di GO TO e di STOP RUN. |
La divisione può articolarsi in tre modi diversi; quello che si vede descritto nello schema segue è il più semplice, perché non fa uso delle sezioni:
[PROCEDURE DIVISION. ¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯ {paragraph-name. [sentence]...}... |
Se si usano le sezioni, i paragrafi devono essere contenuti tutti all'interno di sezioni:
[PROCEDURE DIVISION. ¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯ / section-name SECTION [segment-number]. \ | ¯¯¯¯¯¯¯ | < {paragraph-name. >... | [sentence]...}... | \ / |
Eventualmente ci può essere un gruppo iniziale di sezioni speciali; in tal caso, è obbligatorio suddividere il resto del programma in sezioni:
[PROCEDURE DIVISION. ¯¯¯¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯ DECLARATIVES. ¯¯¯¯¯¯¯¯¯¯¯¯ / section-name SECTION [segment-number]. \ | ¯¯¯¯¯¯¯ | | USE statement | < ¯¯¯ >... | {paragraph-name. | | [sentence]...}... | \ / END DECLARATIVES. ¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯ / section-name SECTION [segment-number]. \ | ¯¯¯¯¯¯¯ | < {paragraph-name. >... | [sentence]...}... | \ / |
Il primo gruppo di istruzioni a essere eseguito è quello che si trova nel primo paragrafo della prima sezione; escludendo quelli inseriti in un blocco DECLARATIVES. In condizioni normali, la sequenza dei gruppi di istruzioni eseguiti prosegue con quelli successivi, salvo quando si incontrano istruzioni speciali che richiedono esplicitamente di modificare questo tipo di flusso.
Quando all'inizio della divisione PROCEDURE DIVISION appare la parola chiave DECLARATIVES, che inizia dall'area A del modulo di programmazione, le sezioni dichiarate fino alla riga dove appare END DECLARATIVES (sempre a partire dall'area A), non vengono eseguite normalmente, ma solo al verificarsi di certe condizioni.
DECLARATIVES. ¯¯¯¯¯¯¯¯¯¯¯¯ / section-name SECTION [segment-number]. \ | ¯¯¯¯¯¯¯ | | USE statement | < ¯¯¯ >... | {paragraph-name. | | [sentence]...}... | \ / END DECLARATIVES. ¯¯¯ ¯¯¯¯¯¯¯¯¯¯¯¯ |
Ogni sezione di questo gruppo speciale, inizia con una o più istruzioni USE, prima di procedere con dei paragrafi contenenti altre istruzioni. L'istruzione USE serve ad abbinare l'esecuzione della sezione (a partire dal primo dei suoi paragrafi), a condizione che si verifichi una certa condizione:
/ {file-name}... \ | | / \ | INPUT | | EXCEPTION | | ¯¯¯¯¯ | USE AFTER STANDARD < ¯¯¯¯¯¯¯¯¯ > PROCEDURE ON < OUTPUT > ¯¯¯ ¯¯¯¯¯ | ERROR | ¯¯¯¯¯¯¯¯¯ | ¯¯¯¯¯¯ | \ ¯¯¯¯¯ / | I-O | | ¯¯¯ | \ EXTEND / ¯¯¯¯¯¯ |
Tenendo conto che le parole chiave EXCEPTION e ERROR del modello sono equivalenti, si intende che questa istruzione serve ad attivare la sezione che la contiene se si verifica una condizione di errore, che non sia stato gestito diversamente all'interno del programma, riguardante: un certo file (file-name), un file qualunque aperto in lettura (INPUT), scrittura (OUTPUT), lettura e scrittura (I-O) o in estensione (EXTEND).
Viene mostrato l'esempio di un piccolo programma completo, che ha lo scopo di leggere un file (input.txt
) e di mostrarne il contenuto sullo schermo:
|
Si può osservare nel programma che il ciclo di lettura non termina mai, perché la condizione 0 = 1 non si può avverare. Così facendo, dato che la lettura non prevede alcun controllo del superamento della fine del file, si verifica un errore che viene preso in considerazione dalla sezione FILE-ACCESS-ERROR.
Compilando il programma con OpenCOBOL, l'avvio dell'eseguibile che si ottiene genera un risultato simile a quello seguente:
aaaaaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbbb ccccccccccccccccccc FILE ACCESS ERROR: 10 |
In pratica, alla fine del file termina la visualizzazione del suo contenuto e si ottiene un messaggio di errore, come organizzato nella sezione FILE-ACCESS-ERROR.
Le sezioni della divisione PROCEDURE DIVISION, oltre al nome possono indicare un numero di segmento, che può andare da zero a 99.
section-name SECTION [segment-number]. |
Il numero di segmento serve a raggruppare tutte le sezioni con lo stesso numero in uno stesso segmento, allo scopo di sapere, quale parte del programma deve rimanere simultaneamente nella memoria centrale durante il funzionamento.
Si dividono precisamente due tipi di segmenti: quelli fissi, con numeri da 00 a 49, e quelli indipendenti, da 50 a 99. I segmenti numerati fino al numero 49 devono rimanere sempre in memoria, mentre gli altri devono esserci solo per il tempo necessario al loro funzionamento. Per questa ragione, le sezioni dichiarate nella zona DECLARATIVES, possono essere associate soltanto a segmenti fissi (da 00 a 49).
Naturalmente, questa possibilità di segmentare il programma dipende dal compilatore, che potrebbe limitarsi semplicemente a ignorare il numero di segmento.
Un gruppo di istruzioni si evidenzia per la presenza del punto fermo conclusivo (seguito da uno spazio). Le istruzioni che non costituiscono gruppi singoli possono essere separate, oltre che con lo spazio, con la virgola, il punto e virgola, e con la parola THEN.
Le istruzioni condizionali sono quelle che alterano la sequenza normale dell'esecuzione delle istruzioni, sulla base della verifica di una condizione. L'istruzione condizionale tipica è IF, ma molte altre istruzioni prevedono delle parole opzionali per descrivere un'azione da compiere al verificarsi di una certa condizione.
/ \ .-- / \ --. | { statement-1 }... | | | { statement-2 }... | | IF condition-1 < > | ELSE < > | ¯¯ | NEXT SENTENCE | | ¯¯¯¯ | NEXT SENTENCE | | \ ¯¯¯¯ ¯¯¯¯¯¯¯¯ / `-- \ ¯¯¯¯ ¯¯¯¯¯¯¯¯ / --' |
Quello che si vede sopra è lo schema sintattico dell'istruzione IF, che incorpora a sua volta altre istruzioni. Naturalmente, le istruzioni incorporate possono contenere altre istruzioni condizionali annidate; in ogni caso, non è possibile suddividere una struttura del genere in gruppi di istruzioni più piccoli, pertanto il punto fermo finale può apparire solo alla fine della struttura più esterna.
|
L'esempio mostra un'istruzione IF annidata, dove sono stati usati i vari separatori disponibili, per facilitare la lettura: la parola THEN non fa parte dell'istruzione, ma introduce qui le istruzioni da eseguire nel caso la condizione si avveri; la virgola viene usata per terminare le istruzioni singole, mentre il punto e virgola si usa per concludere quelle istruzioni dopo le quali si passa all'alternativa (introdotta dalla parola chiave ELSE).
Il punto fermo finale è molto importante, perché rappresenta l'unico modo per stabilire dove finisca tutta la struttura, dal momento che nel linguaggio non è previsto l'uso di parole come «end if».
Quando la parte procedurale del programma si suddivide in sezioni, i nomi dei paragrafi devono essere univoci soltanto nell'ambito della sezione in cui vengono dichiarati.
Quando si deve fare riferimento al nome di un paragrafo che non è unico nel programma, si deve usare la qualificazione per distinguere a quale sezione si sta facendo riferimento; eccezionalmente, se si tratta della sezione in cui ci si trova già, la qualificazione è implicita.
La qualificazione si ottiene aggiungendo la parola OF, oppure IN, seguita dal nome della sezione.
/ \ | IN | paragraph-name < ¯¯ > section-name | OF | \ ¯¯ / |
L'espressione aritmetica è ciò che si traduce in un valore numerico, eventualmente attraverso l'uso di operatori. Gli operatori aritmetici disponibili nel linguaggio COBOL sono molto pochi, limitando le possibilità alle quattro operazioni.
È importante osservare che gli operatori aritmetici, tranne nel caso delle parentesi, vanno separati dai loro argomenti; diversamente, il segno - verrebbe confuso come carattere che compone una parola. Per esempio, A - B è un'espressione che rappresenta una sottrazione, mentre A-B è una parola. |
|
L'ordine di precedenza nelle espressioni aritmetiche è quello consueto: prima gli operatori unari, che si applicano a un operando singolo, poi la moltiplicazione e la divisione, quindi la somma e la sottrazione.
Nel linguaggio COBOL si distinguono diversi tipi di espressioni condizionali elementari, che vengono descritte nelle sezioni successive. Le espressioni elementari, a loro volta, si possono combinare in espressioni composte, con l'uso di operatori booleani ed eventualmente con l'aiuto di parentesi tonde per modificare l'ordine di valutazione.
Le condizioni di relazione stabiliscono un confronto tra due valori, che possono essere rappresentati da variabili, costanti o da espressioni aritmetiche. Segue lo schema sintattico:
/ IS [NOT] GREATER THAN \ | ¯¯¯ ¯¯¯¯¯¯¯ | | IS [NOT] LESS THAN | / identifier-1 \ | ¯¯¯ ¯¯¯¯ | / identifier-2 \ | | | IS [NOT] EQUAL TO | | | < literal-1 > < ¯¯¯ ¯¯¯¯¯ > < literal-2 > | | | IS [NOT] > | | | \ arith-expression-1 / | ¯¯¯ ¯ | \ arith-expression-2 / | IS [NOT] < | | ¯¯¯ ¯ | \ IS [NOT] = / ¯¯¯ ¯ |
|
Quando gli operandi sono entrambi numerici, indipendentemente dal fatto che la loro rappresentazione sia in forma di «indice» (INDEX), compatta (COMPUTATIONAL) o in forma di byte (DISPLAY), il confronto si basa sul valore numerico che esprimono, tenendo conto del segno, se c'è, considerando positivi i valori senza segno.
Quando si confrontano operandi alfanumerici, o quando anche uno solo è di tipo alfanumerico, il confronto avviene in modo lessicografico (in base all'ordinamento previsto dalla codifica adottata).
La condizione di classe serve a stabilire se l'operando a cui si applica è numerico o alfabetico. È numerico un operando che è composto soltanto di cifre da 0 a 9, con il segno eventuale; è alfabetico un operando composto soltanto dal lettere alfabetiche ed eventualmente da spazi.
La condizione di classe si utilizza solo per verificare il contenuto di variabili che sono state dichiarate con una rappresentazione in byte (USAGE IS DISPLAY). |
Segue lo schema sintattico per esprimere la condizione di classe:
/ \ | NUMERIC | identifier IS [NOT] < ¯¯¯¯¯¯¯ > ¯¯¯ | ALPHABETIC | \ ¯¯¯¯¯¯¯¯¯¯ / |
Naturalmente, se si usa la parola chiave NOT, si intende invertire il significato della condizione.
I nomi di condizione, che si dichiarano nella divisione DATA DIVISION con il numero di livello 88, servono a descrivere il confronto della variabile a cui si riferiscono con i valori che rappresentano.
Supponendo di avere dichiarato il nome di condizione PARI nel modo seguente:
|
Nella divisione PROCEDURE DIVISION potrebbero apparire righe come quelle successive, per verificare che la variabile CODICE contenga un valore pari:
|
La condizione di segno permette di stabilire se un'espressione aritmetica (e può essere anche solo una costante o una variabile numerica) è positiva, negativa o se vale esattamente zero:
/ POSITIVE \ | ¯¯¯¯¯¯¯¯ | arithmetic-expression IS [NOT] < NEGATIVE > ¯¯¯ | ¯¯¯¯¯¯¯¯ | \ ZERO / ¯¯¯¯ |
Attraverso gli operatori booleani comuni, si possono definire delle condizioni composte, oppure negate. Si utilizzano le parole chiave AND, OR e NOT per esprimere gli operatori booleani noti con lo stesso nome. Con l'ausilio delle parentesi tonde si possono modificare le precedenze nella valutazione delle espressioni.
Il linguaggio COBOL prevede una forma abbreviata per esprimere delle condizioni di relazione composte. Si osservi l'espressione seguente: A > B OR A > C OR A < D Questa potrebbe essere abbreviata così: A > B OR > C OR < D Tuttavia, si comprende che l'abbreviazione comporta maggiore difficoltà interpretativa nella lettura umana del programma sorgente. |
Per «avverbi comuni» qui si intendono delle parole chiave che possono far parte di varie istruzioni, fornendo però lo stesso tipo di funzionalità.
|
Nelle sezioni successive 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 72.4.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 delle 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 72.4.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.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»:
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.
|
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.
|
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 |
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.
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. |
.-- / \ --. | | 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 72.225.
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 superata, 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 delle 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).
Il riordino e la fusione del contenuto dei file sono gestite normalmente attraverso funzionalità speciali del linguaggio COBOL. Si utilizzano in particolare file dichiarati con l'indicatore di livello SD nella sezione FILE SECTION, per svolgere la funzione di riordino o di fusione, mentre i file da ordinare o da fondere, assieme al risultato dell'ordinamento o della fusione, possono essere file normali organizzati secondo le esigenze del programma.
I file che prendono parte alle operazioni di riordino e di fusione, non devono essere aperti o chiusi durante tali operazioni.
Il riordino di un file, con l'istruzione SORT del COBOL, richiede in linea di massima il coinvolgimento di tre file: il file che formalmente serve come appoggio per svolgere la funzione di ordinamento, dichiarato nella sezione FILE SECTION con l'indicatore di livello SD; un file per i dati in ingresso da ordinare; un file per accogliere il risultato del procedimento di ordinamento.
/ / \ \ | | ASCENDING | | SORT file-name-1 < ON < ¯¯¯¯¯¯¯¯¯ > KEY { data-name-1 }... >... ¯¯¯¯ | | DESCENDING | | \ \ ¯¯¯¯¯¯¯¯¯¯ / / / .-- / \ --. \ | | | THROUGH | | | | INPUT PROCEDURE IS procedure-name-1 | < ¯¯¯¯¯¯¯ > procedure-name-2 | | < ¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | > | `-- \ ¯¯¯¯ / --' | | | \ USING { file-name-2 }... / ¯¯¯¯¯ / .-- / \ --. \ | | | THROUGH | | | | OUTPUT PROCEDURE IS procedure-name-3 | < ¯¯¯¯¯¯¯ > procedure-name-4 | | < ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | > | `-- \ ¯¯¯¯ / --' | | | \ GIVING { file-name-3 }... / ¯¯¯¯¯¯ |
Il file che nello schema sintattico appare nominato come file-name-1, è quello che deve essere dichiarato nella sezione FILE SECTION con l'indicatore di livello SD. Dopo la parola ASCENDING si indica un elenco di chiavi di ordinamento crescenti; dopo la parola DESCENDING si indica un elenco di chiavi di ordinamento decrescenti. Le chiavi di ordinamento sono campi del record del file file-name-1 e la possibilità di indicare più chiavi serve a definire una gerarchia di ordinamento quando se ne crea la necessità. In pratica, la presenza di più chiavi fa sì che in presenza di chiavi doppie a un certo livello gerarchico, permetta di distinguere l'ordine dei record utilizzando anche le chiavi di livello inferiore.
Nell'ordinamento di un file, la presenza di record con tutte le chiavi previste doppie, ha l'unico inconveniente di non poter stabilire quale sequenza effettiva ottengono tali record dopo l'ordinamento. |
Il file da ordinare può essere costituito dal nome che appare dopo la parola USING, oppure può essere generato da un gruppo di procedure del programma, specificate dopo le parole INPUT PROCEDURE. Il file indicato dopo la parola USING è un file dichiarato normalmente, con l'organizzazione e l'accesso desiderati.
Il file che risulta dall'ordinamento può essere costituito dal nome che appare dopo la parola GIVING, oppure può essere letto da un gruppo di procedure del programma, specificate dopo le parole OUTPUT PROCEDURE. Il file indicato dopo la parola GIVING è un file dichiarato normalmente, con l'organizzazione e l'accesso desiderati.
La gestione dei dati in ingresso o in uscita, attraverso delle procedure, viene descritto in altre sezioni; per il momento viene mostrato un esempio di ordinamento tipico, che coinvolge il file per il riordino, più due file per i dati (in ingresso e in uscita).
|
Nell'esempio, il file usato per ottenere il riordino è sort.tmp
; il file da ordinare è input.seq
(organizzato in modo sequenziale); il file ordinato che si ottiene è output.seq
(anche questo organizzato in modo sequenziale). Come chiave di ordinamento si prendono in considerazione i primi cinque byte del record.
Lo schema sintattico consentirebbe l'indicazione di più file da ordinare e di più file ordinati da generare. Nel primo caso, i dati dei vari file vengono raccolti assieme e considerati parte di un file unico da ordinare; i file ordinati da generare, invece, rappresentano copie dello stesso risultato ordinato.
La fusione di due o più file, con l'istruzione MERGE del COBOL, richiede la presenza di due o più file ordinati nello stesso modo, che si vogliono mettere insieme in un solo file ordinato. Per compiere questa funzione, si aggiunge un file ulteriore, dichiarato nella sezione FILE SECTION con l'indicatore di livello SD.
/ / \ \ | | ASCENDING | | MERGE file-name-1 < ON < ¯¯¯¯¯¯¯¯¯ > KEY { data-name-1 }... >... ¯¯¯¯¯ | | DESCENDING | | \ \ ¯¯¯¯¯¯¯¯¯¯ / / USING file-name-2 { file-name-3 }... ¯¯¯¯¯ / .-- / \ --. \ | | | THROUGH | | | | OUTPUT PROCEDURE IS procedure-name-1 | < ¯¯¯¯¯¯¯ > procedure-name-2 | | < ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | > | `-- \ ¯¯¯¯ / --' | | | \ GIVING { file-name-4 }... / ¯¯¯¯¯¯ |
La prima parte dello schema sintattico va interpretata nello stesso modo di quello per il riordino; dove file-name-1 è il file che deve essere dichiarato nella sezione FILE SECTION con l'indicatore di livello SD e le variabili indicate dopo le parole ASCENDING o DESCENDING sono le chiavi di ordinamento previste nei file in ingresso.
Successivamente si può osservare nello schema sintattico che sono previsti soltanto file in ingresso, dopo la parola USING, tenendo in considerazione il fatto che devono essere almeno due. Questi file devono risultare già ordinati secondo le chiavi previste, altrimenti il risultato della fusione non è prevedibile.
Il risultato della fusione può essere costituito dal nome che appare dopo la parola GIVING, oppure può essere letto da un gruppo di procedure del programma, specificate dopo le parole OUTPUT PROCEDURE. Il file indicato dopo la parola GIVING è un file dichiarato normalmente, con l'organizzazione e l'accesso desiderati.
La gestione dei dati in uscita, attraverso delle procedure, viene descritto in altre sezioni; per il momento viene mostrato un esempio di fusione tipico, si hanno i file input-1.seq
e input-2.seq
ordinati, si vuole ottenere il file output.seq
con la somma dei record, mantenendo l'ordinamento:
|
Lo schema sintattico consentirebbe l'indicazione di più file ordinati da generare: se viene indicato più di un file per raccogliere il risultato della fusione, questi ottengono lo stesso contenuto; i file in sé possono essere differenti, se possiedono una diversa organizzazione.
Nelle istruzioni SORT e MERGE, a seconda dei casi, esiste la possibilità di specificare un gruppo di procedure con le forme seguenti:
.-- / \ --. | | THROUGH | | INPUT PROCEDURE IS procedure-name-1 | < ¯¯¯¯¯¯¯ > procedure-name-2 | ¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | `-- \ ¯¯¯¯ / --' |
.-- / \ --. | | THROUGH | | OUTPUT PROCEDURE IS procedure-name-1 | < ¯¯¯¯¯¯¯ > procedure-name-2 | ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | `-- \ ¯¯¯¯ / --' |
Queste procedure sono da intendere come un intervallo di sezioni o di paragrafi della divisione PROCEDURE DIVISION, da procedure-name-1 a procedure-name-2. Questa porzione di sezione o di paragrafi deve però rispettare delle condizioni: deve servire esclusivamente per lo scopo del riordino o della fusione; non può contenere chiamate a procedure esterne; non può essere usata nel programma per fini differenti.
In generale, se si intendono usare delle procedure per generare dati da ordinare, leggere i dati ordinati o fusi, conviene gestire la divisione PROCEDURE DIVISION in sezioni. L'esempio seguente mostra proprio una sezione che potrebbe essere usata per leggere il risultato di un file ordinato o fuso:
|
Nell'esempio si vede anche l'uso del famigerato GO TO, allo scopo di uscire dalla sezione dopo l'esecuzione del ciclo di chiamate al paragrafo MOSTRA-RECORD, dal momento che l'istruzione EXIT, secondo lo standard, deve trovarsi da sola in un paragrafo.
Quando si usano le istruzioni SORT o MERGE, invece di generare un file ordinato o fuso, è possibile leggere il risultato dell'ordinamento o della fusione, specificando la chiamata di un intervallo di procedure (paragrafi o sezioni):
.-- / \ --. | | THROUGH | | OUTPUT PROCEDURE IS procedure-name-1 | < ¯¯¯¯¯¯¯ > procedure-name-2 | ¯¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | `-- \ ¯¯¯¯ / --' |
Nell'ambito dell'intervallo di procedure chiamato, occorre usare l'istruzione RETURN per leggere questi dati dal file di riordino o di fusione:
RETURN file-name-1 [NEXT] RECORD [ INTO identifier ] ¯¯¯¯¯¯ ¯¯¯¯ ¯¯¯¯ AT END { imperative-statement }... ¯¯¯ |
L'istruzione RETURN funziona a tutti gli effetti come l'istruzione READ di un file sequenziale, dove il file indicato è precisamente quello che appare nell'istruzione SORT o MERGE chiamante, con la stessa metavariabile.
|
L'esempio riguarda la visualizzazione di un file ordinato, senza generare il file stesso, ma si applica tale e quale al caso della fusione.
Limitatamente al caso del riordino, con l'istruzione SORT, è possibile acquisire i record da riordinare attraverso una procedura:
.-- / \ --. | | THROUGH | | INPUT PROCEDURE IS procedure-name-1 | < ¯¯¯¯¯¯¯ > procedure-name-2 | ¯¯¯¯¯ ¯¯¯¯¯¯¯¯¯ | | THRU | | `-- \ ¯¯¯¯ / --' |
Nell'ambito dell'intervallo di procedure chiamato, occorre usare l'istruzione RELEASE per passare formalmente un record. L'istruzione RELEASE si utilizza e si comporta come l'istruzione WRITE per i file sequenziali:
WRITE record-name [ FROM identifier-1 ] ¯¯¯¯¯ ¯¯¯¯ |
Il record è il nome della variabile strutturata corrispondente del file che esegue in pratica l'ordinamento, ovvero quello che nello schema sintattico dell'istruzione SORT appare come file-name-1.
|
L'esempio è completo, in quanto anche il risultato del riordino viene gestito tramite una procedura. Nella fase di inserimento dati, si può osservare che un inserimento nullo (pari all'inserimento di tutti spazi), implica la conclusione di quella fase.
Christopher Heng, Free COBOL compilers and interpreters, http://www.thefreecountry.com/compilers/cobol.shtml
Programming manuals and tutorials, COBOL, http://www.theamericanprogrammer.com/programming/manuals.cobol.html
MPE/iX and HP e3000 Technical Documentation, HP COBOL II/XL
Programmer's guide, http://wayback.archive.org/web/2006*/http://docs.hp.com/en/424/31500-90014.pdf
Quick reference guide, http://wayback.archive.org/web/2006*/http://docs.hp.com/en/425/31500-90015.pdf
Reference manual, http://wayback.archive.org/web/2006*/http://docs.hp.com/en/426/31500-90013.pdf
Compaq COBOL Reference Manual, http://www.helsinki.fi/atk/unix/dec_manuals/cobv27ua/cobrm_contents.htm
«a2» 2013.11.11 --- Copyright © Daniele Giacomini -- appunti2@gmail.com http://informaticalibera.net