Procedure con argomenti

Le procedure viste nelle pagine precedenti erano semplicemente un modo per scrivere una volta sola una sequenza di istruzioni che andava eseguita in più punti del programma. In più esisteva la limitazione che le variabili del programma non potevano venire usate dalla procedura. Ci sono dei casi in cui questa limitazione è un problema. Si pensi al caso in cui, in più punti del programma, occorre stampare un certo numero di linee bianche. Il caso in cui occorre stampare sempre lo stesso numero di linee (per esempio, 20) è stato già analizzato nella pagina sulle variabili e procedure. Consideriamo ora il caso in cui in un certo punto del programma occorre stampare 10 linee bianche, in un altro 20, in un altro 2, ecc. Chiaramente il programma già visto non va bene, dal momento che la procedura lineebianche() stampa sempre lo stesso numero di linee bianche (20).

Se le variabili del programma principali fossero visibili (=utilizzabili) nella procedura, sarebbe facile: la procedura dovrebbe effettuare un ciclo partendo da 1 fino al valore di una certa variabile che rappresenta il numero di linee da stampare. In altre parole si potrebbe (ma non si può) fare cosí:

class Linee {
  static void lineebianche() {		/* NON FUNZIONA */
    int i;

    for(i=1; i<=lineedalasciare; i=i+1) {
      System.out.println(" ");
    }
  }

  public static void main(String[] args) {
    int lineedalasciare;

    /* stampa 10 linee */
    lineedalasciare=10;
    lineebianche();

    ...

    /* stampa 20 linee */
    lineedalasciare=20;
    lineebianche();
  }
}

Questo funzionerebbe se la procedura potesse utilizzare le variabili del programma. Sfortunatamente questo non è possibile. Esiste però un meccanismo che consente ad una procedura di ricevere dei dati dal programma. Usando questo metodo, il programma può ``comunicare'' alla procedura il numero di linee bianche che vanno lasciate. Questo meccanismo si chiama passaggio dei parametri.

Si può pensare al passaggio dei parametri come a un tipo di ``trasmissione'', in cui il programma trasmette dei dati alla procedura (nell'esempio quante linee bianche stampare); la procedura riceve questi dati e li può usare.

Per poter effettuare questa trasmissione, è necessario che la procedura sia definita in modo tale da poter ricevere questi parametri. La definizione della procedura viene modificata in questo modo: dopo il nome, viene aggiunta la lista dei dati che la procedura può ricevere:

static void nome(lista dei parametri)
I parametri sono appunto i dati che il programma trasmette alla procedura. La lista dei parametri deve contenere il tipo di questi dati. In altre parole, per ogni dato che la procedura può ricevere occorre specificare quale è il suo tipo, se int, double, ecc. Oltre al tipo, per ogni parametro è necessario utilizzare un nome, in modo tale che la procedura possa poi utilizzare il dato ricevuto come se fosse una qualsiasi variabile.

Queste cose sono più facili da far vedere su un esempio che da spiegare. Nel caso della procedura che stampa un certo numero di linee bianche, il programma trasmette un unico dato, che è il numero di linee da stampare. Logicamente, la procedura riceve solo questo dato, quindi nella lista dei parametri c'è un unico parametro, che è di tipo intero. La definizione della procedura viene quindi modificata come segue:

  static void lineebianche(int ...) {
    int i;

    for(i=1; i<=lineedalasciare; i=i+1) {
      System.out.println(" ");
    }
  }
A questo punto, occorre un modo per far sì cha la procedura possa usare questo numero. Si ricordi che la procedura è una unità indipendente dal programma, quindi non ``sa'' come è fatto il programma che la usa. Il meccanismo che si usa è quello di associare una variabile al dato che è stato passato. In altre parole, si specifica che il dato che il programma ha passato va messo in una variabile di cui diamo noi il nome. In questo modo, la procedura può accedere al dato che è stato trasmesso dal programma semplicemente usando la variabile. La definizione della procedura, oltre al tipo di ogni dato che viene passato, specifica un nome di variabile, che è il posto dove il dato ricevuto viene memorizzato.

  static void lineebianche(int l) {
    int i;

    for(i=1; i<=l; i=i+1) {
      System.out.println(" ");
    }
  }
Nelle istruzioni che compongono la procedura, l si comporta esattamente come una qualsiasi variabile. Il punto importante è che all'inizio in questa variabile viene memorizzato il dato che il programma ha trasmesso.

Manca ora da specificare in che modo il programma trasmette i parametri alle procedure. Nel caso di procedure senza parametri, per indicare che si voleva la esecuzione di una procedura, si usava la istruzione nome(), in cui ``nome'' è il nome della procedura. Nel caso in cui il programma vuole passare dei parametri, questi vanno semplicemente messi, l'uno dietro l'altro e separati da virgole, fra le due parentesi tonde: nome(dato1, dato2, dato3,...); Nel caso della stampa di linee bianche, il programma finale è fatto come segue.

class Linee {
  static void lineebianche(int l) {
    int i;

    for(i=1; i<=l; i=i+1) {
      System.out.println(" ");
    }
  }

  public static void main(String[] args) {

    /* stampa 10 linee */
    lineebianche(10);

    ...

    /* stampa 20 linee */
    lineebianche(20);
  }
}
Cosa succede quando il programma viene eseguito? La prima istruzione è lineebianche(10); La esecuzione di questa istruzione comporta che:
  1. viene chiamata la procedura lineebianche
  2. a questa procedura viene mandato il dato intero 10.
La procedura ha un parametro di tipo intero: può ricevere un dato intero, e questo viene memorizzato nella variabile l:
  1. si crea la variabile intera l, e ci si mette 10
  2. si eseguono le istruzioni di lineebianche.

Viene quindi creata una variabile di tipo intero l, in cui viene messo il valore 10. A questo punto, si esegue il resto della procedura come al solito: si crea la variabile i e si esegue il ciclo.

La successiva istruzione è lineebianche(20). Qui avviene tutto come prima: il programma passa il valore 20 alla procedura, la quale lo riceve e lo memorizza nella variabile l, al che vengono eseguite le altre istruzioni della procedura.

Graficamente, la situazione è la seguente: inizialmente abbiamo un contenitore (zona di memoria) per il programma principale (main) e un contenitore per la procedura lineebianche. Inizialmente, non sono definite variabili, quindi abbiamo:

lineebianche
 
    
    
 
    
 
 
 
main
 
    
      
 

Viene chiamata lineebianche(10). Questo corrisponde a: primo, si crea una variabile l che è una variabile locale a lineebianche, in cui viene messo il valore passato, cioè 10; secondo, si eseguono le istruzioni di lineebianche.

lineebianche
l
  10
    
    
 
 
 
main
 
    
      
 

La procedura lineebianche contiene una dichiarazione di un'altra variabile intera i che è sempre locale alla procedura. Si ha quindi la situazione seguente.

lineebianche
l
  10
    
i
    
 
 
 
main
 
    
      
 

La procedura contiene anche un ciclo in cui i assume tutti i valori interi da 1 al valore attualmente memorizzato in l. All'interno del ciclo si stampa una linea bianca. Quindi, alla fine verranno stampate tante linee bianche quanto è il valore di l. Questo è esattamente quello che si voleva ottenere.

Le pagine successive mostrano esempi di procedure che ricevono argomenti: stampa di valori solo se positivi e la stampa del grafico di una funzione con istruzioni di testo.