Programmazione per TWM: Esercitazione di laboratorio #9 (18/02)

L'obiettivo di questa esercitazione è l'approfondimento dei sottoprogrammi, o metodi (illustrati nel capitolo 5).

Raccomandazioni: Ove non altrimenti indicato, rispondete alle domande prima ragionando su carta e poi provando a editare, compilare ed eseguire. Come vedrete, per poter lavorare sui metodi è indispensabile conoscere le strutture di controllo della programmazione strutturata, e gli array, visti nelle lezioni ed esercitazioni precedenti. Gli esercizi etichettati con l'asterisco (*) sono più difficili: affrontateli dopo aver risolto gli altri.

Esercizio 1

Obiettivo: Comprendere i meccanismi di base del funzionamento dei metodi. Questi sono tutti esercizi molto semplici, che tutti dovete sapere svolgere.

Attività:

  1. Qual è l'output del programma seguente? (rispondete senza eseguirlo, poi controllate la vostra risposta compilandolo ed eseguendolo)
    class Metodi1 {
      static void m1() {
        System.out.println("    Prima di chiamare m3");
        m3();
        System.out.println("    Dopo aver chiamato m3");
      }
    
      static void m2() {
        System.out.println("  Prima di chiamare m1");
        m1();
        System.out.println("  Dopo aver chiamato m1");
      }
    
      static void m3() {
        int y;
        System.out.println("      Prima di chiamare m4 con parametro 2");
        y = m4(2);
        System.out.println("      Dopo aver chiamato m4. Risultato: " + y);
      }
    
      static int m4(int x) {
        return x + 1;
      }
        
      public static void main(String[] args){
        System.out.println("Prima di chiamare m2");
        m2();
        System.out.println("Dopo aver chiamato m2");
      }
    }
    
  2. Come modificare il programma precedente per far eseguire prima m1 (e poi m3 e m4, quindi senza m2)? (bisogna modificare un'unica istruzione)
  3. E come fare per far eseguire in ordine m1, poi m2, poi e m3, e poi m4, modificando solo alcune istruzioni di chiamata (servono solo 3 minime modifiche al programma originale)?
    Come potete vedere, i metodi possono essere usati per specificare l'ordine di esecuzione delle istruzioni. Osservate come la chiamata dei metodi sia annidata: se un metodo m invoca un altro metodo n, l'esecuzione di n deve terminare prima dell'esecuzione di m. In altri termini: l'ultimo invocato è il primo a terminare (LIFO, Last In First Out...).
  4. Il metodo EasyIn.readInt() è una procedura o una funzione? E il metodo Math.sin()?
  5. Qual è l'output del programma seguente? (rispondete senza eseguirlo, poi controllate la vostra risposta compilandolo ed eseguendolo)
    class Metodi2 {
      static void m(int x) {
        System.out.println("m invocato con parametro attuale: " + x);
      }
      
      public static void main(String[] args){
        int y;
        int[] a = new int[2];
        y = 5;
        a[1] = 8;
        m(y);
        m(y+1);
        m(a[1]);
        m(2*4+3);
        y = EasyIn.readInt();
        m(y);
      }
    }
    
    Come potete vedere, come parametro attuale si può usare una qualsiasi espressione (purché sia del tipo corretto).
  6. Provate a dare 2*4+3 in input durante l'esecuzione (e quindi in corrispondenza della riga y = EasyIn.readInt();): cosa succede? Perché? Perché invece nella riga precedente m(2*4+3) si può invocare m con, come parametro attuale la stessa espressione? Che differenza c'è? Chi è che valuta quell'espressione nei due casi?
  7. Che errori restituisce il compilatore sul programma seguente? (rispondete senza compilarlo, poi controllate la vostra risposta compilandolo)
    class Metodi3 {
      static void m1(int x) {
        System.out.println("Metodo m1. Paramtro: " + x);
      }
    
      public static void main (String[] args) {
        int a;
        int b;
        boolean p;
        double x;
        a = 2;
        p = true;
        x = 2.0;
        m1(a);
        b = m1(a);
        m1(b);
        m1(p);
        m1(x);
      }
    }
    
    Studiate i messaggi di errore, cercando di capirli.
  8. Ripetete l'esercizio precedente, modificando la dichiarazione del metodo m1 con la seguente:
      static int m1(int x) {
        System.out.println("Metodo m1. Parametro: " + x);
        return x;
      }
    
    (ora m1() è una funzione). Notate che il compilatore vi consente di invocare una funzione (un metodo che restituisce un valore) senza assegnare a nulla il valore restituito: non vi è nessun errore, ad esempio, alla riga m1(a);. È comunque un esempio di cattiva programmazione: evitatelo.
  9. Cosa succede se rimuovete l'istruzione di return dal metodo m1? Come vedete, la distinzione fra procedure e funzioni è piuttosto sfuocata...
  10. Ripetete gli esercizi precedenti, modificando nuovamente la dichiarazione del metodo m1 così:
      static int m1(double x) {
        System.out.println("Metodo m1. Parametro: " + x);
        return 23;
      }
    
    Come vedete vi è una promozione automatica del parametro attuale int al parametro formale double.
  11. Ripetete gli esercizi precedenti, modificando nuovamente la dichiarazione del metodo m1 così:
      static double m1(double x) {
        System.out.println("Metodo m1. Parametro: " + x);
        return 23;
      }
    
  12. Ripetete gli esercizi precedenti, modificando nuovamente la dichiarazione del metodo m1 così:
      static boolean m1(boolean x) {
        System.out.println("Metodo m1. Parametro: " + x);
        return 23;
      }
    

Riassumendo: la distinzione fra procedure e funzioni è un po' sfuocata (ma dove ci vuole una funzione non si può mettere una procedura!). Il controllo dei tipi è invece piuttosto rigido.

Esercizio 2

Obiettivo: Comprendere il passaggio parametri per valore.

Attività:

  1. Qual è l'output del programma seguente? (rispondete senza eseguire e poi verificate)
    class Azzera {
      public static void main(String[] args) {
        int x;
        x = 1;
        System.out.println(x);
        azzera(x);
        System.out.println(x);
      }
    
      static void azzera(int x) {
        x = 0;
      }
    }
    
    (Come abbiamo visto a lezione, bisognerebbe definire il metodo azzera() come funzione... se non ve lo ricordate, rivedetelo...)
  2. E cosa succede se il parametro attuale è un elemento di un array, come nel programma seguente?
    class Azzera {
      public static void main(String[] args) {
        int[] a = {0, 1, 2, 3, 4};
        for (int i = 0; i < a.length; i++)
          System.out.println(a[i]);
        for (int i = 0; i < a.length; i++)
          azzera(a[i]);
        for (int i = 0; i < a.length; i++)
          System.out.println(a[i]);
      }
    
      static void azzera(int x) {
        x = 0;
      }
    }
    
  3. E cosa succede se il parametro attuale è un array, come nel programma seguente?
    class Azzera {
      public static void main(String[] args) {
        int[] a = {0, 1, 2, 3, 4};
        for (int i = 0; i < a.length; i++)
          System.out.println(a[i]);
        azzera(a);
        for (int i = 0; i < a.length; i++)
          System.out.println(a[i]);
      }
    
      static void azzera(int x) {
        x = 0;
      }
    }
    

Esercizio 3

Obiettivo: Comprendere la visibilità delle variabili.

Attività: Riprendiamo l'esercizio precedente e vediamone alcune varianti.

  1. Qual è l'output del programma seguente, ottenuto dal precedente modificando x in una variabile di classe? (rispondete senza eseguire e poi verificate)
    class Azzera {
      static int x;
      public static void main(String[] args) {
        x = 1;
        System.out.println(x);
        azzera(x);
        System.out.println(x);
      }
    
      static void azzera(int x) {
        x = 0;
      }
    }
    
  2. E qual è l'output del programma seguente, ottenuto modificando il metodo azzera() rimuovendone l'argomento? (rispondete senza eseguire e poi verificate)
    class Azzera {
      static int x;
      public static void main(String[] args) {
        x = 1;
        System.out.println(x);
        azzera();
        System.out.println(x);
      }
    
      static void azzera() {
        x = 0;
      }
    }
    

Esercizio 4

Obiettivo: Saper usare le funzioni.

Attività:

  1. Considerate il seguente programma
    class Calcola {
      public static void main(String[] args) {
        int x;
        x = somma(2,3);
        System.out.println(x);
        x = sottrai(x,1);
        System.out.println(x);
      }
      static int somma(int x, int y) {
        return x+y;
      }
      static int sottrai(int x, int y) {
        return x-y;
      }
    }
    
    Senza utilizzare una seconda variabile e senza utilizzare gli operatori aritmetici, ma solo le funzioni somma e sottrai, fate in modo che x contenga il risultato di 45-(32+9) (ovviamente non dovete fare i conti voi!). (insomma, dovete scrivere un'unica istruzione di assegnamento, con le opportune invocazioni alle funzioni somma e sottrai)
  2. Fate in modo che x contenga il risultato di (12+6)-(22-9).
  3. Modificando così il metodo somma
      static void somma(int x, int y) {
        System.out.println((x+y));
      }
    
    è ancora possibile ottenere gli stessi risultati? Perché?

Esercizio 5

Obiettivo: Uso dei metodi.

Attività:

  1. Scrivete un sottoprogramma che restituisce la parte intera della radice quadrata del numero passato come parametro. Scrivete un programma che usa tale metodo per visualizzare le parti intere delle radici quadrate dei primi n numeri, con n letto in input durante l'esecuzione. Suggerimento: Completate il programma seguente, basandovi sull'esercizio 3 dell'esercitazione 6
    class Radice {
      public static void main(String[] args) {
        int n;
        n = EasyIn.readInt();
        for (int i = 0; i <= n; i++) { 
          System.out.println(radice(i));
        }
      }
    
      static int radice(int x) {
        // COMPLETARE QUI
      }
    }
    
  2. Modificate l'esercizio precedente per far visualizzare ogni radice una sola volta.
  3. Basandovi sull'esercizio precedente e sull'esercizio 2.1 dell'esercitazione scorsa, scrivete un programma che visualizza le parti intere delle radici quadrate dei numeri primi fra 1 e 100.
  4. Scrivete un programma che visualizza solo quelle parti intere delle radici quadrate dei numeri fra 1 e 100 che non sono numeri primi.
  5. Completate il programma seguente che contiene un sottoprogramma per verificare se l'array a (che è in una variabile di classe, o "globale") è palindromo. Cosa c'è di sbagliato in questo programma? (Nella prossima esercitazione lo riscriveremo in forma corretta.)
    class Palindromo {
      static int[] a;
      public static void main(String[] args) {
        int n;
        n = EasyIn.readInt();
        a = new int[n];
        for (int i = 0; i < a.length; i++) { 
          System.out.print("a[" + i + "] = ");
          a[i] = EasyIn.readInt();
        }
        if (palindromo())
          System.out.println("Array palindromo");
        else
          System.out.println("Array non palindromo");
      }
    
      static boolean palindromo() {
        return true;
      }
    }
    
  6. (*) Riprendete gli esercizi 1.2 - 1.7 dell'esercitazione scorsa e modificateli aggiungendo metodi dove opportuno.
  7. (*) Ordinate "per righe" una matrice bidimensionale. Gli elementi in ogni riga devono essere crescenti da sinistra a destra, e gli elementi di ogni riga devono essere minori degli elementi sulla riga successiva. Non usate altri array.
  8. Riprendete i programmi che avete scritto per l'esercizio 4 dell'esercitazione 6 e modificateli definendo metodi dove opportuno.

Valid XHTML 1.1! Stefano Mizzaro Last modified: Sat Jul 10 11:31:52 ora legale Europa occidentale 2004