Sviluppo

Corso di programmazione in REXX - Lezione 6 - nozioni avanzate

Alessandro Cantatore
 

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:

Diagramma programmazione strutturata

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:
    1. L'interprete del REXX cerca un etichetta nello stesso file in cui è presente la chiamata.
    2. Se non viene trovata alcuna etichetta, viene ricercata tra le funzioni standard dell'interprete del REXX una funzione corrispondente a quella chiamata.
    3. 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:
  1. Il REXX sospende l'esecuzione di qualsiasi istruzione.
  2. Il numero di linea dell'istruzione in cui si è verificata la condizione di trap viene assegnato alla variabile speciale SIGL.
  3. La condizione è disabilitata.
  4. 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]


[Pagina precedente] [Sommario] [Pagina successiva]