La programmazione strutturata
L'uso delle subroutine è tipico dell'approccio alla cosiddetta
programmazione strutturata.
Raramente i programmatori esperti scrivono programmi complessi sotto
forma di una singola lista di istruzioni, ma piuttosto li suddividono
in unità più semplici dette moduli, creando poi un
modulo principale che chiama tutti gli altri o in ordine consecutivo
o tramite istruzioni di controllo come IF, SELECT e DO.
Per esempio:
La programmazione strutturata è utile per i seguenti motivi:
- Rende più facile progettare un programma perché progetti
di grande dimensione possono essere separati in sub-progetti
meno complessi e più comprensibili.
- Rende il programma più leggibile. Ciò è particolarmente
uitile nel correggere o espandere uteriormente il programma.
Si possono correggere gli errori dei singoli moduli o aggiungere
nuove funzionalità ai moduli stessi.
E' più facile rintracciare gli errori e la struttura complessiva
del programma è più chiara.
- Con l'acquisizione di una maggior esperienza di programmazione,
diventa più facile definire i compiti svolti dal programma e dai
singoli moduli prima di scrivere il codice del programma.
Inoltre un modulo sviluppato per un programma spesso può trovare utilizzo
anche in altri programmi.
Chiamate di funzioni
Una subroutine può produrre un risultato, (per esempio mostrare
delle informazioni sullo schermo, modificare dei file, ecc.) oppure
può restituire un valore utilizzato dalla procedura chiamante.
E' possibile operare in due differenti modi:
- Usando l'istruzione CALL per chiamare la subroutine e
poi ricavarne il valore calcolato tramite la variabile speciale
RESULT.
- Chiamando la subroutine come una funzione, allo stesso modo
delle funzioni standard incluse nell'interprete del REXX.
Nell'esempio che segue usiamo una semplice subroutine (quadrato)
per calcolare il quadrato di 3 e 4, chiamandola la
prima volta tramite CALL e la seconda come funzione:
/* callfunc.cmd: esempio di chiamata di subroutine che */
/* restituisce un valore */
/* calcola il quadrato di 3 tramite call*/
call quadrato 3
say " 3 al quadrato è" result
/* calcola il quadrato di 4 tramite chiamata di funzione */
say " 4 al quadrato è" quadrato(4)
exit
quadrato:
arg num
return num * num
Creare una funzione
Una subroutine chiamata come funzione è essenzialmente uguale a
qualsiasi altra subroutine che restituisca un valore.
La restituzione di un valore è essenziale perché
le funzioni devono sempre restituire un valore anche se
tale valore è rappresentato da una stringa vuota.
Gli argomenti della funzione devono essere racchiusi tra parentesi
e possono essere letti nella subroutine (come nel caso degli argomenti
passati tramite l'istruzione CALL) tramite l'istruzione ARG
o la funzione ARG().
Poiché le funzioni restituiscono sempre un valore, esse sono di solito
usate in espressioni come, per esempio, in un'istruzione SAY.
Se in una linea di codice si scrive solo una chiamata di funzione,
il valore restituito dalla funzione viene passato al sistema come
un comando. Se per esempio chiamiamo come funzione la subroutine
quadrato, vista nell'esempio del paragrafo precedente,
mettendola all'inizio della linea di codice:
/* errfunc.cmd: mostra come NON usare la chiamata a funzione */
quadrato(3)
say result
exit
quadrato:
arg num
return num * num
non verrà visualizzato 9, bensì:
SYS1041: The name 9 is not recognized as an
internal or external command, operable program or batch file.
2 *-* quadrato(3)
>>> "9"
+++ "RC(1041)"
RESULT
L'interprete del REXX infatti calcola il risultato dell'espressione
quadrato(3) passandolo poi all'interprete dei comandi di OS/2.
Non essendo presente alcun comando chiamato 9 viene quindi
mostrato il messaggio di errore precedente.
Quando non si vuole utilizzare il valore restituito da una funzione
è necessario chiamarla tramite l'istruzione CALL.
Differenze tra subroutine e funzioni
Ci sono sia differenze che somiglianze tra routine e funzioni.
Le differenze sono:
- Per chiamare una subroutine si usa l'istruzione CALL:
CALL routine [argomento1, ... ]
Per chiamare una funzione si usa la sintassi della chiamata di funzione:
routine([argomento1, ... ])
- Una subroutine non deve necessariamente restituire un valore, mentre
una funzione deve restituire un valore risultante.
In una subroutine è possibile scrivere RETURN senza alcun valore:
RETURN
In una funzione è necessario restituire almeno una stringa nulla:
RETURN ""
Invece sia le subroutine che le funzioni:
- usano le istruzione ARG e PARSE ARG e la funzione
ARG() per ottenere i valori degli argomenti.
- possono essere interne (cioè definite da un'etichetta nello stesso
file da cui sono chiamate) o esterne (situate in un differente file).
- usano lo stesso ordine di ricerca. In presenza di una chiamata di
funzione o subroutine:
- L'interprete del REXX cerca un etichetta nello stesso file in cui
è presente la chiamata.
- Se non viene trovata alcuna etichetta, viene ricercata tra le
funzioni standard dell'interprete del REXX una funzione corrispondente
a quella chiamata.
- Se le precedenti condizioni non vengono soddisfatte l'interprete
cerca una routine esterna o in un file CMD avente lo stesso nome
della routine chiamata, o in una libreria dinamica di funzioni scritta
in un altro linguaggio.
Un'altra similitudine tra funzioni e subroutine è che anche i programmi
progettati come funzioni possono essere chiamati come subroutine, mentre
programmi progettati come subroutine, purché restituiscano un risultato,
possono essere chiamati come funzioni.
Inoltre sia le subroutine che le funzioni possono usare l'istruzione
PROCEDURE quando sono interne.
Istruzioni di salto
Un'istruzione di salto sposta l'elaborazione ad una differente posizione
nel programma. Dopo il salto l'elaborazione non riprende atomaticamente
dal punto precedente.
L'istruzione SIGNAL permette di trasferire il flusso di esecuzione
in un'altra sezione del programma. Quando l'interprete del REXX incontra
questa istruzione abbandona immediatamente l'elaborazione dal punto
corrente uscendo eventualmente da un loop DO o SELECT.
SIGNAL non può essere usato per tornare indietro o semplicemente
per saltare attorno ad un loop. Ciò significa che SIGNAL
può essere usato solo quando si vuole terminare il programma.
Per altri scopi è meglio controllare il flusso di elaborazione del
programma tramite IF, SELECT o DO.
La sintassi dell'istruzione SIGNAL è:
SIGNAL simbolo
che causa un salto all'etichetta simbolo specificata.
L'esecuzione del programma riprende dal codice che segue l'etichetta.
L'istruzione SIGNAL memorizza sempre il suo numero di linea
nella variabile speciale SIGL.
Questo è un esempio di utilizzo di SIGNAL e SIGL per
causare la terminazione anormale di un programma:
.
.
SIGNAL termine
.
.
.
EXIT
/* codice relativo alla terminazione del programma */
termine:
say "Teminazione anomala segnalata alla linea "SIGL". Impossibile continuare"
exit
La prima istruzione EXIT determina la fine del programma quando
viene normalmente eseguito. Se il flusso di elaborazione provoca
l'esecuzione dell'istruzione SIGNAL, le linee di codice tra
l'istruzione stessa e il summenzionato EXIT non verranno mai
eseguite, ma il flusso riprenderà direttamente dall'etichetta termine,
mostrando tramite la variabile SIGL la linea del codice che
ha determinato il salto e poi terminerà il programma con la seconda
istruzione EXIT.
SIGNAL viene solitamente usato per terminare un programma
nel modo piu' opportuno nel caso si verifichi un errore.
Un altro modo di usare SIGNAL è con la parola chiave ON
seguita da una specifica condizione che si vuole sia testata
dal REXX. Quando si verifica la condizione specificata, l'elaborazione
salta immediatamente all'etichetta corrispondente.
La sintassi è:
SIGNAL ON condizione [NAME nometrap]
Quando l'istruzione SIGNAL è stata abilitata dalla parola chiave
ON e si verifica la condizione specificata, l'interprete del REXX
salta ad un'etichetta che può essere sia:
- il nome della condizione stessa (default)
- un nome alternativo specificato tramite la parola chiave NAME
Dopo l'istruzione SIGNAL ON l'interprete del REXX, per ogni
linea di codice eseguita, controlla la condizione specificata.
Tale funzione di rilevamento della condizione (trap) rimane abilitata
fino alla fine del programma o finché non compare la corrispondente
istruzione:
SIGNAL OFF condizione
Le condizioni che possono essere rilevate tramite l'istruzione SIGNAL ON
sono:
- ERROR
- Provoca un salto alla subroutine con l'etichetta ERROR:.
Si ha una condizione di ERROR quando il REXX passa un comando
all'interprete dei comandi di OS/2 e quest'ultimo restituisce un
risultato di errore. Questo include comandi che restituiscono un
valore di ritorno differente da zero e comandi sconosciuti al sistema.
Ciò include anche le condizioni di FAILURE nel caso non
sia stata espressamente stabilita una trap su di esse tramite
l'istruzione SIGNAL ON FAILURE.
- FAILURE
- Provoca un salto alla subroutine con l'etichetta FAILURE:.
Il salto avviene quando il REXX passa un comando all'interprete dei
comandi e il sistema incontra una condizione che impedisce di
eseguire il comando stesso.
- NOTREADY
- Si verifica un salto alla subroutine con l'etichetta NOTREADY:
quando si verifica un'errore durante un'operazione di lettura o scrittura.
Per esempio questa condizione viene soddisfatta quando si tenta di
accedere ad un'unità removibile in cui non sia inserito alcun
supporto di memorizzazione.
- NOVALUE
- Provoca un salto alla subroutine con etichetta NOVALUE:
quando si incontra un simbolo possibile nome di una variabile e
tale variabile non esiste. Questo può essere particolarmente utile
durante lo sviluppo dei programmi REXX per trovare nomi di variabile
scritti male.
- SYNTAX
- Provoca un salto alla subroutine con etichetta SYNTAX
quando viene rilevato un errore di sintassi del REXX. Questo è
particolarmente utile per scoprire errori presenti nel programma.
Se viene verificata una delle condizioni per cui è stata impostata
un'istruzione SIGNAL ON:
- Il REXX sospende l'esecuzione di qualsiasi istruzione.
- Il numero di linea dell'istruzione in cui si è verificata la
condizione di trap viene assegnato alla variabile speciale SIGL.
- La condizione è disabilitata.
- L'elaborazione salta alla subroutine con l'etichetta appropriata
alla condizione verificatasi.
In alcuni casi è possibile riprendere l'esecuzione del programma dopo
il rilevamento di una condizione di trap.
A tale scopo invece dell'istruzione SIGNAL si deve usare
l'istruzione CALL con la seguente sintassi:
CALL ON condizione [NAME nometrap]
|