appunti java – Capitolo 6 pag. 1 6. Acquisizione dati da tastiera e sottoprogrammi Si sarà notato che gli esempi proposti non acquisiscono mai dati da tastiera, ma solo per assegnamento o generazione random di numeri. Tutto questo è imputabile al fatto che, per eseguire una acquisizione dati da tastiera, si rende necessario introdurre le classi di Input e Output di Java. In secondo luogo Java è un linguaggio utile per realizzare interfacce di input di carattere grafico ovvero è più semplice acquisire dati in un campo all’interno di una finestra video (vedi ambiente windows) che non direttamente da console o ambiente DOS come usualmente opera il Pascal. Per avere a disposizione la possibilità di acquisire dati da tastiera in modo interattivo faremo uso in seguito di una Classe Tastiera che contiene tre metodi con i quali è possibile acquisire da tastiera un dato di tipo String oppure un dato di tipo int o double. 6.1 L’uso di istruzioni di input da console con la classe Tastiera I metodi della classe tastiera sono i seguenti: public static String readString(); public static int readInt(); public static double readDouble(); esempio 1 : Si codifichi il seguente problema semplice “costruire un programma che acquisisca da tastiera una stringa e la stampi sul video”. Richieste: a) realizzare il main() program. Codifica public class cap06_es_01 { public static void main(String arg[]) { System.out.print("immetti una parola:"); String s=Tastiera.readString(); System.out.println(s); } } Commenti alla codifica La prima stesura del codice mostra il main che non contiene elementi nuovi, se si esclude l’invocazione di s=Tastiera.readString(). La chiamata di questo metodo della classe Tastiera è equivalente all’istruzione Readln(s) del Pascal con la sola differenza che non è una procedure (è un metodo statico che ha sempre la forma di una function Pascal). Si ricorda che per un corretto funzionamento la classe Tastiera (il file Tastiera.class) deve trovarsi nella stessa cartella del main(). Realizziamo ora il seguente programma : esempio 2 : Si codifichi il seguente problema semplice “costruire un programma che acquisisca la dimensione N dall’utente, generi un array di N interi e assegni ad ogni componente i valori 100, 101 … 100+N-1 e lo stampi”. Richieste: a) realizzare il main() program. appunti java – Capitolo 6 pag. 2 public class cap06_es_02 { public static void main(String arg[]) { System.out.print("Numero elementi per il vettore:"); int n=Tastiera.readInt(); int V[]=new int[n]; for (int i=0; i<n; i++) V[i]=100+i; //Fine generazione vettore for (int i=0; i<n; i++) System.out.println("V["+i+"]="+V[i]); // Fine stampa } } Commenti alla codifica int n=Tastiera.readInt(); significa che la readInt() non riceve dati di input, (non ha parametri tra le parentesi), ma restituisce un intero (int) in output. Servirà per acquisire un numero che corrisponde alla dimensione del vettore da generare nel main(). Si può notare che l’istruzione int V[]=new int[n]; consente di ‘dimensionare in corsa il vettore’. A differenza del linguaggio Pascal, i vettori di Java hanno un comportamento dinamico. I due cicli for che seguono servono rispettivamente per generare il vettore e per stamparlo. 6.2 Sottoprogrammi e Parametri in Pascal e in Java Si è visto che per codificare un sottoproblema in Pascal si utilizza lo strumento della Procedure e della Function. Tale strumento è utile per stabilire quali dati di input riceve e quali dati in output deve restituire il sottoproblema individuato. In Java è definito il concetto equivalente di Metodo e lo si è usato con analoghe modalità. Per Metodo si intende una funzione (una scatola nera) che riceve dati di input, dal programma invocante, esegue certe operazioni su un oggetto predefinito e restituisce al termine, eventuali dati di output al programma che lo ha invocato. L'interfaccia o intestazione di un metodo Java trova quindi una corrispondenza nelle intestazioni di procedure o function del Pascal. Di seguito si cercheranno di individuare criteri per stabilire l'esatta corrispondenza tra questi strumenti di codifica nei due linguaggi e di conseguenza permettere di trasformare procedure del Pascal in Metodi di Java. Nei paragrafi precedenti si è fatto uso di metodi Java statici (static) e di metodi dinamici il cui diverso funzionamento è stato schematizzato con un diverso utilizzo della memoria del computer per l'allocazione dei valori assegnati. Si tratta ora di capire come questi due tipi di strumento possano essere usati correttamente per risolvere sottoproblemi. In sintesi si potrebbe dire che un metodo statico corrisponde al concetto di Function (o procedure) del Pascal, in quanto può essere interpretato come uno spezzone di codice che riceve dati di input e restituisce risultati in output. La collocazione dei tre precedenti metodi statici nella classe Tastiera ha un appunti java – Capitolo 6 pag. 3 significato simile a quello delle UNIT del Pascal. In questo caso la classe Tastiera non ha alcun Costruttore. Un metodo dinamico è invece sempre associato ad una Classe che genera Oggetti, tali oggetti possono essere manipolati solo con l'invocazione di questi metodi dinamici, in particolare una classe di questo tipo è sempre dotata di un metodo particolare detto costruttore. Vediamo queste differenze attraverso esempi. Il sottoproblema, ormai risolto n volte, che calcola il Massimo Comun Divisore tra due numeri Naturali potrebbe essere schematizzato con le seguenti intestazioni: Pascal Intestazione sottoproblema Function MCD (a, b :integer):integer; Esempio di invocazione Var c:integer; c:=MCD(12, 18); Java public static int MCD( int a, int b); int c=MCD(12, 18) Java public (*)Naturale MCD(Naturale b); Naturale a, b, c; a = new Naturale(12); b = new Naturale(18); c = a.MCD(b); (b.1) (*)Se esistesse la Classe Naturale in Java (a.1) Nei tre casi precedenti si può interpretare l'interfaccia di MCD() come una "scatola nera" che riceve due numeri interi (a,b) e restituisce un risultato intero (c). a, b MCD c La terza riga della tabella individua un terzo caso puramente teorico, in quanto la classe Naturale non esiste in Java, ma serve per mostrare che l'invocazione di un metodo dinamico in Java ha una forma diversa e deve sempre essere chiamato anteponendo all'identificatore del metodo l'oggetto a cui è associato: in questo caso è un Naturale. <oggetto> . metodo( <parametri> ); Se ci si sofferma sul significato dei parametri di invocazione di un metodo si nota che l'intestazione può avere due forme: (a) Metodo statico intestazione: public static <tipo in out> nome_metodo ( <tipo e par. input> ); (a.1) invocazione di metodo statico non associato ad una classe: ris = nome_metodo(<tipo e par. Input>); [il metodo statico deve essere codificato nello stesso file del main() ] (a.2) invocazione metodo statico associato ad una classe: ris = <nome Classe>.nome_metodo(<tipo e par. Input>); [il metodo statico è codificato nel file della Classe: <nome Classe>] (b) Metodo dinamico intestazione: public < tipo par. out > nome_metodo ( <tipo e par. input> ); appunti java – Capitolo 6 pag. 4 (b.1) invocazione metodo dinamico ris = <Ident. Oggetto>.nome_metodo(<tipo e par. input>); [il metodo dinamico è codificato nel file della Classe che ha un costruttore per l’Oggetto] Quali sono nei due casi (a) e (b) i parametri di input e quelli di output ? Nel metodo statico non associato ad una classe (a.1) l'invocazione è analoga a quella di una Function del Pascal. I parametri di input sono tutti e solo quelli che compaiono entro le parentesi tonde (<tipo e par. input>), quelli restituiti devono sempre comparire raggruppati in una unica variabile (eventualmente strutturata) il cui tipo viene dichiarato davanti all'identificatore del metodo <tipo par. out>. Nel metodo statico associato ad una classe (a.2) ) l'invocazione richiede che si anteponga all'identificatore del metodo la corrispondente class di Java <nome Classe>. I parametri di input di output sono esattamente gli stessi caso (a.1). I metodi usati negli esempi precedenti: String s=Tastiera.readString(); Int n=Tastiera.readInt(); erano del tipo (a.2), ossia si trattava di due metodi statici codificati nel file della classe Tastiera. Nel metodo dinamico, sempre associato a una classe (b.1) l'invocazione richiede che si anteponga all'identificatore del metodo un oggetto <oggetto> allocato in precedenza con new. I parametri di input sono tutti quelli che compaiono entro le parentesi tonde, a cui si deve aggiungere l'oggetto con cui è invocato. Quelli di output sono quindi identici a quelli del precedente caso (a.1). I metodi di String e StringBuffer del capitolo precedente: String A=new String(”pippo”); String S = A.substring(int, int); riceve in input A che contiene la parola “pippo” e i due parametri di tipo int, restituisce in output la sottostringa S. Risolviamo ora un sottoproblema per mostrare le analogie e le differenze tra Pascal e Java, proponendo le diverse modalità java oltre alla modalità pascal. esempio 3. Si desidera realizzare un sottoprogramma “che determini la prima e l'ultima occorrenza di un carattere in una stringa assegnata” Richieste: a) risolverlo codificando procedura e main di invocazione in un unico file Pascal prova.pas. b) risolverlo codificando metodo statico e main di invocazione in un unico file Java di nome pro_3_6_uno.java. c) risolverlo codificando il metodo statico in una classe “contenitore” chiamata OpString, il main di invocazione è contenuto nel file pro_3_6_due.java. d) risolverlo codificandolo come metodo dinamico in una classe chiamata Stringa estensione della classe String di Java, il main di invocazione è contenuto nel file pro_3_6_tre.java. appunti java – Capitolo 6 Soluzione a) : Codifica Pascal pag. 5 Program prova; Var p,u : integer; procedure pri_ult(s : string; ch : char; Var p,u : integer); Begin (* codice omesso *) end; Begin (* main program *) pri_ult('banana', 'a', p,u); writeln(p," ", u); readln; end. Soluzione b) : file java cap06_es_03uno.java Per risolvere questo problema in java è indispensabile una classe Coppia ausiliaria che verrà codificata nel file Coppia.java. Tale classe si rende necessaria in quanto qualsiasi metodo statico e dinamico di Java restituisce un solo Oggetto e quindi senza un “Oggetto Coppia” non sarebbe possibile restituire due numeri interi. Primo file Coppia.java (necessario in tutte le soluzioni java) public class Coppia { private int p, int u; // l’oggetto Coppia ha come attributi 2 interi public Coppia(int a, int b) { p=a; u=b; } //Costruttore di una coppia; public void stampa(){ // Metodo che consente la stampa di una Coppia System.out.println(p+" "+u); } } Secondo file cap06_es_03uno.java Il codice della classe di cap06_es_03uno.java deve contenere sia il main() che il metodo statico pri_ult() come segue: public class cap06_es_03uno { public static Coppia pri_ult((String s, char ch) { // codice } public static void main(String arg[]){ coppia c; c = pri_ult("banana", 'a' ); (modalità di invocazione a.1) c.stampa(); (modalità di invocazione b.1) } } Soluzione c) : file java cap06_es_03due.java Per risolvere questo problema in java si deve costruire una ulteriore classe contenitore di nome OpString.java che conterrà il solo metodo statico pri_ult(). Rimane indispensabile la classe Coppia ausiliaria già codificata nel file Coppia.java. Codifica cap06_es_03due.java Il metodo statico pri_ult() è contenuto nella sottostante classe contenitore: public class OpString { public static Coppia pri_ult((String s, char ch) { // codice } } appunti java – Capitolo 6 pag. 6 Il codice di cap06_es_03due.java deve contenere SOLO il main() public class cap06_es_03due { public static void main(String arg[]){ coppia c; c = OpString.pri_ult("banana", 'a' ); (modalità di invocazione a.2) c.stampa(); (modalità di invocazione b.1) } } Soluzione d) : file java cap06_es_03tre.java Per risolvere questo problema in java si deve costruire una ulteriore classe Stringa.java sottoclasse di String che conterrà il solo pri_ult() che non è più statico. Rimane indispensabile la classe Coppia ausiliaria già codificata nel file Coppia.java. Codifica cap06_es_03tre Il metodo pri_ult() non è più statico ma è contenuto nella sottostante classe Stringa sottoclasse di String: public class Stringa extends String { public Stringa(String s) { super String(s); } // Costruttore di un oggetto Stringa. E’ una String Java public Coppia pri_ult( char ch) { // codice } // metodo dinamico applicabile a un oggetto Stringa // l’invocazione Stringa S=”pippo”; Coppia c=S.pri_ult(‘p’); // è il modo in cui si passano i due parametri Stringa e char } Il codice di cap06_es_03tre.java deve contenere SOLO il main() public class cap06_es_03tre { public static void main(String arg[]){ coppia c; Stringa S=new Stringa(”banana”); c = S.pri_ult('a'); (modalità di invocazione b.1) c.stampa(); (modalità di invocazione b.1) } Si nota che un metodo statico o dinamico restituisce sempre un solo tipo di dato e si rende di conseguenza sempre necessario definire il tipo di dato da restituire se questo è strutturato. Nel caso particolare, siccome devono essere restituiti due interi, si dovrà dichiarare una classe Coppia che diviene il contenitore unico dei due valori restituiti in output dal metodo. Anche in questo caso i tre sottoproblemi Java possono essere interpretati dalla seguente "scatola nera" s, ch c Pri_ult c non è più un tipo semplice ma è costituito dalla coppia (c.p,c.u). Un ulteriore sottoproblema che “elimini un carattere di posizione assegnata in una sequenza di caratteri” ha le seguenti intestazioni: Pascal Intestazione sottoproblema Function deleteCharAt(s:string;ind:integer):string; Esempio di invocazione Var sr:string; sr :=deleteCharAt(‘fico’,3) ; appunti java – Capitolo 6 Pascal Java Java pag. 7 Procedure deleteCharAt (Var s:string; ind:integer); Var s:string; s:=’fico’; deleteCharAt(s,3); public static StringBuffer deleteCharAt (StringBuffer StringBuffer sr,s ; s, int ind) s=new StringBuffer(‘fico’) ; sr=deleteCharAt(s,3); (a.1) public StringBuffer deleteCharAt (int ind) (*) StringBuffer sr,s ; s=new StringBuffer(‘fico’) ; (*)metodo presente in StringBuffer sr=s.deleteCharAt(ind); 6.E - Esercizi [di conversione di intestazioni da Pascal a Java static e loro prova] 6.1. Si è realizzato un programma Pascal che “genera un array di interi compresi tra –100 e 100 in modo random, di dimensione N con 1<=N<=Dim (Dim=20) e ricerca un intero X nell’array assegnato dall’utente, infine stampa il messaggio X esiste/NON esiste nell’array”. La struttura del main() Pascal e le procedure realizzate sono le seguenti: program es_pascal_6_1; Type Vett=array[1..20] of Integer; Var N,X:Integer; A:Vett; Procedure genera(Var V:Vett; Dim, vmin, vmax :integer ); Begin (* codice *) End; (*genera il vettore di interi*) Function ricerca(V:Vett, Dim:Integer; X:Integer):boolean; Begin (* codice *) End; (*Ricerca X nel vettore e restituisce true o false*) Procedure stampa(V:Vett, Dim:Integer); Begin (* codice *) End; (*stampa il vettore di interi per verificare*) Begin (* main *) Write(‘inserisci il numero di elementi da generare N=”); readln(N); Write(‘inserisci l’elemento da cercare nell’array X=”); readln(X); genera(A,N,-100,100); stampa(A,N); if (ricerca(A,N, X)) Writeln(X,’ Esiste.’) else Writeln(X,’ NON Esiste.’); readln; end. Richiesta: realizzare gli equivalenti metodi statici java e il main() equivalente adeguando i parametri, il tutto in una unica Classe Java. FARE uso della classe Tastiera. 6.2. Si è realizzato un programma Pascal che “genera due array di interi compresi tra –100 e 100 in modo random, la dimensione degli array è N1 ed N2 con 1<=N1 ed N2<=Dim (Dim=20), crea l’array unione dei due array generati e li stampa tutti”. La struttura del main() Pascal e le procedure realizzate sono le seguenti: program es_pascal_6_2; Type Vett=array[1..40] of Integer; Var N1,N2:Integer; A,B:Vett; Procedure genera(Var V:Vett; Dim, vmin, vmax :integer ); Begin (* codice *) End; (*genera un vettore di interi*) Procedure fondi(V1, V2:Vett, Dim1, Dim2:Integer; Var V:Vett; DimV:Integer); Begin (* codice *) End; (*fonde i due vettori V1 e V2 in V*) Procedure stampa(V:Vett, Dim:Integer); Begin (* codice *) End; (*stampa il vettore di interi*) Var appunti java – Capitolo 6 pag. 8 Begin (* main *) Write(‘inserisci il numero di elementi del primo array N1=”); readln(N1); Write(‘inserisci il numero di elementi del secondo array N2=”); readln(N2); genera(A,N1,B,N2,-100,100); stampa(A,N1); stampa(B,N2); fondi(A,N1, B, N2, C, N); stampa(C,N); readln; end. Richiesta: realizzare gli equivalenti metodi statici java e il main() equivalente adeguando i parametri il tutto in una unica Classe Java. FARE uso della classe Tastiera. [Di conversione di codici di procedure Pascal in metodi statici Java raggruppati in una classe “Contenitore” esterna. (Equivalente a una Unit del Pascal) e loro prova.] 6.3. Si desidera tradurre la seguente Unit realizzata in Pascal usando il tipo String di java. Unit Stringa; Type Stringa=String; interface function inverti(Stringa S):Stringa; (* inverte la stringa *) function conta(Stringa S; ch:char) : Integer; (* conta quanti ch ci sono in S*) implementation function inverti(String S):String; Begin (* codice *) End; function conta(Stringa S; ch:char) : Integer; Begin (* codice *) End; Begin end. (* fine Unit*) Program es_pascal_6_3; Uses Stringa; Var S:Stringa; ch:char; Begin S:=’pippo’; ch=“p“; Writeln(inverti(S)); Writeln(conta(S,ch)); Readln; End. Richiesta: realizzare gli equivalenti metodi statici java inserendoli in una classe Java esterna chiamata Stringa e realizzare un main() equivalente inserendolo in un diverso file. Adeguare i parametri dei metodi.