Logo

Marco Cantù
Essential Pascal


Tradotto dall'inglese da: Paolo Rossi

Capitolo 5
Le istruzioni

Se i tipi di dato sono una delle fondamenta della programmazione Pascal le istruzioni sono le altre. Le istruzioni dei linguaggi di programmazione sono basate sulle keyword e altri elementi i quali permettono di indicare al programma una sequenza di operazioni da compiere. Le istruzioni sono spesso racchiuse in procedure o funzioni, come si potra' vedere nel prossimo capitolo. Adesso si concentrera' l'attenzione solo sui comandi di base che si possono usare per creare un programma.

Istruzioni semplici e composte

Un'istruzione Pascal e' semplice quando non contiene nessun altra istruzione. Esempi di istruzioni semplici sono istruzioni di assegnamento e chiamate a procedure. Le istruzioni semplici sono separate da un punto e virgola:

X := Y + Z;  // assignment
Randomize;   // procedure call

Di solito, le istruzioni sono parte di un'istruzione complessa, raggruppata tra un begin e un end. Un'istruzione composta puo' apparire al posto di un'istruzione Pascal generica. Ecco un esempio:

begin
  A := B;
  C := A * 2;
end;

Il punto e virgola dopo l'ultima istruzione prima dell'end non e' richiesto, come nel seguente codice:

begin
  A := B;
  C := A * 2
end;

Entrambe le versioni sono corrette. La prima versione ha un inutile (ma innocuo) punto e virgola. Questo punto e virgola e', di fatto, un'istruzione nulla che e' un'istruzione senza codice. Notare che, talvolta, le istruzioni nulle possono essere usate all'interno di un ciclo o in altri particolari casi.

Nota: Benche' questi punto e virgolafinali non hanno nessuno scopo, tendo ad usarli e suggerisco di fare altrettanto. Alcune volte dopo aver scritto diverse righe si puo' aver bisogno di aggiungere ancora alcune istruzioni. Se l'ultimo punto e virgola e' mancante bisogna per forza ricordarsi di aggiungerlo, cosi' puo' essere consigliabile inserirlo da subito.

Istruzioni di assegnamento

Gli assegnamenti in Pascal usano l'oeratore := (due punti-uguale), una singolare notazione per i programmatori che hanno usato altri linguaggi. L'operatore = (uguale), che e' usato per l'assegnamento in altri linguaggi, in Pascal e' usato per eseguire i test di uguaglianza.

Nota: Usando differenti simboli per un assegnamento e per il test d'uguaglianza, il compilatore Pascal (come il compilatore C) puo' tradurre il codice sorgente piu' velocemente, siccome esso non deve esaminare il contesto nel quale l'operatore e' usato per determinarne il significato. L'uso di differenti operatori rende anche il codice piu' facilmente leggibile.

Istruzioni Condizionali

Un'istruzione condizionale e' usata per eseguire sia un'istruzione che contiene o nessuna di esse, dipende da alcuni test. Ci sono due tipi di istruzioni condizionali: l'istruzione if e l'istruzione case.

Istruzione If

L'istruzione if puo' essere usata per eseguire un'istruzione solo se una certa condizione e' verificata (if-then), o per scegliere tra due differenti alternative (if-then-else). La condizione e' descritta mediante un'espressione booleana. Un semplice esempio di Delphi dimostra come scrivere istruzioni condizionali. Primo creare una nuova applicazione, mettere due check-box e quattro button nel form. Non cambiare il nome dei pulsanti o dei checkbox, fare doppio click su ogni pulsante per aggiungere un gestore per l'evento OnClick. Ecco una semplice istruzione if per il primo pulsante.

procedure TForm1.Button1Click(Sender: TObject);
begin
  // simple if statement
  if CheckBox1.Checked then
    ShowMessage ('CheckBox1 is checked')
end;

Quando si clicca sul pulsante, se il primo checkbox e' selezionato, il programma mostrera' un semplice messaggio (vedere Figura 5.1). Ho usato la funzione ShowMessage siccome e' la funzione Delphi piu' semplice per mostrare un piccolo messaggio all'utente.

Figura 5.1: Il messaggio visualizzato dall'esempio IfTest quandosi preme il primo pulsante e il primo checkbox e' selezionato.

Se si clicca il pulsante e non accade nulla, vuol dire che il checkbox non e' stato selezionato. In un caso come questo, sarebbeprobabilmente meglio rendere cio' piu' esplicito, come con il codice per il secondo pulsante, il quale usa un'istruzione if-then-else:

