Programmazione per TWM: Esercitazione di laboratorio #2 (15/01)

L'obiettivo di questa esercitazione è familiarizzare con i seguenti concetti, illustrati nel capitolo 2 del libro:

Scriverete anche i primi semplici programmi creati da voi. Gli esercizi etichettati con l'asterisco (*) sono più difficili: vi consiglio di affrontarli dopo aver risolto gli altri.

Esercizio 1

Obiettivo: Comprendere i meccanismi del ciclo editing-compilazione-esecuzione.

Attività:

  1. Per prima cosa create una cartella es2 in cui mettere tutti i programmi che scriverete in questa esercitazione.
  2. Prendete il programma CiaoATutti e salvatelo in un file di nome ciaoatutti.java (senza le lettere maiuscole). Provate a compilare: che comando dovete usare? Provate poi a eseguire: che comando dovete usare? Suggerimento: usate il comando ls per vedere che file è stato creato dal compilatore...
  3. Ripetete l'esercizio precedente usando un file di nome pippo (senza l'estensione .java).

Morale: usate sempre l'estensione .java e salvate ogni programma in un file con lo stesso "nome del programma" (quello che c'è dopo class) a cui aggiungere l'estensione .java. Inoltre, prestate particolare attenzione alle lettere maiuscole, che sono diverse da quelle minuscole.

Esercizio 2

Obiettivo: Familiarizzare con le regole per gli identificatori e con i messaggi di errore del compilatore.

Attività:

  1. Considerate il seguente programma:
    class Identificatori {
        public static void main(String[] args) {
          int x;
        }
    }
    
    (in realtà è un programma estremamente povero, che non fa assolutamente nulla, ma per ora ci basta). Provate a modificare il nome dell'identificatore di variabile x in maniera non consentita (rivedete le regole sintattiche per gli identificatori). Ad esempio, provate in sequenza, a sostituire x con class (che è una parola riservata), con 1x (che comincia con un carattere non consentito), o con x 1 ("x spazio uno", che contiene carattere spazio, non consentito). Ricompilate dopo ogni modifica e cercate di capire i messaggi di errore del compilatore.
  2. Ripetete l'esercizio precedente, però modificando il nome del programma (anche questo è un identificatore) invece del nome della variabile.

Esercizio 3

Obiettivo: Familiarizzare con i tipi di dato primitivi.

Attività:

  1. Rivedete l'esercizio 4.5 dell'esercitazione scorsa. Riprendete il programma AreaTriangolo visto a lezione e modificate le dichiarazioni delle variabili area, base e altezza per far sì che il programma funzioni correttamente anche se l'area non è un valore intero.
  2. Verificate la correttezza dei valori massimi e minimi che è possibile memorizzare nelle variabili di tipo byte, short, int e long (tabella 2.2 a pag. 33), scrivendo un breve programma in cui: Suggerimento: Come avrete capito, tutti i programmi hanno la stessa struttura:
    class "NomeProgramma" {
        public static void main(String[] args) {
            "..."
        }
    }
    
    dove, al posto di "NomeProgramma" ci va il nome del programma, e al posto dei puntini "..." le istruzioni che compongono il programma, in sequenza. In questo caso il programma sarà (le parti fra virgolette "" o doppi apici, come dicono gli informatici, sono quelle da completare):
    /* Programma per verificare "..." */
    class VerificaMassimiMinimi {
      public static void main (String[] args) {
        "dichiarazione delle 4 variabili"
    
        b = (byte)(127 + 1);
        s = (short)("...");
        "..."
        "..."
    
        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
        System.out.println(l);
      }
    }
    
    Fate attenzione che i letterali di tipo long vanno seguiti da una L. E non dimenticate i punti e virgola alla fine di ogni istruzione!

Esercizio 4

Obiettivo: Familiarizzare con i letterali e le sequenze di escape.

