Il Perl tratta ogni istruzione come una funzione che quindi restituisce un valore. Ogni valore può essere interpretato in Perl come vero o falso; in generale 0 e la stringa vuota indicano falso, mentre 1 ed una stringa non vuota (e non 0) indicano vero.
Gli operatori logici vengono generalmente usati all'interno di una struttura di controllo (come l'istruzione if-else o l'istruzione while che vedremo nelle pagine seguenti) utilizzata per variare il flusso del programma, modificandone la struttura sequenziale. Ci accorgeremo presto che in Perl, sfruttando proprio il fatto che ogni operazione restituisce comunque un valore di vero o falso, si può controllare il flusso del programma anche al di fuori delle tradizionali strutture di controllo.
Vediamo innanzi tutto in quale modo vengono rappresentati in Perl gli operatori logici. La seguente tabella ne riassume la sintassi:
$a && $b | AND | l'espressione logica è vera se $a e $b sono entrambe vere; se $a o $b è falsa allora l'espressione è falsa. |
$a || $b | OR | l'espressione logica è vera se almeno una delle due variabili è vera; se $a e $b sono entrambe false allora l'espressione è falsa. |
!$a | NOT | l'espressione logica è vera se $a è falsa; l'espressione è falsa se $a è vera. |
Il Perl ci mette a disposizione tre insiemi di operatori di confronto; ognuno di questi insiemi si applica su un tipo di dato differente: numeri, stringhe e nomi di file.
Come per ogni altro operatore il Perl effettuerà un cast (conversione di tipo) delle variabili prima di effettuare il confronto per rendere omogeneo e coerente con l'operatore il tipo dei dati trattati.
Le tabelle 8, 9 e 10 riportano i principali operatori:
$a == $b | uguaglianza | vero se $a è uguale a $b |
$a < $b | minore di | vero se $a è minore di $b |
$a <= $b | minore o uguale | vero se $a è minore o uguale a $b |
$a > $b | maggiore | vero se $a è maggiore di $b |
$a >= $b | maggiore o uguale | vero se $a è maggiore o uguale a $b |
$a != $b | diverso | vero se $a è diverso da $b |
$a eq $b | uguaglianza | vero se $a è uguale a $b |
$a lt $b | minore di | vero se $a è minore di $b (ordine alfabetico) |
$a le $b | minore o uguale | vero se $a è minore o uguale a $b (ordine alfabetico) |
$a gt $b | maggiore | vero se $a è maggiore di $b (ordine alfabetico) |
$a ge $b | maggiore o uguale | vero se $a è maggiore o uguale a $b (ordine alfabetico) |
$a ne $b | diverso | vero se $a è diverso da $b |
-r $a | leggibile | vero se il file $a è leggibile |
-w $a | scrivibile | vero se è possibile scrivere sul file $a |
-d $a | directory | vero se $a è una directory |
-f $a | file regolare | vero se $a è un file regolare (non un device o altro) |
-T $a | file di testo | vero se $a è un file di testo |
-e $a | esiste | vero se il file $a esiste |
La prima e più elementare struttura di controllo è la if-else. Permette di modificare il flusso delle istruzioni del programma a seconda che una certa condizione sia o meno verificata.
La sintassi dell'istruzione è la seguente:
if ( | condizione) { |
>primo blocco di istruzioni | |
} else { | |
secondo blocco di istruzioni | |
} |
Se la condizione è vera allora viene eseguito il primo blocco di istruzioni racchiuso tra parentesi graffe, altrimenti viene eseguito il secondo blocco di istruzioni. Al termine di ognuno dei due blocchi il flusso del programma riprende con le istruzioni presenti dopo la chiusura dell'ultima parentesi graffa.
Il blocco else può essere omesso:
if ( | condizione) { |
blocco di istruzioni | |
} |
In questo caso se la condizione è verificata allora viene eseguito il blocco di istruzioni riportato tra parentesi graffe, quindi l'esecuzione del programma continuerà con le istruzioni che seguono la parentesi graffa chiusa; se invece la condizione risulta falsa allora il blocco di istruzioni tra parentesi graffe non viene eseguito ed il programma continua semplicemente saltando il blocco.
La struttura di controllo if ha alcune interessanti variazioni che rendono in certi casi più sintetico il programma. Ad esempio è possibile utilizzare la notazione post-fissa:
istruzione if condizione
In questo caso l'istruzione (singola) viene eseguita solo se è verificata la condizione (che con la notazione post-fissa può non essere racchiusa tra parentesi tonde).
Un altro modo di utilizzare una struttura di controllo simile alla if è, come accennavo precedentemente, quello di sfruttare il fatto che l'esecuzione di ogni istruzione in Perl restituisce un valore che può essere interpretato come vero o falso.
Supponiamo quindi di voler eseguire l'istruzione istruz2
solo se l'istruzione istruz1 va a buon fine (restituisce un
valore vero); potremo allora utilizzare l'operatore AND e concatenare le due
istruzioni con la seguente espressione:
istruz1 &&
istruz2
L'espressione appena vista equivale a scrivere
if ( | istruz1) { |
istruz2; | |
} |
Viceversa se si vuole eseguire l'istruzione istruz2 solo se l'istruzione istruz1 restituisce un valore falso, potremo utilizzare la seguente espressione:
istruz1 || istruz2;
che equivale alla struttura canonica:
if ( | ! istruz1) { |
istruz2; | |
} |
Cerchiamo di chiarire meglio questo comportamento assai interessante del Perl, ma piuttosto insolito se confrontato con altri linguaggi di programmazione.
Innanzi tutto negli esempi precedenti abbiamo utilizzato una istruzione al pari di una espressione logica. La differenza tra questi due oggetti è piuttosto marcata: una istruzione serve per compiere una determinata operazione (scrivere qualcosa sul canale di output, eseguire una operazione matematica, eseguire una operazione su una stringa di caratteri, ecc.), mentre una espressione logica è un oggetto che in base al valore logico (vero o falso) delle sue componenti ci fornisce un valore logico (vero o falso). In Perl però le due cose si confondono visto che ogni istruzione restituisce comunque un valore che può essere interpretato in termini logici, ossia in termini di 'vero o falso'.
Quindi non è sbagliato utilizzare una istruzione come operatore logico che ci premette di compiere una decisione.
In secondo luogo è importante capire il modo in cui il Perl valuta le espressioni logiche. Come per ogni espressione si parte da sinistra e si procede verso destra nella valutazione delle componenti; le parentesi servono a stabilire priorità diverse da quelle standard. Inoltre, per ottimizzare i tempi di esecuzione, il Perl interrompe la valutazione di una espressione logica non appena è in grado di stabilirne il valore di vero o falso.
Quindi nel caso di una espressione del tipo 'A AND B' (in Perl scriveremo 'A && B') se dopo aver valutato l'espressione logica A si ottiene un valore falso, allora il Perl non valuterà l'espressione B, visto che questa operazione risulterebbe comunque inutile al fine della valutazione dell'espressione 'A AND B' (l'operatore logico AND restituisce vero se e solo se entrambi i termini sono veri). L'espressione B verrà quindi valutata solo se l'espressione A risulta vera.
Viceversa nella valutazione dell'espressione logica 'A OR B' se il termine A risulta vero allora il Perl non valuterà il termine B, visto che comunque basta che uno dei due termini sia vero per stabilire il valore (vero) dell'intera espressione. Se il termine A risulta falso allora il Perl non essendo ancora in grado di stabilire il valore dell'intera espressione 'A OR B' e quindi procederà alla valutazione anche del secondo termine.
In Perl esistono sostanzialmente due strutture di controllo per realizzare cicli iterativi: la struttura while e la struttura for.
La struttura while ci permette di ripetere un certo blocco di istruzioni finché l'espressione logica che controlla il ciclo risulta vera. Quando dovesse risultare falsa il flusso del programma uscirebbe fuori dal ciclo. Se l'espressione è falsa già prima di entrare nel ciclo while allora questo non verrà eseguito neanche una volta. La sintassi è la seguente:
while | (espressione) { |
blocco di istruzioni | |
} |
Vediamo tre esempi di come può essere utilizzata la struttura iterativa while. Il primo è il più semplice: il ciclo viene ripetuto fino a quando la variabile flag $n non assume il valore zero.
#!/usr/local/bin/perl $n=10; while ($n > 0) { $n--; print "$n "; } |
Il secondo esempio legge e stampa il contenuto di un file:
#!/usr/local/bin/perl open(IN, "< /tmp/dati") || die "Impossibile aprire il file\n\n"; while ($r = <IN>) { print $r; } close(IN); |
Infine il terzo esempio visualizza il contenuto di una lista:
#!/usr/local/bin/perl @frutta = ("mele", "pere", "pesche", "albicocche"); while (@frutta) { $frutto = shift @frutta; print "$frutto\n"; } |
La struttura for consente di ripetere un numero prefissato di volte un certo blocco di istruzioni, controllando la ripetizione del ciclo mediante un contatore. La sintassi dell'istruzione for è la seguente:
for ( | condizione iniziale; condizione finale; incremento) { |
blocco di istruzioni | |
} |
Nell'esempio seguente viene utilizzato un ciclo for per stampare tutti gli elementi di un array.
#!/usr/local/bin/perl @frutta = ("mele", "pere", "pesche", "albicocche"); for ($i=0; $i<=$#frutta; $i++) { print "$frutta[$i]\n"; } |
La variabile contatore $i assume inizialmente il valore 0, viene incrementata ad ogni ciclo di 1, fino a quando non raggiunge il valore pari al numero di elementi dell'array @frutta (ricordiamo che questo numero è espresso da $#frutta).
Una struttura di controllo per iterare un blocco di istruzioni, simile alla for, è la foreach, che consente di inserire in una variabile scalare uno dopo l'altro tutti gli elementi di una lista. Il seguente esempio, del tutto equivalente al precedente, ci sarà di aiuto per comprendere il funzionamento di questa istruzione:
#!/usr/local/bin/perl @frutta = ("mele", "pere", "pesche", "albicocche"); foreach $frutto (@frutta) { print "$frutto\n"; } |
Ad ogni iterazione del ciclo la variabile scalare $frutto assume un valore pari a quello dell'elemento 'corrente' della lista @frutta.
Chiudiamo questo capitolo riportando altre due istruzioni che possono risultare utili se utilizzate in modo oculato all'interno di un cliclo.
L'istruzione next forza il Perl a saltare alla successiva iterazione del ciclo, senza eseguire le rimanenti istruzioni del blocco.
L'istruzione last invece termina l'esecuzione di un ciclo facendo saltare il flusso del programma alla prima istruzione successiva al ciclo stesso.
Vediamo con un esempio un possibile utilizzo di queste due potenti istruzioni. Supponiamo di voler leggere il contenuto di un file e di volerne stampare solo le prime 20 linee saltando però tutte le linee vuote. Le linee stampate dovranno anche essere numerate. Il programma potrebbe essere il seguente:
#!/usr/local/bin/perl $cont = 0; open (IN, "< /tmp/data") || die "Impossibile aprire il file\n\n"; while ($riga = <IN>) { chop($riga); $riga || next; $cont++; print "$cont) $riga\n"; ($cont == 20) && last; } close(IN); |