b>procedure TForm1.Button2Click(Sender: TObject);
begin
  // if-then-else statement
  if CheckBox2.Checked then
    ShowMessage ('CheckBox2 is checked')
  else
    ShowMessage ('CheckBox2 is NOT checked');
end;

Notare che nopn si puo' avere un punto e virgola dopo la prima istruzione e prima della keyword else, o il compilatore generera' un errore di sintassi. L'istruzione if-then-else, di fatto, e' una singola istruzione, cosi' non si puo' mettere un punto e virgola in mezzo ad essa.

Un'istruzione if puo' essere abbastanza complessa. La condizione puo essere trasformata in una serie di condizioni (usando gli operatori booleani and, or e not), o l'istruzione if puo' contenere una seconda istruzione if. Gli ultimi due pulsanti dell'esempio IfTest dimostrano questi casi:

procedure TForm1.Button3Click(Sender: TObject);
begin
  // statement with a double condition
  if CheckBox1.Checked and CheckBox2.Checked then
    ShowMessage ('Both check boxes are checked')
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  // compound if statement
  if CheckBox1.Checked then
    if CheckBox2.Checked then
      ShowMessage ('CheckBox1 and 2 are checked')
    else
      ShowMessage ('Only CheckBox1 is checked')
  else
    ShowMessage (
      'Checkbox1 is not checked, who cares for Checkbox2?')
end;

Si prega di guardare il codice con attenzione ed eseguire il programma per veder se si comprende tutto. Quando si hanno dei dubbi riguardo un costrutto di programmazione, scrivere un semplice programma come questo puo' aiutare molto la comprensione. Si possono aggiungere piu' checkbox ed incrementare la complessita' di questo piccolo esempio, costruento qualsiasi test.

L'istruzione Case

Se un'istruzione if diventa molto complessa, a volte si puo' sostituire con un'istruzione case. Un'istruzione case consiste in un'espressione usata per scegliere un valore, una lista di possibili valori, o un intervallo di valori. Questi valori sono costanti, e devono essere unici e di tipo ordinale. Eventualmente, ci puo' essere un'istruzione else che e' eseguita se nessuna etichetta corrisponde al valore del selettore. Ecco due semplici esempi:

case Number of
  1: Text := 'One';
  2: Text := 'Two';
  3: Text := 'Three';
end;

case MyChar of
  '+' : Text := 'Plus sign';
  '-' : Text := 'Minus sign';
  '*', '/': Text := 'Multiplication or division';
  '0'..'9': Text := 'Number';
  'a'..'z': Text := 'Lowercase character';
  'A'..'Z': Text := 'Uppercase character';
else
  Text := 'Unknown character';
end;

Cicli in Pascal

Il linguaggio Pascal ha le tipiche istruzioni di ripetizione di molti altri linguaggi di programmazione, incluso le istruzioni for, while, e repeat. La maggioranza di queste istruzioni saranno familiari a chi conosce altri linguaggi, cosi verranno discusse brevemente.

Il Ciclo For

Il ciclo for in Pascal e' strettamente basato su un contatore, il quale puo' essere sia incrementato che decrementato ogni volta che il ciclo viene eseguito. Ecco un semplice esempio di un ciclo for usato per sommare i primi dieci numeri:

var
  K: Integer;
begin
  K := 0;
  for I := 1 to 10 do
    K := K + I;

Questa stessa istruzione puo' essere riscritta usando un contatore inverso:

begin
  K := 0;
  for I := 10 downto 1 do
    K := K + I;

Il ciclo for in Pascal e' meno flessibile che in altri linguaggi (non e' possibile specificare un incremento diverso da uno) ma e' semplice e facile da comprendere. Se si vuole testare una condizione piu' complessa o provveder un contatore personalizzato, bisogna usare l'istruzione while o repeat invece di un ciclo for.

Nota: Il contatore di un ciclo for non deve essere necessariamente un numero. Esso puo' essere un valore di qualsiasi tipo ordinale, ad esempio un carattere o un tipo enumerativo.

Istruzioni While e Repeat

La differenza tra un ciclo while-do e repeat-until e' che il codice del ciclo repeat e' sempre eseguito almeno una volta. Si puo' facilmente capire perche' guardando il prossimo esempio:

while (I < 100) and (J < 100) do
begin
  // use I and J to compute something...
  I := I + 1;
  J := J + 1;
end;

repeat
  // use I and J to compute something...
  I := I + 1;
  J := J + 1;
until (I > 100) or (J > 100);

Se il valore iniziale di I e' maggiore di 100, l'istruzione dentro al ciclo repeat e' eseguita comunque una volta.

