Sei sulla pagina 1di 13

ARRAY,

METODI STATICI E VARIABILI STATICHE IN JAVA Domenico Sacc


1. Memorizzazione di Array
Come abbiamo visto nella dispensa sulle stringhe, in Java possibile utilizzare, oltre ai tipi base (quali ad esempio, int, double, char, boolean), anche tipi derivati definiti tramite apposite classi. Nel caso di String, tale classe gi disponibile tra quelle messe a disposizione da una qualsiasi piattaforma Java. Molte altre sono disponibili e agevolmente utilizzabili. Inoltre un programmatore pu crearne delle proprie. La definizione di classi in Java non rientra tra gli obiettivi del corso. A una variabile di tipo base viene assegnata una locazione di memoria in cui viene memorizzato il valore assunto dalla variabile durante lesecuzione di un programma. Una locazione di memoria viene assegnata anche ad una variabile di tipo derivato (che chiamata oggetto) ma, in essa, non viene memorizzato il valore (che nel caso di una stringa o di un array non prefissato ma variabile durante lutilizzo) ma un puntatore (cio lindirizzo, che occupa tipicamente 4 byte) ad unaltra area di memoria dove effettivamente memorizzato il valore; inoltre viene memorizzato il numero di byte occupati dalla memorizzazione del valore. Per gestire efficacemente le aree di memorie da destinare ai valori degli oggetti, la memoria dati di un programma viene suddivisa in due parti: lo stack (pila), dove vengono memorizzate le variabili di tipo base e i puntatori degli oggetti, e lo heap (mucchio) dove sono memorizzati i valori degli oggetti. Ad esempio, se vengono dichiarate le variabili char A =a; String S = ciao, esse vengono memorizzate nel seguente modo. Nello stack la variabile A occupa una locazione di 2 byte per rappresentare direttamente il suo valore (si ricorda che un carattere occupa 2 byte) e la stringa S occupa due locazioni, ciascuna di 4 byte, la prima contenente lindirizzo della locazione nello heap in cui memorizzato il valore della stringa, e la seconda il numero di byte occupati dalla stringa pari a 8 byte: (4 caratteri) (2 byte), cio la stringa memorizzata nello heap dallindirizzo 100 fino allindirizzo 107.

In effetti la memorizzazione di una stringa un po pi complicata poich la classe String speciale e ha delle particolarit che la distinguono dalle altre classi e che sono state introdotte per la rilevanza che oggi assume la gestione di stringhe in molte applicazioni (si pensi a GOOGLE). Una variabile pu essere definita di tipo derivato anche attraverso lutilizzo degli array, che rappresentano vettori di elementi tutti dello stesso tipo (che pu essere un tipo base o una classe o anche un ulteriore array come accade per una matrice che un array di array di interi o double). Se adesso definiamo un array di 3 interi come int [ ] V = {1, 2, 3}; la variabile V occupa nello stack due locazioni di 4 byte ciascuna, la prima per memorizzare lindirizzo del primo elemento dellarray nello heap e la seconda il numero di byte necessari per memorizzare tutti gli elementi pari a 12: (3 interi) (4 byte). vale variabili sono ora complessivamente memorizzate

nel seguente modo (si ricorda che un intero occupa 4 byte), cio il vettore memorizzato nello heap dallindirizzo 108 fino allindirizzo 119.

