6. Acquisizione dati da tastiera

appunti java
pag.39
6. Acquisizione dati da tastiera
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.
Si farà un semplicissimo esempio di acquisizione dati da tastiera senza
approfondire tutti i particolari di questo metodo perché è scarsamente utilizzato
in un programma Java come si vedrà nei successivi sviluppi.
6.1.
Uso dell’input da console (tastiera)
Le classi di Input e output sono contenute nel package java.io l’informazione può
essere utile anche se di tali classi si farà un uso limitatissimo e non si entrerà nel
dettaglio. L’unica istruzione di output utilizzata fino ad ora è stata la
System.out.println( ) oppure print( ): la prima equivale alla Writeln() del Pascal e
la seconda alla Write( ).
Nel seguito si farà uso di una istruzione equivalente alla Read() del Pascal la
System.in.read( ). Questa è in grado di leggere solo semplici caratteri o byte da
tastiera. Iniziamo da un esempio.
esempio 6.1 : 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) Rispettare la seguente scomposizione ad albero:
• main() chiama leggi() che restituisce un intero, genera il vettore richiesto e lo stampa.
• leggi() : acquisisce da tastiera un intero che sarà la dimensione N del vettore;
b) realizzare il main() program.
main( )
leggi( )
c) realizzare le procedure leggi() e alt();
Prima codifica del main( )
appunti java
pag.40
Commenti alla codifica
La prima stesura del codice mostra il main che non contiene elementi nuovi, se si
esclude l’invocazione di leggi(). Questa procedura (più esattamente metodo
statico) é scritta in forma di prototipo vuoto. Anche in queste condizioni il
programma può essere compilato per testare gli eventuali errori.
L’intestazione della function ha il seguente significato:
int leggi() – significa che la leggi() non riceve dati di input ma restituisce un
intero (int) in output. Servirà per acquisire un numero che corrisponde alla
dimensione del vettore da generare nel main().
Prosecuzione della codifica: codice di leggi().
Le nuove parole chiave sono:
import java.io.*; che importa il package di i/o per usare il metodo di lettura da
tastiera System.in.read().
throws IOException se non si aggiunge questa istruzione il compilatore segnala
errore (Exception). Per ora la si interpreti semplicemente come un ordine al
compilatore di non segnalare errori in sede di acquisizione dati. Vedremo in
seguito cosa sono le eccezioni. (Notare che il throws è ripetuto nel main()).
throws NumberFormatException (tralascia le segnalazioni di eccezione sul
formato dei numeri) se non si aggiunge questa istruzione il compilatore segnala
errore. Per ora interpretiamola semplicemente come un ordine al compilatore di
non segnalare errori in sede di trasformazione di una stringa in intero operata
dal metodo
r= Integer.parseInt(s);
La chiamata di questo metodo statico associato agli oggetti Integer trasforma la
stringa s di input in un int r restituito in output.
Il ciclo while (<cond>) { <blocco> }; - acquisisce singoli caratteri da tastiera con
la System.in.read() e li pone nella stringa s fino al RETURN=‘\n’.
appunti java
pag.41
s=s+String.valueOf(c);
è un metodo statico associato agli oggetti Stringa che, applicato al carattere (c), lo
trasforma in una stringa che poi appende a s.
s=s.substring(in, fin);
è un metodo associato agli oggetti Stringa che, applicato alla stringa s con
parametri interi (in, fin), restituisce la sottostringa da in a fin e la assegna a s
stessa. (Serve ad eliminare da s il carattere di RETURN appeso dal ciclo
precedente).
Si può notare che le istruzioni del main() i=leggi(); V=new int[i]; consentono di
dimensionare in corsa il vettore. A differenza del linguaggio Pascal i vettori di
Java hanno un comportamento dinamico.
Il Run da console
Dopo la compilazione del programma si passa all’esecuzione che avverrà (a
differenza dell’esecuzione dei programmi precedenti) con l’opzione: Build: Run
with Console e non con l’opzione Build: Rum application. La ragione dell’uso di
questa diversa opzione di RUN risiede nel fatto che per la prima volta il
programma non deve mostrare solo un OUTPUT ma anche acquisire in INPUT;
questo impone l’uso della finestra di tipo Console.
Di seguito si nota la console di Input Output con i risultati:
appunti java
6.2.
pag.42
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 che riceve dati di input, dal
programma invocante, esegue certe operazioni su uno o più oggetti predefiniti 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 tesi a 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.
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 senza
essere vincolato a nessuna Classe o Oggetto dinamico.
Un Metodo dinamico è invece sempre definito in una Classe e viene sempre
applicato ad una sua istanza (Oggetto). Gli oggetti possono essere manipolati
solo con l'invocazione di questi metodi dinamici. Vediamo queste differenze
attraverso esempi.
Il sottoproblema, ormai risolto tante 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’intestazione di MCD() come una
"entità" che riceve due numeri interi (a,b) e restituisce l’intero o il Naturale (c).
a, b
MCD
c
appunti java
pag.43
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 par. out> nome_metodo ( <par. input> );
(a.1) invocazione di metodo statico definito all’interno di una classe:
ris = nome_metodo(<par. Input>);
(a.2) invocazione metodo statico associato ad una classe:
ris = <Classe>.nome_metodo(<par. Input>);
(b) Metodo dinamico intestazione:
public < tipo par. out > nome_metodo ( <par. input> );
(b.1) invocazione metodo dinamico
ris = <Oggetto>.nome_metodo(<par. input>);
Quali sono nei due casi (a) e (b) i parametri di input e quelli di output ?
Nel metodo statico (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 (<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>. I metodi int
leggi() e void alt() dell’esempio precedente erano di questo tipo.
Nel metodo statico associato ad una classe (a.2) l'invocazione richiede che si
anteponga all'identificatore del metodo la corrispondente class di Java <classe>.
I parametri di input di output sono esattamente gli stessi caso (a.1).
I metodi usati nell’esempio precedente:
int i = Integer.parseInt(String);
String s = String.valueOf( char );
erano del tipo (a.2).
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 S = A.substring(int, int);
è applicato all’aggetto A che deve essere allocato e riceve in input i due parametri
di tipo int, restituisce in output la sottostringa S.
appunti java
Se A,B sono due StringBuffer allocati con caratteri, l’invocazione
pag.44
StringBuffer C = A.appen(B);
Applica all’oggetto referenziato A l’operazione di append dell’oggetto in input B e
restituisce l’oggetto C modificato in output.
Un sottoproblema “che determini la prima è l'ultima occorrenza di un carattere
assegnato in stringa” ha le seguenti intestazioni:
Pascal
Java
Java
Intestazione sottoproblema
Esempio di invocazione
Procedure pri_ult(s : string; ch : char; Var p,u : Var p,u : integer;
integer);
pri_ult('banana', 'a', p,u);
writeln(p," ", u);
public static coppia pri_ult(String s, char ch);
class coppia { int p, int u }
public coppia pri_ult(ch:char);
(*)
(*)Se esistesse il metodo in String
coppia c;
c = pri_ult("banana", 'a' ); (a.1)
System.out.println(c.p+" "+c.u);
String s="banana";
coppia c;
c = s.pri_ult('a');
(b.1)
System.out.println(c.p+" "+c.u);
Si nota che un metodo statico o dinamico restituisce sempre un solo tipo di dato,
è di conseguenza sempre necessario definire il tipo di dato da restituire, sia che
questo tipo di dato appartenga a quelli predefiniti del linguaggio, sia che si tratti
di un tipo di dato strutturato di nostra definizione. 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 possono essere interpretati dal seguente
"schema"
s, ch
c
Pri_ult
(c) non è più un tipo semplice ma è costituito dalla coppia (c.p, c.u).
Un sottoproblema che “elimini un carattere di posizione assegnata in una
sequenza di caratteri” ha le seguenti intestazioni;
Pascal
Intestazione sottoproblema
Esempio di invocazione
Function deleteCharAt(s:string; ind:integer ):string; Var sr : string;
sr := deleteCharAt ('fico', 3);
Pascal
Procedure deleteChatAt(Var s:string, ind:Integer);
Java
Java
Var s:straing;
s := 'fico';
deleteCharAt (s, 3);
public static StringBuffer deleteCharAt
StringBuffer sr, s;
(StringBuffer s, int ind); s = new StringBuffer("fico");
sr = deleteCharAt(s, 3) (a.1)
public StringBuffer deleteCharAt(int ind); (*)
(*) metodo presente in StringBuffer
StringBuffer sr, s;
s = new StringBuffer("fico");
sr= s.deleteCharAt(ind);
appunti java
pag.45
6.E – Esercizi
Realizzazione o Conversione di sottoproblemi (con dati assegnati nel programma)
con acquisizione dell’input da tastiera. Uso dei metodi di String, StringBuffer,
Integer, Float, Double per risolvere problemi.
6.1
(conversione 5.2) Si desidera costruire un programma che “acquisita da
tastiera una stringa che contenga sia lettere che numeri determini e stampi
quante sono le prime e quanti i secondi e stampi a video il risultato”.
Richiesta: realizzare un main() che invochi una function leggi_stri() analoga
a leggi_int() dell’esercizio svolto del paragrafo 6.1. e fare uso dei metodi di
String e StringBuffer.
6.2
(conversione 5.3) Si desidera costruire un programma che “acquisita da
tastiera una stringa che contenga sia lettere che numeri sostituisca tutti i
numeri con in carattere ‘x’ e stampi vecchia e nuova stringa a video”.
Richiesta: realizzare un main() che invochi una function leggi_stri() analoga
a leggi_int() dell’esercizio svolto del paragrafo 6.1. e fare uso dei metodi di
String e StringBuffer.
6.3
(conversione 5.1) Realizzare un programma che “acquisita da tastiera una
stringa e un carattere determini quante volte il carattere ricorre nella
stringa e stampi a video il risultato”. Richiesta: rispettare la seguente
scomposizione ad albero in sottoproblemi:
leggi stri()
main()
leggi char()
conta()
main() deve invocare in sequenza leggi_stri(), leggi_char(), conta() e quindi
stampare sulla “console” la stringa, il carattere e la sua ricorrenza;
leggi_stri() deve acquisire da tastiera la stringa (String o StringBuffer);
leggi_char() deve acquisire da tastiera il carattere necessario;
conta() deve restituire il conteggio delle ricorrenze.
Nota: le “procedure” leggi_xx() possono essere realizzate in analogia a
leggi_int() dell’esercizio svolto del paragrafo 6.1.
6.4
(conversione 5.4) Realizzare un programma che “acquisita da tastiera la
dimensione N<=20 di un vettore (di Integer), lo generi in modo random con
interi compresi tra 0 e 99. Stampi a video tutte le componenti dell’array e la
somma.”. Richiesta: rispettare la seguente scomposizione ad albero in
sottoproblemi:
leggi dim()
main()
somma()
stampa()
appunti java
pag.46
main() deve invocare in sequenza leggi_dim(), somma(), stampa();
leggi_dim() deve acquisire da tastiera la dim<=20 del vettore.
somma() solo eseguire e restituire la somma, inviando gli opportuni
parametri di input;
stampa() deve ricevere i dati da stampare ed eseguire la stampa a video.
Nota: la “procedura” leggi_dim() può essere realizzata in analogia a
leggi_int() dell’esercizio svolto del paragrafo 6.1.
6.5
(conversione 5.7) Si desidera costruire un programma che generi un array
di dimensione dim<=30 di oggetti Number, immettendo in modo casuale
nelle componenti sia Integer compresi tra 1 e 30 che Double compresi tra
0.00000 e 0.99999 e infine stampi e verifichi la distribuzione generata.
Richieste: rispettare la seguente scomposizione in procedure:
leggi_dim()
scelta()
main()
gen_int()
gen_double()
stampa()
main(): invoca dim=leggi_dim() per acquisire la dimensione dim<=30
dell’array da allocare, alloca l’array di Number e quindi all’interno di un
ciclo invochi ripetutamente la procedura scelta() e, a seconda del numero
generato da questa, saranno invocate gen_int() o gen_double() per generare
l’oggetto desiderato e assegnarlo alla componente dell’array. Infine, fuori dal
ciclo, sarà invocata la procedura stampa() che mostrerà a video il contenuto
dell’array.
scelta(): restituisce casualmente (random) 0 oppure 1. Zero indica la
necessità di invocare gen_int() per assegnare un Integer all’array, Uno di
invocare gen_double() per assegnare un Double all’array;
gen_int(): genera un int compreso tra 1 e 30;
gen_double(): genera un double compreso tra 0.0000 e 0.9999.
stampa(): stampa l’array.
6.6
Si desidera costruire un programma che generi un array di dimensione
dim<=30 di oggetti Number, come il precedente 6.5. Oltre alla stampa delle
componenti dell’array come il precedente, stampi a fianco di ogni
componente il tipo di dato (la classe a cui appartiene). Vedi esercizio 5.8.
Esempio di stampa:
V[0] = 0.0073 Double
V[1] = 23 Integer
V[2] = 2 Integer
Ecc.