Funzioni che ritornano un valore

Fino ad ora abbiamo visto due tipi di procedure: quelle che eseguono una sequenza di istruzioni senza ricevere dati dal programma, e quelle che ricevono dati dal programma e li usano per fare qualcosa. Manca ora l'ultima parte, ossia: fare in modo che la procedura possa mandare dei dati al programma principale.

Questo è utile per esempio se il programma contiene molte volte la stessa espressione. Consideriamo il programma che trova un intervallo in cui la funzione contiene uno zero Nullo.java. In questo programma, si ripete per cinque volte una istruzione in cui si valuta il valore della funzione per valori diversi dell'argomento. Farebbe comodo poter scrivere la funzione una volta sola. Con le cose che sono state viste fino ad ora, il calcolo non è difficile da inserire in una procedura:

  static void f( double x ) {
    double risultato;

    risultato=x*x-5*x-2;
  }
Questa procedura crea una variabile reale x, in cui viene messo il dato che la procedura principale manda; viene poi creata un'altra variabile reale risultato. Si valuta la espressione (usando il valore della variabile x, che è quello passato dalla variabile principale) e la si mette in risultato. Da un certo punto di vista, siamo effettivamente riusciti a scrivere l'espressione una volta sola, soltanto che ancora ci manca il modo in cui la procedura rimanda il valore del risultato al programma.

Come si scrive una procedura che manda risultati al programma

Si fa in due passi. Per prima cosa, occorre dire quale è il tipo del risultato, ossia se la procedura vuole inviare al programma un intero oppure un reale, ecc. Per fare questo si modifica la prima linea della procedura, come segue:

  static double f( double x ) {
    ...
  }
La parole void è stata rimpiazzata dalla parola double. Questo specifica il tipo di dato che la procedura rimanda al programma principale. Si spiega ora il piccolo mistero dalla parola void usata nelle procedure che non tornano valori al programma: dichiara che il tipo di dati che la procedura rimanda al programma è vuoto, ossia la procedura non rimanda niente.

Il secondo passo è quello di specificare esattamente che cosa la procedura restituisce al programma. Per fare questo si usa la istruzione:

return espressione

Questa istruzione ha due effetti: la prima è che l'esecuzione della procedura termina; la seconda è che la espressione viene valutata, e il suo valore viene mandato indietro al programma.

La procedura di calcolo della funzione va quindi modificata in maniera tale che il contenuto della variabile risultato venga rimandato al programma principale. Per fare questo è sufficiente aggiungere come ultima istruzione della procedura il return:

  static double f( double x ) {
    double risultato;

    risultato=x*x-5*x-2;

    return risultato;
  }
Si noti che quello che segue la parola return può essere sia una variabile che una espressione complessa. Quindi, la procedura si può semplificare mettendo l'espressione da calcolare direttamente dopo la parola return, rendendo inutile la variabile risultato:

  static double f( double x ) {
    return x*x-5*x-2;
  }

Come fa il programma a usare il valore che la procedura ritorna

Questo è l'ultimo passo: abbiamo visto come si fa a specificare che una procedura calcola valori da rimandare al programma; ora dobbiamo dire come si fa a usare questi valori nel programma.

Nel caso in cui una procedura nomeproc non mandava dati al programma, per far eseguire le sue istruzioni si usava una singola istruzione nomeproc(....) in cui si mettevano fra parentesi i dati che il programma mandava. Nel caso in cui la procedura ritorna un valore, si usa invece:

     variabile = nomeproc(...);
Questa istruzione significa: esegui la procedura mandando i dati fra parentesi; il risultato che la procedura manda mettilo nella variabile.

Più in generale, se una procedura f(...) ritorna un dato di un certo tipo, allora si può scrivere f(...) in qualsiasi punto in cui è possibile usare una variabile di quel tipo. Per esempio, se vogliamo assegnare a x la media fra il risultato di f e 5, possiamo anche scrivere: x=(f(...)+5)/2;. In altre parole, se una espressione contiene una o più procedure, queste vengono eseguite, e al loro posto ci si mette il valore che la procedura ha rimandato al programma.

Nota: le procedure che ritornano un valore vengono chiamate funzioni.

Il programma NulloFunzione.java trova un intervallo piccolo in cui la funzione ha uno zero, e questo viene fatto usando una funzione (procedura che ritorna un valore).

/*
  Trova un punto in cui una funzione f(x) ha un
  valore sufficientemente vicino allo zero.
  Siano a e b due valori tali che f(a) ha segno
  opposto a f(b). Si assume che la funzione sia
  continua.
  Variante con chiamata a funzione.
*/

class NulloFunzione {

    /* la funzione di cui si vuole trovare lo zero */

  static double f( double x ) {
    return x*x-5*x-2;
  }


    /* la procedura principale */

  public static void main(String[] args) {
    double a=0,b=10;
    double e=0.01;
    double x=a;

    if( f(a)*f(b) >0 ) {
      System.out.println("La funzione non ha segno diverso negli estremi");
      System.out.println(f(a)+" "+f(b));
      return;
    }

    while( Math.abs(f(x))>e ) {
      x=(a+b)/2;
      System.out.println(a+" "+x+" "+b);

      if( f(x)*f(a) > 0 ) {
        a=x;
      }
      else {
        b=x; 
      }
    }

    System.out.println("Trovato valore "+f(x)+" per x pari a "+x);
  }
}

Nel caso ci fosse un qualche dubbio sull'uso che si può fare delle funzioni, si tenga presente che è sempre possibile usare una istruzione del tipo variabile=nomefunzione(...), che mette il risultato della funzione nella variabile, e poi utilizzare il valore della variabile nel punto in cui serve il risultato calcolato della funzione.