Attività:

  1. Scrivete un programma che memorizza il carattere ' (apice) in una varibile di tipo char e poi visualizza il contenuto della variabile. Suggerimento Avrete bisogno di un programma con 3 istruzioni: dovrete prima dichiarare la variabile di tipo char, poi assegnarle il valore voluto usando la sequenza di escape e poi stamparla (usando la System.out.println).
  2. Scrivete un programma che memorizza il carattere " (doppio apice) in una varibile di tipo String e poi visualizza il contenuto della variabile (usando la System.out.println).
  3. Scrivete un programma che memorizza tutti i caratteri corrispondenti alle sequenze di escape in una stringa e poi la visualizza.

Esercizio 5

Obiettivo: Familiarizzare con gli operatori fra valori interi.

Attività:

  1. La formula per il calcolo dell'area di un trapezio è: "somma delle basi per altezza diviso due". Si scriva un programma AreaTrapezio che assegna dei valori a vostra scelta alle variabili b1, b2 e h e calcola l'area del trapezio corrispondente. Scrivete un'unica espressione, usando le parentesi dove opportuno. Suggerimento: Ispiratevi al programma AreaTriangolo e adattatelo (bastano minime modifiche).

Esercizio 6

Obiettivo: Familiarizzare con l'operatore di modulo "%" (il resto della divisione intera).

Attività:

  1. Qual è il valore assunto dalla variabile x, di tipo int, dopo ognuno degli assegnamenti seguenti?
    x = 16 / 2;
    x = 15 / 2;
    x = 16 % 2;
    x = 15 % 2;
    
    Scrivete un breve programma per verificare che la vostra risposta è corretta. Suggerimento: È sufficiente scrivere un programma contenente la dichiarazione della variabile x e poi i 4 assegnamenti a x dei 4 valori, ognuno seguito dall'istruzione di visualizzazione del valore di x (System.out.println).
  2. Scrivete l'istruzione di assegnamento che assegna alla variabile intera y il valore 0 se la variabile intera x è pari, 1 se x è dispari. Scrivete un breve programma per verificare che la vostra soluzione è corretta. Suggerimento: ripensate al risultato delle ultime due operazioni dell'esercizio precedente e completate (ed eseguite più volte su più valori di x) il programma seguente:
    /* Programma per  "..." */
    class PariDispari {
      public static void main (String[] args) {
        int y;
        int x;
        x = "..."; // valore di prova
        y = "..."; //espressione da trovare...
        System.out.println(y);
      }
    }
    

Esercizio 7

Obiettivo: Familiarizzare con il tipo di dato boolean.

Attività:

  1. Il tipo boolean è il più semplice di tutti, dato che le variabili di quel tipo possono assumere solo i valori true e false. Eppure spesso risulta di difficile comprensione ai neofiti. Scrivete l'istruzione di assegnamento che assegna alla variabile booleana p il valore true se la variabile intera x è pari, false altrimenti. Scrivete un breve programma per verificare che la vostra soluzione è corretta. Suggerimento: ripensate all'esercizio precedente e usate l'operatore di uguaglianza "==" nella parte destra dell'assegnamento. Fate attenzione: la modifica è minima, ma richiede un salto logico...
  2. Scrivete l'istruzione di assegnamento che assegna alla variabile booleana p il valore true se la variabile intera x ha un valore positivo (maggiore o uguale a zero), false altrimenti. Scrivete un breve programma per verificare che la vostra soluzione è corretta. Suggerimento: Questo esercizio e quelli immediatamente successivi sono varianti minime del precedente.
  3. Scrivete l'istruzione di assegnamento che assegna alla variabile booleana p il valore true se la variabile intera x ha un valore positivo (maggiore o uguale a zero) o pari, false altrimenti. Scrivete un breve programma per verificare che la vostra soluzione è corretta. Suggerimento: usate gli operatori logici.
  4. Scrivete l'istruzione di assegnamento che assegna alla variabile booleana p il valore true se la variabile intera x ha un valore positivo (maggiore o uguale a zero) e pari, false altrimenti. Scrivete un breve programma per verificare che la vostra soluzione è corretta.
  5. Scrivete l'istruzione di assegnamento che assegna alla variabile booleana p il valore true se la variabile intera x ha un valore positivo compreso in uno dei seguenti intervalli: [15,18], [39,45], [91,103], false altrimenti. Scrivete un breve programma per verificare che la vostra soluzione è corretta.

