![]() |
Corso di programmazione in REXX - Lezione 5 - nozioni di base![]() |
Nota: i sorgenti descritti in questo numero sono stati raccolti
nel file rxsource.zip (3386 byte).
Istruzioni di controllo dell'esecuzione del programma
I blocchi di istruzioni DO espressione 1 espressione 2 espressione 3 ENDIn questo caso la serie di istruzioni racchiusa dalla coppia DO - END viene considerata dall'interprete del REXX come un'unica istruzione. E' consigliabile indentare il blocco di istruzioni di alcuni spazi a destra per rendere il programma più leggibile, mostrando chiaramente che la serie di istruzioni costituisce un unico blocco.
Condizioni di comparazione /* comp.cmd */ say 5 = 5 /* mostra '1' - vero */ say 5 < 4 /* mostra '0' - falso */ say 5 = 4 /* mostra '0' - falso */ say 5 > 4 /* mostra '1' - vero */ reply = "sì" /* assegna la stringa "sì" alla variabile REPLY */ say reply /* mostra "sì" */ say reply = "FORSE" /* mostra '0' - falso */ say reply = "sì" /* mostra '1' - vero */
Diramazione semplice if espressione then istruzioneL'istruzione viene eseguita solo se l'espressione è vera. Per esempio: /* richiesta di conferma */ say " digita YES per continuare" pull reply if reply = "YES" then say "OK!"L'istruzione say "OK!" viene eseguita solo se nella variabile reply è memorizzato il valore "YES". L'istruzione IF introduce una nuova branca di istruzioni da eseguire se l'espressione che segue IF è vera. I programmatori spesso visualizzano l'azione di decisione di quale branca di istruzioni tramite un diagramma chiamato diagramma di flusso.
![]() L'espressione di decisione è rappresentata dal rombo. Se l'espressione (reply = "YES") è vera, il flusso di esecuzione del programma prende una deviazione per un ulteriore istruzione (say "OK!") prima di riprendere il flusso normale.
L'uso di DO...END per il raggruppamento di più espressioni Per eseguire una serie di istruzioni dopo il THEN che segue IF è necessario raggrupparle in modo che l'interprete del REXX possa valutarle come un'unica espressione. A tale scopo si usa l'istruzione DO per iniziare la lista delle istruzioni e la parola chiave END per chiuderla. Per esempio: IF espressione THEN DO istruzione 1 istruzione 2 istruzione 3 ...eccetera ENDSe espressione è VERA tutte le istruzioni comprese tra DO e END vengono eseguite come se fossero un'unica istruzione. Se l'espressione è FALSA si passa invece direttamente all'istruzione successiva a END. if sole = "splende" then do say "Svegliati!" say "Alzati!" say "Esci!" endIl diagramma di flusso è il seguente:
![]() Se la condizione sole = "splendente" è vera tutte le 3 istruzioni SAY vengono eseguite, se invece è falsa non ne viene eseguita nessuna. E' bene indentare il testo racchiuso tra DO e END in modo che sia immediatamente comprensibile che esso rappresenta un gruppo di istruzioni. Il DO può essere anche scritto sulla stessa riga di seguito a THEN. Un diverso stile di formattazione è ad esempio: if sole = "splendente" then do say "Svegliati!" say "Alzati!" say "Esci!" end
La parola chiave ELSE IF espressione THEN istruzione 1 ELSE istruzione 2Quando IF è usato in questo modo il REXX esegue solo una delle due istruzioni: istruzione 1 se espressione è VERA, istruzione 2 se invece è FALSA. Anche nel primo esempio di questo corso si era usato IF...THEN...ELSE: /* CIAO.CMD : questo è il mio primo programma REXX */ say "Ciao! Come ti chiami?" pull nome if nome = "" then say "Ciao sconosciuto!" else say "Ciao" nomeIl diagramma di flusso è il seguente:
![]() Un altro esempio di IF...THEN...ELSE è BACKITUP.CMD che crea una copia di backup del file introdotto: /* BACKITUP.CMD: fa una copia di backup di un programma REXX */ arg fname"."ext /* attraverso questo costrutto viene effettuato */ /* il "parsing" dell'argomento scomponendolo */ /* nelle due parti: "nome file" e "estensione" */ if fname = "" then do /* se non si è introdotto il nome del file */ say "Scrivi il nome del file" /* lo chiede all'utente */ pull fname"."ext end if ext = "" then ext = "CMD" /* se non c'è l'estensione usa */ /* ".CMD" come estensione */ "@dir" fname"."ext /* esegue il comando "DIR" sul nome del file */ /* introdotto per controllare che sia valido */ if rc \= 0 then do /* se il file non esiste mostra */ say "Impossibile eseguire il backup!" /* un messaggio di errore e */ say "Programma terminato." /* termina */ exit end else do /* altrimenti copia il file in */ say "Backing up" fname"."ext /* un nuovo file con estensione */ "@copy" fname"."ext fname".BKP" /* ".BKP"; mostrandone i dati */ "@dir" fname".BKP" /* tramite il comando "DIR" */ say "Programma terminato." exit endCome si può notare anche con ELSE si è usato DO...END per raggruppare in un blocco unico diverse istruzioni. Note:
L'istruzione SELECT SELECT WHEN espressione1 THEN istruzione1 WHEN espressione2 THEN istruzione2 WHEN espressione3 THEN istruzione3 ... OTHERWISE istruzione istruzione istruzione ... ENDSe espressione1 è VERA viene eseguita istruzione1 e l'elaborazione poi prosegue con le istruzioni che seguono END. Se espressione1 è FALSA viene controllata la validità di espressione2: se questa è VERA viene eseguita istruzione2 e l'elaborazione riprende con l'istruzione successiva a END, altrimenti vengono testate le rimanenti espressioni finché non se ne trovi una VERA. Nel caso nessuna espressione risulti VERA viene eseguito il blocco di istruzioni compreso tra OTHERWISE (che in italiano significa altrimenti) e END. Il diagramma di flusso per l'istruzione SELECT è il seguente:
![]() Anche con l'istruzione SELECT è possibile raggruppare una lista di istruzioni tramite DO...END: SELECT WHEN espressione1 THEN DO istruzione_a istruzione_b istruzione_c END WHEN espressione2 THEN istruzione2 WHEN espressione3 THEN istruzione3 ... OTHERWISE istruzione istruzione istruzione ... ENDNon è necessario invece usare DO...END per raggruppare più istruzioni dopo la parola chiave OTHERWISE. Nel seguente esempio, INFO.CMD, viene mostrato l'uso di SELECT. /* INFO.CMD */ say "cosa vuoi sapere? (data/ora/giorno/mese/adesso)" pull reply select when reply = "DATA" then say date() when reply = "ORA" then say time() when reply = "GIORNO" then say date("W") when reply = "MESE" then say date("M") when reply = "ADESSO" then do say time("H") say time("M") say time("S") end otherwise say "gli argomenti validi sono: DATA" say " ORA" say " GIORNO" say " MESE" say " ADESSO" end exitE' essenziale che SELECT abbia un corrispondente END! E' inoltre opportuno, come per i costrutti che abbiamo visto in precedenza, indentare le istruzioni che costituiscono un gruppo unico per facilitare la lettura del codice sia per correzioni che per espansioni future.
Esecuzione ripetitiva: i loop
DO espressione istruzione1 istruzione2 istruzione3 .... ENDdove il valore di espressione è un numero intero che rappresenta il numero di volte che la serie di istruzione viene eseguita. Il seguente esempio, SUONA.CMD esegue una serie di suoni un determinato (5) numero di volte: /* SUONA.CMD */ do 5 call beep 440, 200 call beep 880, 200 call beep 1760, 200 call beep 3520, 200 endIl seguente programma chiede di specificare la base e l'altezza e disegna un rettangolo sullo schermo: /* RETT.CMD */ /* chiede di introdurre la dimensione della base del rettangolo */ say "Introduci la base del rettangolo (3 - 60)" pull base /* controlla la validità della grandezza introdotta */ select when \datatype(base, "WHOLE") then /* (1) */ say base "non è un numero valido!" when base < 3 then /* (2) */ say "è troppo stretto!" when base > 60 then say "è troppo largo!" otherwise say "Introduci l'altezza del rettangolo (3 - 15)" pull altezza select when \datatype(altezza, "WHOLE") then /* (1) */ say altezza "non è un numero valido!" when altezza < 3 then /* (2) */ say "è troppo basso!" when altezza > 15 then say "è troppo alto!" otherwise say say do altezza /* (3) */ call charout , " " do base /* (3) */ call charout , "db"x /* (4) */ end /* do base */ call charout , "0d0a"x /* ritorno a capo */ end /* do altezza */ say say end end exitLa funzione dell'interprete del REXX DATATYPE (1) viene usata con il parametro "WHOLE" per controllare che il dato introdotto sia un numero intero. Viene inoltre controllato che il valore introdotto sia compreso entro i limiti stabiliti (2) (nell'esempio 3 - 60 per la base e 3 - 15 per l'altezza. Le istruzioni comprese tra DO e END vengono eseguite tante volte quanto è il valore delle variabili che seguono DO (3), nell'esempio altezza e base. La funzione CHAROUT (4) viene qui usata per visualizzare un singolo carattere sullo schermo (un rettangolo se la codepage usata è 850). La visualizzazione viene ripetuta tante volte quanta è la larghezza della base (do base ... end) e il ciclo viene ripetuto tante volte quanto il valore dell'altezza ( do altezza ... end).
Loop condizionali
Nota: nello scrivere programmi in REXX può capitare di creare inavvertitamente dei cicli infiniti, la cui esepressione condizionale è sempre valutata vera. Per interrompere l'esecuzione dello script in tali casi è sufficiente premere i tasti Control e C.
Il ciclo DO FOREVER e l'istruzione LEAVE
![]()
Il seguente programma di esempio somma i numeri introdotti da tastiera. /* somma.cmd */ totale = 0 do forever say "Introduci un numero" pull numero if \datatype(numero,"N") /* se non si è introdotto un numero */ then leave /* termina il ciclo */ totale = totale + numero say "Totale = "totale end say "'"numero"' non è un numero. Termine del programma."
Il ciclo DO WHILE DO WHILE espressione istruzione1 istruzione2 istruzione3 . . . ENDdove espressione deve dare come risultato 0 (FALSO) o 1 (VERO). Come si vede dal sottostante diagramma di flusso, la condizione viene testata all'inizio del ciclo, prima dell'esecuzione della lista di istruzioni. Ciò implica che se la condizione non viene soddisfatta nella fase iniziale la lista di istruzioni non viene mai eseguita. Al contrario nel ciclo DO UNTIL la condizione viene verificata solo a fine ciclo, perciò, anche nel caso che non venga sodisfatta dall'inizio, la lista di istruzioni viene eseguita almeno una volta.
![]() La stessa funzione del ciclo DO WHILE può essere ottenuta anche da DO FOREVER. Infatti: DO FOREVER IF \ espressione THEN LEAVE istruzione1 istruzione2 istruzione3 . . . ENDequivale a: DO WHILE espressione istruzione1 istruzione2 istruzione3 . . . ENDPer esempio il ciclo DO WHILE può essere usato per richiedere all'utente dei parametri quando non li ha immessi da riga di comando all'avvio del programma: /* ottiene l'argomento */ arg filename do while filename = "" /* viene eseguito se non ci sono argomenti */ say "Introduci il nome di un file o '*' per terminare il programma:" pull filename if filename = "*" then exit end . . .Se l'utente scrive l'argomento quando avvia il programma la lista di istruzioni compresa nel ciclo DO WHILE non viene mai eseguita. Nel caso contrario il ciclo viene eseguito finché non viene immesso il nome di un file.
Il ciclo DO UNTIL DO UNTIL espressione istruzione1 istruzione2 istruzione3 . . . ENDdove espressione quando viene valutata dall'interprete del REXX deve dare come risultato o 0 o 1. La condizione viene verificata solo a fine ciclo, cioè dopo ogni volta che la lista di istruzioni viene eseguita. Ciò implica che la lista di istruzioni viene eseguita anche se espressione è VERA dall'inizio. Paragonate il seguente diagramma di flusso con quello del ciclo DO WHILE:
![]() Come anche per DO WHILE si può ottenere la stessa funzione di DO UNTIL usando DO FOREVER: DO FOREVER istruzione1 istruzione2 istruzione3 . . . IF espressione THEN LEAVE ENDequivale a: DO UNTIL espressione istruzione1 istruzione2 istruzione3 . . . ENDL'istruzione DO UNTIL può convenientemente usata per controllare la validità dei dati immessi dall'utente. Nell'esempio seguente la funzione DATATYPE è usata per controllare che il dato immesso sia un numero. Il ciclo continua finché l'utente non immette un numero o introduce un argomento nullo premendo semplicemente Invio: /* accetta solo dati numerici */ do until datatype(input, "N") say "Introduci un numero o premi 'Invio' per terminare." pull input if input = "" then exit end . . .Il ciclo viene sempre eseguito almeno una volta, anche se nella variabile input è già memorizzato un numero valido. Nell'esempio del paragrafo precedente era possibile terminare il loop immettendo il carattere '*', mentre in questo esempio basta premere Invio. E' importante, quando si impiegano dei loop per richiedere dati all'utente, prevedere dei comandi di terminazione del loop stesso.
Cicli controllati da contatori DO variabile = espressione_i istruzione1 istruzione2 istruzione3 . . . ENDoppure DO variabile = espressione_i TO espressione_t istruzione1 istruzione2 istruzione3 . . . ENDdove:
![]() Il seguente esempio mostra come sia possibile disegnare un triangolo sullo schermo usando un contatore: /* triangle.cmd: viene richiesto all'utente di specificare l'altezza */ /* del triangolo e il triangolo viene disegnato sullo */ /* schermo */ say "Introduci l'altezza del triangolo (un numero intero tra 3 e 15):" pull altezza select when \ datatype(altezza,"WHOLE") then /* se non è un numero */ say "Il dato introdotto non è valido!" when altezza < 3 then /* se è < 3 */ say "Il dato introdotto è troppo piccolo!" when altezza > 15 then /* se è > 15 */ say "Il dato introdotto è troppo grande!" otherwise /* se il dato è valido disegna il triangolo */ do contatore = 1 to altezza say copies("db"x, 2 * contatore - 1) end /* contatore = 1 to altezza */ end /* select */ exitAl termine del loop è ancora possibile usare la variabile impiegata per il contatore tenendo però presente che il suo valore sarà maggiore di quello indicato nell'espressione dopo il TO. Nei cicli visti precedentemente il contatore viene aumentato di 1 ad ogni esecuzione del ciclo. Con l'aggiunta di alcune parole chiave è possibile eseguire incrementi diversi da 1, negativi e non interi: DO variabile = espressione_i BY espressione_b istruzione1 istruzione2 istruzione3 . . . ENDoppure DO variabile = espressione_i BY espressione_b TO espressione_t istruzione1 istruzione2 istruzione3 . . . ENDdove BY espressione_b rappresenta l'incremento (o decremento) desiderato. Il seguente semplice esempio mostra come per l'incremento sia possibile usare amche un valore negativo e/o non intero: il valore iniziale viene decrementato e il loop viene eseguito fino a che il valore del contatore non scende al di sotto del valore che segue TO. /* decr.cmd */ i = 0 do contatore = 60 by -5.2 TO 10 i = i + 1 say "ciclo n. "i" - contatore = "contatore end say "Al termine del loop il valore del contatore è:" contatore exitUn contatore può essere implementato anche con gli altri tipi di loop visti in precedenza essendo equivalente per esempio a: variabile = espressione_i DO WHILE variabile < espressione_t istruzione1 istruzione2 istruzione3 . . . espressione_i = espressione_i + espressione_b ENDOvviamente nel caso di decremento (espressione_i = espressione_i - espressione_b) si dovrà usare ">" nella comparazione iniziale.
L'istruzione EXIT /* esempio di programma REXX */ /* elabora alcune istruzioni */ . . . . . . . . . /* se si verifica un errore restituisce "2" all'interprete dei comandi */ if errore then exit 2 /* elabora altre istruzioni */ . . . . . . . . . /* termina senza errori */ exit 0 |