Nota: L'altra differenza chiave tra questi due tipi di cicli e' che il ciclo repeat-until ha una condizione inversa. Il ciclo e' eseguito finche' la condizione non e' soddisfatta. Quando la condizione e' soddisfatta, il ciclo termina. Questo e' l'opposto che in un ciclo while-do, il quale e' eseguito mentre la condizione e' vera. Per questa ragione bisogna invertire la condizione nel codice sopra per ottenere un'istruzione simile.

Un esempio di Ciclo

Per esplorare i dettagli dei cicli, si puo' guardare un piccolo esempio Delphi. Il programma Loops sottolinea la differenza tra un ciclo con un contatore fisso e un ciclo con un contatore quasi casuale. Aprire un progetto nuovo, piazzare una listbox e due pulsanti sul form principale e dare ai pulsanti un nome appropriato (BtnFor e BtnWhile) settando la loro proprieta' Name nell'Object Inspector. Si puo' anche la parola Btn dalla proprieta' Caption (ed eventualmente aggiungere anche il carattere & per attivare la lettera successiva come un tasto scorciatoia). Di seguito il sommario della descrizione testuale del form d'esempio:

object Form1: TForm1
  Caption = 'Loops'
  object ListBox1: TListBox ...
  object BtnFor: TButton
    Caption = '&For'
    OnClick = BtnForClick
  end
  object BtnWhile: TButton
    Caption = '&While'
    OnClick = BtnWhileClick
  end
end

Figura 5.2: Ogni volta che si preme il pulsante for dell'esempio Loops, la listbox e' riempita con numeri consecutivi.

Adesso si puo' aggiungere codice all'evento OnClick dei due pulsanti. Il primo pulsante ha un semplice ciclo for per visualizzare una lista di numeri, come si puo' vedere nella Figura 5.2. Prima di eseguire questo ciclo, il quale aggiunge parecchie stringhe alla proprieta' Items della listbox, bisogna pulire il contenuto della listbox stessa:

procedure TForm1.BtnForClick(Sender: TObject);
var
  I: Integer;
begin
  ListBox1.Items.Clear;
  for I := 1 to 20 do
    Listbox1.Items.Add ('String ' + IntToStr (I));
end;

Il codice associato con il secondo pulsante e' leggermente piu' complesso. In questo caso c'e' un ciclo while basato su un contatore, che e' incrementato casualmente. Per realizzare cio', ho invocato la procedura Randomize, che resetta il generatore di numeri casuali, e la funzione Random con un intervallo di valori di 100. Il risultato di questa funzione e' un numero compreso tra 0 e 99, scelto in modo casuale. La serie di numeri casuali controlla quante volte il ciclo while e' eseguito.

procedure TForm1.BtnWhileClick(Sender: TObject);
var
  I: Integer;
begin
  ListBox1.Items.Clear;
  Randomize;
  I := 0;
  while I < 1000 do
  begin
    I := I + Random (100);
    Listbox1.Items.Add ('Random Number: ' + IntToStr (I));
  end;
end;

Ogni volta che si preme il pulsante While, i numeri sono sempre diversi, siccome dipendono dal generatore di numeri casuali. La Figura 5.3 mostra il risultato di due distinti click sul pulsante. Da notare che non solo i numeri generati sono differenti, ma anche il numero di elementi. Vale a dire che il ciclo while e' eseguito un numero casuale di volte. Se si preme il pulsante While per diverse volte, si vedra' che la listbox avra' di volta in volta numeri di linee differenti.

Figura 5.3: Il contenuto della listbox dell'esempio Loops cambia ogni volta che viene premuto il pulsante While. Siccome il contatore del ciclo e' incrementato da un valore casuale, ogni volta che viene premuto il pulsante, il ciclo verra' eseguito un numero di volte sempre diverso.

Nota: Si puo' alterare il flusso standard dell'esecuzione di un ciclo usando le procedure di sistema Break e Continue. La prima interrompe il ciclo, la seconda e' usata e' usata per saltare direttamente alla parte di test del ciclo o all'icremento del contatore, continuando con la successiva iterazione del ciclo (salvo che la condizione sia zero o il contatore abbia raggiunto il valore limite). Altre due procedure di sistema, Exit e Halt, permettono di uscire dalla funzione o procedura corrente o terminare il programma.

L'istruzione With