2. Tipi Array
Come gi anticipato, in Java possibile utilizzare tipi derivati tra cui gli array, che rappresentano vettori di elementi tutti dello stesso tipo. Ad esempio: int [ ] X; char [ ] Y; double [ ] Z; String [ ] W; definiscono X come array di interi, Y come array di caratteri, Z come array di double e W come array di stringhe. Per essere utilizzabile, un array deve essere dimensionato attraverso un apposito comando new. Ad esempio: X = new int [20]; stabilisce che X ha 20 elementi individuati con appositi indici che vanno da 0 a 19. Quindi X[0] il primo elemento e X[19] lultimo elemento. Si noti che, per ogni i=0..19, X[i] di tipo intero cio un elemento di un array ha come tipo quello base dellarray. E possibile anche stabilire la dimensione dellarray nel momento della sua definizione: int X = new int [20]; E anche possibile anche inizializzare larray nel momento della sua definizione: int Y = {1,2,3}; Larray Y viene definito con dimensione 3 e vengono assegnati ai suoi 3 elementi rispettivamente i valori: 1, 2 e 3. Successivamente alla sua definizione, possibile ridimensionare un array. Ad esempio int X = new int [20]; // definizione dellarray X e sua inizializzazione // attenzione che X inizializzato ma i suoi 20 elementi non ancora System.out.println(X[5]); // viene stampato un valore indeterminato valore non inizializzato System.out.println(X[20]); // viene segnalato errore: lelemento 20.mo non esiste for ( int i =0; i < 20; i++ ) X[i]=i; // ora vengono inizializzati gli elementi di X System.out.println(X[5]); // viene stampato il valore 5 System.out.println(X[40]); // errore: lelemento 40.mo non esiste for ( int i =0; i < 20; i++ ) System.out.println(X[i]); // vengono stampati i venti elementi di X X = new int [40]; // viene ridefinito larray X che ora ha 40 elementi // attenzione che i 20 elementi precedenti ora sono cancellati for ( int i =0; i < 40; i++ ) X[i]=i; // ora vengono inizializzati i nuovi 40 elementi di X for ( int i =0; i < 40; i++ ) System.out.println(X[i]); // vengono stampati i nuovi quaranta elementi di X Larray X viene prima dimensionato con 20 elementi e poi ridimensionato con 40 elementi. Subito dopo il ridimensionamento, tutti i dati precedenti non sono pi utilizzabili ( come se venissero cancellati).

E possibile anche definire un array di array, ad esempio: int [ ] X; int [] [] U; int k; U = new int [5][10]; X = U[3]; k = U[3][9]; La variabile U rappresenta una matrice di 5 righe e 10 colonne. Con il comando X = U[3] copiamo la 4^ riga (si ricorda che lindice parte da 0) di U in X. Con il comando k = U[3][9] copiamo lelemento della 4^ riga e della 10^ colonna in k. Un primo esempio di utilizzo degli array, l'abbiamo gi visto nella codifica di un intero relativo in 16 bit con complemento a due allorquando abbiamo utilizzato un array per memorizzare le cifre binarie mana mano che venivano prodotte e poi stamparle alla fine nell'ordine giusto. Come secondo esempio di utilizzo degli array, scriviamo un programma che legge una stringa e per ogni carattere di essa, determina il numero di occorrenze (frequenza) nella stringa. Ad esempio, se la stringa cara carla, il carattere c ha frequenza 2, a 4, r 2, l 1.
public static void main(String [] args){ Scanner reader = new Scanner(System.in); System.out.println("Introduci il testo"); String testo = reader.next(); int n = testo.length(); // viene definito un array elencoCar di massimo n caratteri // in cui memorizzare tutti i possibili caratteri diversi di testo // nel caso peggiore ci saranno n caratteri diversi // ma in generale ce ne saranno di meno essendoci caratteri duplicati char [] elencoCar = new char [n]; // viene definito un array freqCar di massimo n interi // se elencoCar[5] vale c e freqCar[5] vale 3 // significa che il carattere c ha frequenza 3 int [] freqCar = new int [n]; // m indica il numero di caratteri differenti nel testo int m = 0; // inizialmente 0 caratteri diversi in testo for ( int i = 0; i < n; i++ ) { char carCorr = testo.charAt(i); // carattere i-mo in testo // verifica se carCorr gi in elencoCar o va inserito boolean trovatoCar = false; for ( int j = 0; j < m && !trovatoCar; j++ ) if ( carCorr == elencoCar[j] ) { // carCorr in elencoCar, // quindi va incrementata la frequenza trovatoCar = true; freqCar[j]++; } if ( !trovatoCar ) { // carCorr non in elencoCar // carCorr va inserito in elencoCar con frequenza 1 elencoCar [m] = carCorr; freqCar[m]=1; m++; // va incrementato il numero di caratteri diversi } }; System.out.println("Frequenze dei caratteri"); for ( int i = 0; i < m; i++ ) { System.out.print("Carattere <" + elencoCar[i]+">: "); System.out.println(freqCar[i]); };

}
3