Esercizio 8

Obiettivo: Familiarizzare con le convenzioni per gli identificatori e con i commenti.

Attività:

  1. Riprendete tutti i programmi scritti finora e controllate di avere rispettato le convenzioni per gli identificatori. Modificate i programmi dove non l'avete fatto.
  2. Riprendete tutti i programmi scritti finora, aggiungendo commenti dove opportuno. Usate il tipo di commento più adatto per ogni situazione (/* ... */ o // ...).

Esercizio 9

Obiettivo: Affrontare alcuni problemi e trucchi ricorrenti nella programmazione.

Attività:

  1. Sapete che la variabile x può assumere i valori 0 o 1 (e nessun altro). Scrivete, usando l'operatore condizionale "?:", un'istruzione di assegnamento che assegna alla variabile y il valore 1 se x è 0 e il valore 0 se x è 1. Scrivete un breve programma per verificare che la vostra soluzione è corretta. Nota Anche se questa soluzione funziona, non è sicuramente la migliore possibile: si vedano i punti seguenti.
  2. Sapete che la variabile x può assumere i valori 0 o 1 (e nessun altro). Usate, invece dell'operatore condizionale "?:", l'operatore di modulo "%" e scrivete un'istruzione di assegnamento che assegna alla variabile y il valore 1 se x è 0 e il valore 0 se x è 1. Scrivete un breve programma per verificare che la vostra soluzione è corretta. Suggerimento Se a y assegnate x + 1, il risultato è corretto se x vale 0, ma se x vale 1 in y vi ritrovate il valore 2. Però se prendete il "modulo 2"... Nota Anche questa soluzione, pur funzionante, non è la migliore possibile: si veda il punto seguente.
  3. In realtà, nei due esercizi precedenti gli operatori condizionale "?:" e modulo "%" non sono necessari. Anzi, sono di gran lunga superflui e si può fare di meglio senza. Come? Scrivete un breve programma per verificare che la vostra soluzione è corretta. Suggerimento: chiedetevi quanto vale x + y, e risolvete l'equazione...
    Nota: Questa soluzione è di gran lunga migliore delle precedenti. Vi può sembrare un dettaglio, ma è importante e può costarvi punti all'esame...
  4. Scrivete un'espressione per il calcolo del valore assoluto di un numero x. Scrivere anche un breve programma contenente tale espressione per controllarne la correttezza. Suggerimento: usate l'operatore condizionale "?:".
  5. State realizzando un gioco da tavolo in Java e dovete gestire il turno fra i vari giocatori. Per fare ciò usate una variabile turno, di tipo int. Supponiamo per semplicità che ci siano 2 giocatori; quindi turno dovrà assumere in sequenza i valori 0, 1, 0, 1, 0, 1,... Scrivete l'istruzione di assegnamento che dal valore corrente di turno calcola il valore successivo. Suggerimento Non vi sarete già dimenticati le prime tre attività di questo esercizio, no? ;-)
  6. Supponiamo ora che, nel nostro gioco da tavolo, ci siano 4 giocatori; quindi turno dovrà assumere in sequenza i valori 0, 1, 2, 3, 0, 1, 2, 3, 0, ... e così via. Completate il programma seguente, scrivendo l'istruzione di assegnamento che assegna di volta in volta a turno il valore corretto sulla base del valore precedente. Suggerimento: Quali possono essere i valori assunti dal resto della divisione di un numero per 4?
    class Turno {
        public static void main(String[] args) {
    	int turno;
    	int i;
    	turno = 0;
    	i = 0;
    	while (i <= 20) {
    	    System.out.println("Giro numero " + i + " Ora e' di turno giocatore" + turno);
    	    turno = //COMPLETARE!!
    	    i = i + 1;
    	}
        }
    }
    
  7. E se i giocatori sono 7 e quindi si vuole che turno assuma ciclicamente i valori 0, 1, 2, 3, 4, 5 e 6?
  8. Scrivete l'istruzione di assegnamento che assegna alla variabile intera x il valore di y/2 se y è pari, e il valore di y/3 se y è dispari. Usate l'operatore condizionale "?:". Scrivete un programma per verificare la correttezza.
  9. Scrivete l'istruzione di assegnamento che assegna alla variabile intera x il valore di y/2 se y è pari, e il valore di (y-1)/2 se y è dispari. Scrivete un programma per verificare la correttezza. Suggerimento: NON usate l'operatore condizionale.
  10. Scrivete un semplice programmino che verifica che effettuare un'operazione di scorrimento verso sinistra ("<<") equivale ad effettuare una moltiplicazione per 2.

Esercizio 10

Obiettivo: Approfondimenti su alcuni problemi e trucchi ricorrenti nella programmazione.

Attività:

Riprendiamo l'esercizio sulla variabile turno e vediamo un po' di variazioni sul tema... Mi raccomando: provate TUTTE quelle che vi sembrano soluzioni scrivendo, compilando ed eseguendo un programma. Spesso quello che sulla carta sembra corretto, poi non lo è...

  1. (*) E se si vuole che turno assuma ciclicamente i valori 1, 2, 3 e 4? Suggerimenti: Ripartite dalla versione originale e provate a invertire l'ordine delle operazioni nell'istruzione di assegnamento. Una volta che ottenete 0, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2,... ci siete quasi, basta cambiare il valore iniziale di turno...
  2. E se si vuole che turno assuma ciclicamente i valori 0, 2, 4 e 6? Suggerimento: Beh, è molto simile al problema iniziale: invece di incrementare turno turno di 1 lo incrementerete di ...
  3. E se si vuole che turno assuma ciclicamente i valori 1, 3, 5 e 7? Suggerimento: partendo dalla versione precedente, e con una modifica simile a quella fatta in precedenza...
  4. (*) E se si vuole che turno assuma ciclicamente i valori 2, 4, 8 e 16?
  5. (*) E se si vuole che turno assuma ciclicamente i valori 2, 4, 16 e 256? Suggerimento: è più semplice se invertite l'ordine delle istruzioni di assegnamento a turno e di visualizzazione (System.out.println).

Esercizio 11

Obiettivo: Comprendere i concetti delle promozioni (conversioni automatiche di tipo) e del cast.

Attività: In alcuni casi il Java effettua una conversione automatica di tipo (detta promozione): ad esempio, nell'ultima riga del seguente frammento di codice

    byte b;
    int i;
    b = 10;
    i = b;

il valore di b (che è di tipo byte) viene promosso al tipo int per poter essere assegnato a i. Infatti se scrivete un semplice programma contenente il frammento precedente, esso viene compilato ed eseguito senza errori. Fatelo...

Le promozioni vengono effettuate automaticamente per le conversioni byte -> short -> int -> long -> float -> double, come si può dedurre dal programma seguente (comprendetelo, compilatelo ed eseguitelo):

class Prova {
    public static void main (String[] args) {
      byte b;
      short s;
      int i;
      long l;
      float f;
      double d;
      b = 10;
      s = b;
      i = s;
      l = i;
      f = l;
      d = f;
    }
}

Conversioni automatiche di questo tipo sono comode, perché sicuramente non portano a errori (se un valore ci sta in un byte, ci sta senz'altro anche in uno short e così via).

La conversione nel verso opposto può però essere pericolosa. Se proviamo a compilare un programma contenente questo frammento di codice

    byte b;
    int i;
    i = 128;
    b = i;

il compilatore segnala un errore di "tipo incompatibile" sull'assegnamento b = i (verificatelo! E, da qui in poi, prestate particolare attenzione ai messaggi di errore in compilazione e in esecuzione). E ciò è sensato, in quanto 128 non ci sta in un byte (che, ricordo, usa la rappresentazione in complemento a due). Questo comportamento può però sembrare scomodo nel caso in cui il valore di i sia sufficientemente piccolo da stare in un byte, come in

    byte b;
    int i;
    i = 10;
    b = i;

Anche qui il nostro povero compilatore segnala lo stesso errore (verificatelo).  Mettiamoci nei suoi panni. Io, povero compilatore, mi baso sui tipi di i e b: se essi sono tali che i può contenere valori che non stanno in b, io devo segnalare errore. E questo è ragionevole: se i e b fossero dati di input del programma, come potrei sapere quale valore assumeranno b e i?

Per risolvere questi problemi in Java è stato introdotto il meccanismo del cast (che in inglese significa "fondere in uno stampo") che permette di convertire un tipo in un altro: è sufficiente indicare fra parentesi tonde il tipo che si vuole ottenere (vi sono operatori di cast per tutti i tipi: (byte), (short), (int), ecc.). In questo modo, il programmatore deve segnalare esplicitamente nel codice che "sa quello che fa" e che se ne assume tutte le responsabilità. Il frammento di codice precedente andrebbe riscritto come:

    byte b;
    int i;
    i = 10;
    b = (byte)i;

dove (byte) è l'operatore di cast (verificate compilando ed eseguendo). In questo modo il valore di i (che è un int) viene convertito in un byte; se il valore ci sta in un byte, nessun problema; se il valore è troppo grande invece qualche problema c'è. Infatti, un programmatore distratto potrebbe anche scrivere

    byte b;
    int i;
    i = 128;
    b = (byte)i;

ritrovandosi in b lo "strano" valore -128 (verificatelo, e cercate di capire perché!), con somma soddisfazione del compilatore...

Esercizio 12

Obiettivo: Comprendere le problematiche dei valori temporanei delle espressioni.

Attività: Riflettete sul programma seguente:

class Prova {
    public static void main (String[] args) {
      int m;
      int n;
      int i;

      m = 2000000000;
      n = 2000000000;
      i = m * n / m;
      System.out.println(i);
    }
}

Quale risultato vi aspettate? Rispondete prima di provare ad eseguirlo, poi provate ad eseguirlo e cercate di capire perché non è come pensate...

La spiegazione è che  i valori temporanei nelle espressioni sono memorizzati in variabili implicite di tipo int, a meno che nell'espressione non compaiano variabili di tipo più ampio (long, float, double), nel qual caso vengono usate variabili implicite di quel tipo. Quindi, il programma precedente viene compilato senza errori, l'esecuzione avviene anch'essa senza errori, ma il valore stampato è 0 (zero). Questo accade perché il risultato di m * n viene memorizzato in una variabile implicita di tipo int, che non è in grado di contenerlo (e non viene segnalato l'overflow).

Il programma andrebbe riscritto dichiarando le tre variabili di tipo long (in questo modo la variabile temporanea dell'espressione i = m * n / m è un long), oppure dichiarando solo i di tipo long e utilizzando il cast i = (long)m * n / m.

Come consigli generali, ricordate che uno spreco di qualche byte di solito non è importante e quindi conviene usare variabili dei tipi più ampi (long e double); inoltre bisogna cercare di evitare i cast che portano spesso a errori. Riparleremo in modo più generale del cast fra qualche lezione...

... RICORDATEVI DI USCIRE (exit + LOGOUT) E DI NON SPEGNERE IL CALCOLATORE!!!!!


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