L'ultimo tipo di istruzione Pascal su cui mi soffermo e' l'istruzione With, la quale e' peculiare di questo linguaggio di programmazione (recentemente e' stata introdotta in Visual Basic) e molto utile nella programmazione Delphi.

L'istruzione With e' solo per brevita' di scrittura. Quando bisogna riferirsi ad una variabile di tipo record (o ad un oggetto), invece di ripetere il suo nome ogni volta, si puo' usare l'istruzione with. Per esempio, mentre presentavo il tipo record ho scritto il seguente codice:

type
  Date = record
    Year: Integer;
    Month: Byte;
    Day: Byte;
  end;

var
  BirthDay: Date;

begin
  BirthDay.Year := 1997;
  BirthDay.Month := 2;
  BirthDay.Day := 14;

Usando l'istruzione with, posso migliorare la parte finale del codice come segue:

begin
  with BirthDay do
  begin
    Year := 1995;
    Month := 2;
    Day := 14;
  end;

Questo approcio puo' essere usato nei programmi Delphi per riferirsi a componenti e altri tipi classe. Ad esempio, si puo' riscrivere la parte finale dell'ultimo esempio, Loops, usando l'istruzione with per accedere agli items della listbox:

procedure TForm1.WhileButtonClick(Sender: TObject);
var
  I: Integer;
begin
  with ListBox1.Items do
  begin
    Clear; // shortcut
    Randomize;
    I := 0;
    while I < 1000 do
    begin
      I := I + Random (100);
      // shortcut:
      Add ('Random Number: ' + IntToStr (I));
    end;
  end;
end;

Quandi si lavora con componenti o in generale con classi, l'struzione with permette di saltare la scrittura di diverso codice, particolarmente per campi nidificati. Per esempio, si supponga di volor cambiare le proprieta' Width e Color di un oggetto Pen. Si puo' scrivere in seguente codice:

Form1.Canvas.Pen.Width := 2;
Form1.Canvas.Pen.Color := clRed;

Sicuramente e' piu' semplice scrivere il seguente:

with Form1.Canvas.Pen do
begin
  Width := 2;
  Color := clRed;
end;

Quando si sta scrivendo codice complesso, l'istruzione with puo' essere efficace e fa risparmiare la dichiarazione di diverse variabili temporanee ma ha anche un lato negativo. Puo' rendere il codice meno leggibile, particolarmente mentre si usando diverso oggetti con proprieta' corrispondenti.

Un altro svantaggio e' che usando l'istruzione with possono esserci sottili errori di logica che il compilatore non e' in grado di rilevare. Ad esempio:

with Button1 do
begin
  Width := 200;
  Caption := 'New Caption';
  Color := clRed;
end;

Questo codice cambia Caption e Width di un pulsante ma influisce anche sulla proprieta' Color della form, non quella del pulsante! La ragione e' che il componente TButton non ha la proprieta' Color e siccome il codice e' eseguito dall'oggetto Form (si sta scrivendo un metodo della form) questo oggetto e' disponibile di default. Se invece si fosse scritto:

Button1.Width := 200;
Button1.Caption := 'New Caption';
Button1.Color := clRed; // error!

il compilatore avrebbe generato un errore. In generale, si puo' dire che siccome l'istruzione with introduce nuovi identificatori nel contesto corrente, si possono nascondere gli identificatori esestenti, o accedere erroneamente un altro identificatore nel medesimo contesto (come nella prima versione del codice). Anche considerando questi svantaggi, suggerisco di usare l'istruzione with, siccome puo' essere molto pratica e a volte rende il codice piu' leggibile.

Bisogna comunque evitare di usare istruzioni with multiple, come:

with ListBox1, Button1 do...

Il codice che segue a questa riga probabilmente sara' davvero illeggibile, visto che per ogni proprieta' definita in questo blocco bisogna pensare a quale componente appartiene, e questo dipende dalle rispettive proprieta' e dall'ordine dei componenti nell'istruzione with.

Nota: Parlando di leggibilita', il Pascal non ha istruzioni endif o endcase. Se un'istruzione if ha un blocco begin-end, allora la fine del blocco e' la fine dell'istruzione. L'istruzione case, invece, e' sempre terminata da un end. Tutti questi end, che spesso si trovano di seguito, possono rendere il codice difficile da seguire. Solamente seguendo l'indentazione si puo' vedere a quale istruzione un particolare end si riferisce. Un metodo comune per risolvere questo proble e rendere il codice piu' leggibile, e' di aggiungere un commento dopo l'istruzione end che indichi il suo ruolo, come in:

if ... then
 ...
end; // if

Conclusioni

Questo capitolo ha descritto come scrivere istruzioni condizionali e cicli. Invece di scrivere lunghi listati, i programmi di solito si dividono in routine: procedure o funzioni. Questo e' l'argomento del prossimo capitolo, il quale introduce anche alcuni elementi avanzati del Pascal.

Prossimo Capitolo: Procedure e funzioni

© Copyright Marco Cantù, Wintech Italia Srl 1995-2000