Si noti che sono utilizzati due array: larray di caratteri elencoCar e larray di interi freqCar. Entrambi sono dimensionati con un numero di elementi pari alla lunghezza della stringa testo. Tuttavia, in generale non tutti questi elementi sono utilizzati in quanto i due array contengono tanti elementi quanti sono i caratteri differenti di testo. Gli elementi dei due array sono costruiti iterativamente, attraverso la scansione di tutti i caratteri di testo: per ogni carattere, si va a verificare se esso gi compreso in elencoCar ; se presente, si incrementa di 1 la relativa frequenza; altrimenti si aggiunge il carattere a elencoCar e si pone la relativa frequenza a 1.

3. Definizione di Metodi Statici

Nello scrivere un programma opportuno seguire alcune regole di buona scrittura. Una di queste consiste nel suddividere un programma in unit specializzate a svolgere particolari funzioni. Tali unit di programmazione sono chiamati metodi e sono definiti in maniera analoga al metodo main. Essi vengono richiamati dal main o da altri metodi. Alla chiamata del metodo, vanno passati tutti i parametri indicati nella definizione del metodo. Quando ha completato la sua esecuzione, il metodo o non restituisce nulla o restituisce un valore di un tipo predefinito. Come primo esempio si consideri la seguente traccia dellesercizio 2 dellappello del 22/07/2011. Traccia: Si scriva un metodo Java comprimi che riceve in ingresso un vettore di interi V e restituisce un vettore di interi C che ne rappresenta la versione compressa. Il processo di compressione fa corrispondere ad una sequenza ininterrotta di K interi N nel vettore V, la sequenza (K,N) nel vettore C. Ad esempio, se V=[1,1,1,1,3,3,4,3,7,7,7,7,7] allora C=[4,1, 2,3, 1,4, 1,3, 5,7]. Soluzione: Scriviamo la soluzione come un metodo statico Java di nome comprimi che riceve in ingresso un array di interi V (un solo parametro formale) e restituisce un array di interi:
static int [ ] comprimi ( int [ ] V ) { int [ ] K = new int[V.length*2]; // caso peggiore di elementi tutti // distinti int iK = 0; // indice del vettore K int iV = 0; // indice del vettore V while ( iV < V.length ) { // scandiamo tutti gli elementi di V int iV1; boolean elemDiff=false; // indica se trovato un elemento differente for ( int j = iV+1; j < V.length && !elemDiff; j++ ) if ( V[j] != V[iV] ) { iV1 = j; elemDiff = true; } if ( !elemDiff ) iV1 = V.length; K[iK] = iV1 - iV; // viene memorizzato il numero di ripetizioni K[iK+1] = V[iV]; // viene memorizzato lelemento ripetuto iK += 2; // lindice di K viene incrementato di 2 iV = iV1; // la scansione di V riparte dallindice iV1 } int [ ] C = new int[iK] // dimensione effettiva di C for ( int i = 0; i < iK; i++ ) C[i] = K[i]; // viene ricopiato K in C return C; }

La soluzione per lesame tutta qui. Ma se si vuole provare il metodo necessario inserirlo in una classe insieme a un metodo main (ed eventuali ulteriori altri metodi):

Class Compressione { static public void main (String [] args) { int [] V= leggiVettore (); int [] C = comprimi(V); stampaVettore(C); } static int [ ] comprimi ( int [ ] V ) { // vedi codice precedente } static int [] leggiVettore () { Scanner reader = new Scanner(System.in); System.out.println("Numero di elementi del vettore V?"); int n = reader.nextInt(); int [] V = new int[n]; for ( int i = 0; i < n; i++ ) { System.out.println("V["+i+"]?"); V[i] = reader.nextInt(); } return V; } static void stampaVettore ( int [] C) { System.out.println("Compressione di V"); System.out.print("[ "); // senza salto linea for ( int i = 0; i < C.length; i+=2 ) { System.out.print( C[i]+":"+C[i+1]); // senza salto linea if ( i < C.length-2 ) System.out.print( ", "); // senza salto linea } System.out.println(" ]"); // salto linea System.out.println("bye"); } }

Lutilit di utilizzare metodi piuttosto che inserire direttamente il codice nel metodo main essenzialmente quella di evitare di distrarsi con limplementazione di dettagli secondari (tali sono da considerare due semplici metodi di lettura e scrittura di un array), rimandandola a un secondo momento o, ancora meglio, cercando di utilizzare metodi gi scritti (eventualmente opportunamente modificati).


Come un secondo esempio si consideri il seguente metodo per il calcolo del fattoriale di un numero n, calcolato come 12(n-1) n:
static int fattoriale ( int n ) { if ( n < 2 ) return 1; else { int ris = 2; for ( int i =3; i <= n; i++ ) ris = ris*i; return ris; } }

Esso restituisce un intero e ha un parametro formale (la variabile n) di tipo intero. Per richiamare il metodo bisogna scrivere il nome del metodo (nellesempio fattoriale e passare un parametro attuale di tipo intero, cio un valore effettivo per il quale calcolare il fattoriale. Il metodo restituisce un intero. Ad esempio il metodo pu essere richiamato con listruzione:
int R = fattoriale(4);

Dopo la chiamata, la variabile di R conterr 24, cio il fattoriale di 4. Il metodo restituisce 1 se gli viene passato un numero negativo. Volendo eliminare questo inconveniente, potremmo interpretare che fattoriale(-n) con n negativo, corrisponda a fattoriale(n) e riscrivere il metodo come:
static int fattoriale ( int n ) { int segno = 1; if ( n < 0) { n = -n; segno =-1; } if ( n < 2 ) return 1*segno; else { int ris = 2; for ( int i =3; i <= n; i++ ) ris = ris*i; return ris * segno; } }

Ora presentiamo il metodo fibonacci si ricorda che fibonacci(0) vale 0, fibonacci(1) vale 1 e per ogni i > 1, fibonacci(i) = fibonacci(i-1)+fibonacci(i-2). La sequenza dei primi numeri di Fibonacci : 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765.
static int fibonacci ( int n ) { if (n < 2 ) return n; else { int F1 = 1, F2=0, F=1; for (int i= 3; i <= n; i++ ){ F2=F1; F1=F; F=F1+F2; } return F; } } // fine metodo fibonacci

Di seguito presentiamo una classe con 3 metodi: il metodo main, lanciato automaticamente con lavvio della classe, che a sua volta richiama i metodi fattoriale e fibonacci.
public class CalcolaFF { public static void main(String[] args) { Scanner reader = new Scanner(System.in); // prima lettura del comando di interazione System.out.print("Inserisci A per calcolare fattoriale, System.out.println("B per fibonacci e C per finire [A/B/C]?"); char comando = Character.toUpperCase(reader.nextChar()); // sessione interattiva while ( comando == 'A' || comando == 'B' ) { System.out.println("Inserisci n "); int n = reader.nextInt(); int ris; if (comando =='A') { System.out.print("Fattoriale = "); ris = fattoriale(n); System.out.println(ris); // si pu anche scrivere direttamente solo una istruzione // System.out.println(fattoriale(n));

} else { System.out.print("Fibonacci = "); ris = fibonacci(n); System.out.println(ris); // si pu anche scrivere direttamente solo una istruzione // System.out.println(fibonacci(n)); } // successiva lettura del comando di interazione System.out.print("Inserisci A per calcolare fattoriale, System.out.println("B per fibonacci e C per finire [A/B/C]?"); comando = Character.toUpperCase(reader.nextChar()); } System.out.println("bye"); } // fine metodo main // METODO FATTORIALE static int fattoriale ( int n ) { // vedi codice precedente } // fine metodo fattoriale // METODO FIBONACCI static int fibonacci ( int n ) { // vedi codice precedente } } // fine metodo fibonacci } // fine classe CalcolaFF

Come secondo esempio di utilizzo di metodi, consideriamo nuovamente il programma che calcola le frequenze di caratteri in un testo. Vogliamo aggiungere una sessione interattiva che permetta allutente di chiedere se vuole conoscere la frequenza di un dato carattere o quali sono i caratteri con frequenza maggiore o uguale ad un dato valore. Piuttosto che estendere il programma e renderlo meno leggibile (un metodo non dovrebbe mai essere troppo lungo), possiamo definire due appositi metodi:
// METODO trovaFrequenzaCar static void trovaFrequenzaCar ( Scanner reader, char[] C, int [] F, int m) { // dato un carattere, viene verificato se presente nell'elenco C // se presente ne viene stampata la frequenza System.out.println("Carattere?"); char car= reader.nextChar(); boolean trovato = false; for (int i =0; i < m && !trovato; i++ ) { if ( C[i] == car ) { trovato = true; System.out.println("Carattere <" + C[i]+">: "+F[i]); } }; if (!trovato ) System.out.println("Carattere <" + car+"> non presente."); } // fine metodo trovaFrequenzaCar // METODO trovaCarFrequenti static void trovaCarFrequenti (Scanner reader, char[] C, int [] F, int m) { // data una frequenza minima fMin, vengono individuati tutti i caratteri // nell'elenco che abbiano una frequenza di almeno fMin System.out.println("Frequenza minima?"); int fMin = reader.nextInt(); for (int i =0; i < m; i++ )

if ( F[i] >= fMin ) System.out.println("Carattere <" + C[i]+">: "+F[i]); } fine metodo trovaCarFrequenti

Si noti che entrambi i metodi fanno utilizzano loggetto reader definito nel main e passato come parametro. Pertanto abbiamo due possibilit: o passarlo come parametro (ma non labbiamo inserito tra i parametri formali) o definire reader come variabile statica. Vediamo come realizzare questultima soluzione:
public class ContaCaratteri { Scanner reader = new Scanner(System.in); // variabile statica inizializzata public static void main(String [] args){ Scanner reader = new Scanner(System.in); System.out.println("Introduci il testo"); String testo = reader.next(); // . . . // vedi codice precedente // . . . System.out.println("Frequenze dei caratteri"); for ( int i = 0; i < m; i++ ) { System.out.print("Carattere <" + elencoCar[i]+">: "); System.out.println(freqCar[i]); }; // parte aggiuntiva char comando; do { // sessione interattiva con l'utilizzatore System.out.print("Frequenza di un Carattere o); System.out.print(" Caratteri pi Frequenti o Fine? (A/B/C)"); comando = Character.toUpperCase(reader.nextChar()); if ( comando == 'A' ) trovaFrequenzaCar(reader,elencoCar,freqCar,m); else if ( comando == 'B' ) trovaCarFrequenti (reader,elencoCar,freqCar,m); } while ( comando != 'C' ); System.out.println("Bye"); } // fine main // inserire qui le definizioni di trovaFrequenzaCar e trovaCarFrequenti

} // fine classe ContaCaratteri Si noti che lestensione del metodo main limitata a poche linee di codice e le due nuove funzioni aggiunte sono rese astratte attraverso il ricorso a due appositi metodi. Lutilizzo di astrazione ci permette di scrivere un programma senza dover fornire tutti i dettagli implementativi. Ovviamente, alla fine essi dovranno essere forniti cio i metodi dovranno essere scritti. Ma rinviare la scrittura risulta estremamente utile per un approccio sistematico in cui si parte dai concetti generali per poi passare a concetti pi specifici e dettagliati: si parla di stepwise refinement (raffinamento dei concetti a passi successivi). Addirittura il main nellesempio potrebbe essere ridotto ulteriormente spostando una parte del codice in un apposito metodo CalcolaFrequenzaCar:
public static void main(String [] args){ Scanner reader = new Scanner(System.in); System.out.println("Introduci il testo"); String testo = reader.next(); int n = testo.length(); char [] elencoCar = new char [n]; int [] freqCar = new int [n]; int m = calcolaFrequenzaCar(testo, elencoCar, freqCar); System.out.println("Frequenze dei caratteri");

Il metodo CalcolaFrequenzaCar definito nel seguente modo:

for ( int i = 0; i < m; i++ ) { System.out.print("Carattere <" + elencoCar[i]+">: "); System.out.println(freqCar[i]); }; char comando; do { // sessione interattiva con l'utilizzatore System.out.print("Frequenza di un Carattere o); System.out.print(" Caratteri pi Frequenti o Fine? (A/B/C)"); comando = Character.toUpperCase(reader.nextChar()); if ( comando == 'A' ) trovaFrequenzaCar(reader,elencoCar,freqCar,m); else if ( comando == 'B' ) trovaCarFrequenti (reader,elencoCar,freqCar,m); } while ( comando != 'C' ); System.out.println("Bye"); } // fine main

static int calcolaFrequenzaCar ( String testo, char [] elencoCar, int [] freqCar ) { int n = testo.length(); int m = 0; for ( int i = 0; i < n; i++ ) { char carCorr = testo.charAt(i); boolean trovatoCar = false; for ( int j = 0; j < m && !trovatoCar; j++ ) if ( carCorr == elencoCar[j] ) { trovatoCar = true; freqCar[j]++; } if ( !trovatoCar ) { elencoCar [m] = carCorr; freqCar[m]=1; m++; } }; return m, }

Come ulteriore esempio torniamo alla traccia del 22/07/2011. La soluzione pu essere scritta ne seguente moto utilizzando un metodo primoDiverso che riceve come parametri formali un array di interi W e un intero k e restituisce un intero che rappresenta lindice del primo elemento di W successivo a W[k] che diverso da W[k].
static int [ ] comprimi ( int [ ] V ) { int [ ] K = new int[V.length*2] int iK = 0, iV = 0; while ( iV < V.length ){ int iV1 = primoDiverso(V,iV); K[iK] = iV1 iV; K[iK+1] = V[iV]; iK += 2; iV = iV1; } int [ ] C = new int[iK] // dimensione effettiva di C for ( int i = 0; i < iK; i++ ) C[i] = K[i]; return C; }

static int primoDiverso( int [ ] W, int k ) { for ( int i = k+1; i < W.length; i++ ) if ( W[k] != W[i] ) return i; return W.length; }

Il vantaggio delluso del metodo solo nella chiarezza della lettura del programma e nella maggiore facilit di scrittura di esso, derivante dal meccanismo potente dellastrazione: possiamo scrivere il programma assumendo che esista un metodo che svolga una certa funzione che per il momento non vogliamo implementare. Limplementazione viene rimandata a un secondo momento o addirittura evitata se si riesce ad affidare il compito ad altri o a trovarla gi disponibile. Per concludere, sottolineiamo che i metodi introdotti sono di tipo statico nel senso che sono metodi della classe e non di oggetti della classe. E possibile definire anche metodi non statici, ma essi non saranno trattati nel corso, anche se ne saranno utilizzati alcuni in effetti i metodi di lettura associati a reader sono metodi non statici.

3. Definizione di Variabili Statiche

Quando un metodo richiama un altro metodo, deve condividere con esso alcune variabili sulle quali il metodo chiamato deve effettuare i suoi calcoli. Il modo pi utilizzato quello di passare tali variabili come parametri. Ma anche possibile definire allinterno della classe alcune variabili statiche che sono visibili a tutti i metodi della classe. Ad esempio la variabile n su cui va effettuato il calcolo del fattoriale o del Fibonacci, invece di passare tale valore come parametro attuale, n pu essere definita come variabile statica nel seguente modo:
public class CalcolaFF { static int n; // variabile statica public static void main(String[] args) { // prima lettura del comando di interazione System.out.print("Inserisci A per calcolare fattoriale, System.out.println("B per fibonacci e C per finire [A/B/C]?"); char comando = Character.toUpperCase(reader.nextChar()); // sessione interattiva while ( comando == 'A' || comando == 'B' ) { System.out.println("Inserisci n "); n = reader.nextInt(); // la variabile n gi definita if (comando =='A') { System.out.print("Fattoriale = "); // a fattoriale non viene passato alcun parametro attuale System.out.println(fattoriale()); } else { System.out.print("Fibonacci = "); // a fibonacci non viene passato alcun parametro attuale System.out.println(fibonacci()); } // successiva lettura del comando di interazione System.out.print("Inserisci A per calcolare fattoriale, System.out.println("B per fibonacci e C per finire [A/B/C]?"); comando = Character.toUpperCase(reader.nextChar()); } System.out.println("bye"); } // fine metodo main

10

// METODO FATTORIALE static int fattoriale ( ) { // nessun parametro formale if (n < 2 ) return 1; else { int ris = 2; for (int i= 3; i <= n; i++ ) ris = ris*i; return ris; } } // fine metodo fattoriale // METODO FIBONACCI static int fibonacci ( ) { // nessun parametro formale if (n < 2 ) return n; else { int F1 = 1, F2=0, F=1; for (int i= 3; i <= n; i++ ){ F2=F1; F1=F; F=F1+F2; } return F; } } // fine metodo fibonacci } // fine classe CalcolaFF

Ritornando al programma sulla frequenza dei caratteri in una stringa, possiamo evitare di passare i vari parametri alle funzioni richiamate definendole come statica nel seguente modo:
public class ContaCaratteri { Scanner reader = new Scanner(System.in); // variabile statica inizializzata static String testo; static char[] elencoChar; statica int [] freqCar public static void main(String [] args){ System.out.println("Introduci il testo"); testo = reader.next(); // gi definito int n = testo.length(); elencoCar = new char [n]; // gi definito freqCar = new int [n]; // gi definito int m = calcolaFrequenzaCar(); // nessun parametro // utilizzate variabili statiche System.out.println("Frequenze dei caratteri"); for ( int i = 0; i < m; i++ ) { System.out.print("Carattere <" + elencoCar[i]+">: "); System.out.println(freqCar[i]); }; char comando; do { // sessione interattiva con l'utilizzatore System.out.print("Frequenza di un Carattere o); System.out.print(" Caratteri pi Frequenti o Fine? (A/B/C)"); comando = Character.toUpperCase(reader.nextChar()); if ( comando == 'A' ) trovaFrequenzaCar(m); else if ( comando == 'B' ) trovaCarFrequenti (m);

11

} while ( comando != 'C' ); System.out.println("Bye"); } // fine main // METODO calcolaFrequenzaCar senza parametri formali static int calcolaFrequenzaCar ( ) { } // fine metodo calcolaFrequenzaCar // METODO trovaFrequenzaCar con un solo parametro formale static void trovaFrequenzaCar ( int m ) { // istruzioni non modificate // ... } // fine metodo trovaFrequenzaCar // METODO trovaCarFrequenti con un solo parametro formale static void trovaCarFrequenti ( int m ) { // istruzioni non modificate // ... } fine metodo trovaCarFrequenti

} // fine classe ContaCaratteri Non esistono regole precise se utilizzare parametri o variabili statiche. Unindicazione di massima che lutilizzo di parametri rende il metodo pi leggibile (abbiamo sottomano tutte le variabili utilizzate nel metodo senza dover consultare le variabili statiche della classe) e autosufficiente (il metodo pu essere utilizzato senza dover riportarsi le variabili statiche). Il suggerimento quindi di utilizzare i parametri formali il pi possibile.

4. Ulteriori Esercizi

Una palindroma una stringa che pu essere letta in entrambe le direzioni, ad esempio otto oppure ai lati dItalia. In effetti questultima stringa una palindroma solo se si trascurano gli spazi e i caratteri differenti da lettere quale lapice. Se si escludo gli spazi anche questa una palindroma: In girum imus nocte et consumimur igni. Provate a scrivere un metodo che riceve una stringa e risponde true se una palindroma considerando solo le lettere e saltando tutti gli altri caratteri compresi gli spazi. Un altro esercizio il secondo dellappello del 5/0/2011: Traccia: Si scriva un metodo Java verificaSottoVettori che riceve in ingresso due vettori di interi V e W, ognuno dei quali contiene valori distinti, restituisce true se e solo se gli elementi di W sono una sotto- sequenza inversa degli elementi di V, cio gli elementi di W sono tutti contenuti in V (anche se ne possono mancare alcuni di essi) e sono riportati nella stessa sequenza inversa in cui occorrono nel vettore V. Ad esempio, se V=[5,6,1,4,9,2,8,7] e W=[7,4,6,5] allora il metodo restituisce true mentre con W = [5,6,4,7] o W = [4,7,6,5] il metodo restituisce false. Soluzione:


static boolean verificaSottoVettori ( int [ ] V, int [ ] W ) { if ( !tuttiDistinti(V) || !tuttiDistinti(W) ) return false; int pos = 0; for ( int i = W.length-1; i >=0; i-- ) { pos = compresoDaPos(W[i],V,pos)+1; if ( pos == 0 ) return false; // oppure return pos != 0 } return true;

12

} static int compresoDaPos( int X, int [ ] V, int k ) { for ( int i = k; i < V.length; i++ ) if ( X == V[i] ) return i; return -1; } static boolean tuttiDistinti( int [ ] K ) { for ( int i = 0; i < K.length-1; i++ ) if ( compresoDaPos(K[i],K,i+1) >= 0 ) return false; return true; }

E interessante notare che lo stesso metodo compresoDaPos sia utilizzato da una parte per verificare la propriet di sottostringa definita dalla traccia e dallaltra per verificare preliminarmente che entrambi i vettori contengano tutti elementi distinti.

13