• Non ci sono risultati.

Costruire oggetti

N/A
N/A
Protected

Academic year: 2022

Condividi "Costruire oggetti"

Copied!
41
0
0

Testo completo

(1)

Costruire oggetti

Costruire oggetti: oggetto di tipo Point

• Utilizziamo una delle classi della “libreria” di Java che permette di costruire oggetti di tipo Point, che rappresentano punti del piano con coordinate intere.

• Per sapere come si costruisce un oggetto di tipo Point dovremo consultare la documentazione delle classi. Nella documentazione vengono descritti i campi, o dati, (forma) e i metodi (funzionalità) che agiscono sull’oggetto.

Costruire oggetti: oggetto di tipo Point

• Un “punto” viene rappresentato tramite una coppia di numeri interi che rappresentano le sue coordinate xe y.

• Se vogliamo creare un oggetto “punto”

dobbiamo costruire un’area di memoriain cui inserire i datidell’oggetto. (par. 2.6)

• Per creare un oggetto su usa l’operatore new.

Oggetto di tipo Point

• Esempio.

Point p1 = new Point(1, 0);

• I dati dell’oggetto p1 sono i due campi:

x=1 y=0

I valori 1, 0 si chiamano parametri di costruzione.

(2)

Definizione e costruzione di un oggetto

• Sintassi.

• Definizione

NomeClasse nomeoggetto;

• Costruzione

nomeoggetto =

new NomeClasse(parametri);

Definizione e costruzione di un oggetto

• Si può anche definire e costruire l’oggetto nella stessa istruzione:

NomeClasse nomeoggetto = new NomeClasse(parametri);

NomeClasse è quindi un tipo di dato, diverso dai tipi base.

Operatore new

Cosa fa l’operatore new:

1. crea una nuova area di memoria per l’oggetto

2. inizializzai dati dell’oggetto

3. restituisceil riferimento dell’oggetto (la sua locazione in memoria)

dati o campi

proprietà o metodi dati o campi proprietà o metodi

p1

Metodi di accesso e

metodi modificatori

(3)

Metodi di accesso e metodi modificatori

• Le operazioni per le variabili dei tipi base erano accesso e assegnamento.

• Anche per gli oggetti vogliamo poter:

• accederealle informazioni sullo stato dell’oggetto

• modificarelo stato dell’oggetto

• Gli oggetti possono essere usati solo attraverso i metodi della classe:

• metodi di accessoe metodi modificatori.

(par. 2.7)

Metodi che agiscono su oggetti

• Un metodo che agisce su oggetti ha le seguenti caratteristiche:

1) ha un nome

2) ha zero, uno o più parametri espliciti 3) restituisce un valore di un certo tipo oppure

void

4) ha un parametro implicito: l’oggetto che invoca il metodo.

• Un metodo staticonon agisce su oggettie ha solole prime trecaratteristiche.

Metodi di accesso e metodi modificatori

• Metodi di accesso per la classe Point:

getX, getY

• Restituiscono come valore (double) i parametri di costruzione dell’oggetto, rispettivamente le coordinate del vertice x e y

System.out.println(""""ascissa del """"

+ """"punto = """" + p1.getX());

Metodi di accesso e metodi modificatori

• Metodi modificatori per la classe Point.

• Vogliamo spostare il punto di 2 unità in direzione x e in direzione y: ossia eseguire una traslazione. Il metodo translate riceve come parametri le quantità da traslare e cambialo stato dell’oggetto:

p1.translate(2,2);

//le nuove coordinate del punto //saranno (3, 2)

(4)

Metodi che agiscono su oggetti

• Il metodo getX restituisce un valore di tipo double.

• Un metodo che restituisce un valore di un certo tipo viene invocato in una istruzione che può utilizzare quel valore.

• Il metodo translate è void: non restituisce nulla, ma modifica lo stato dell’oggetto.

• Un metodo di tipo voidviene invocato in una istruzione indipendente:

nomeoggetto.nomemetodo(parametri);

Metodi di accesso e metodi modificatori

//costruzione dell'oggetto punto Point p1 = new Point(1,0);

System.out.println("il punto P ha "

+ " coordinate P(" + p1.getX() +

", " + p1.getY() + ")" );

p1.translate(2,2);

System.out.println("il punto P ora “ +

ha coordinate P(" + p1.getX() +

", " + p1.getY() + ")" );

Una classe di collaudo

• La classe Point permette di utilizzare l’oggetto punto p1.

• Per vedere come “funziona” l’oggetto, dobbiamo realizzare una classe di prova che collaudal’oggetto punto: (par. 2.8)

• scrivere il main

• creare uno o più oggetti nel metodo main

• invocare i metodi

• visualizzare i risultati prodotti dai metodi confrontandoli con valori previsti.

Una classe di collaudo

• Come facciamo ad usare la classe Point e i suoi metodi?

• La classe Point appartiene ad un “pacchetto”

che contiene strumenti astratti per realizzare finestre e forme grafiche

Abstract Windowing Toolkit awt

• Dobbiamo collegarci a questo pacchetto o, come si dice, “importarlo”.

(5)

Pacchetti di classi

I pacchetti di classi (package)

• Le classi della libreria di Java sono raggruppate in pacchetti (package) che contengono classi aventi la stessa finalità.

• Per usare una classe di una libreria, bisogna importarlanel programma. (par.2.8)

• Sintassi

import nomePacchetto.NomeClasse;

• Nel nostro esempio:

import java.awt.Point;

I pacchetti di classi (package)

• Le classi System e String appartengono al pacchetto java.lang, che contiene classi di uso fondamentale per la programmazione.

Per tale motivo il pacchetto java.lang viene importato automaticamente.

• Si possono importare una o più o tutte le classi di un pacchetto:

import java.awt.*;

// * : si importano tutte quelle del // pacchetto, anche quelle che non // si usano ...

I pacchetti di classi (package)

• È possibile importare tutte le classi?

import java.*.*; // NO

• È bene importare solo le classi che si utilizzano: si ha una maggiore chiarezza.

• Come possiamo sapere dove sono le classi e quali sono i pacchetti da importare?

(6)

La documentazione della libreria

standard

Documentazione API

• Tutti gli ambienti di sviluppo Java forniscono esaurienti sistemi di supporto: “guide in linea”.

• La documentazione API (Application Programming Interface) si può trovare

all’indirizzo (par. 2.9)

http://java.sun.com/javase/6/docs/api/

index.html

Documentazione API

• La descrizione di una classe comprende l’elenco dei suoi metodi pubblici (la sua interfaccia) ed una sintetica descrizione delle motivazioni alla base del progetto della classe e delle modalità del suo utilizzo.

• Per ogni metodo, vengono indicati

• il nome e la funzionalità svolta

• il tipo ed il significato dei parametri richiesti, se ci sono

• il tipo ed il significato del valore restituito, se c’è

• le eventuali eccezioni lanciate in caso di errore

Documentazione API

• In un riquadro a sinistra ci sono tutte le classi (All Classes); selezionandone una, nel riquadro a destra apparirà la classe con la sua descrizione.

• Nel riquadro a destra compare un sunto dei vari metodi con indicato il tipo, il nome, …

• Selezionando un metodo si ha un’informazione più dettagliata.

(7)

Progettare Algoritmi

Progettare Algoritmi

• La progettazione di un algoritmo è una fase che richiede intelligenza e fantasia:

l’informatica è un’arte.

• Per imparare a progettare un algoritmo ci può aiutare la scomposizione del problema in problemi più semplici e la scrittura dell’algoritmo in un linguaggio intermedio (pseudocodifica) nel quale evidenziare i costrutti importanti senza vincoli di sintassi.

(8)

Progettare Algoritmi

• Scomporre un problema P in sottoproblemi significa individuare problemi più semplici P1, P2, P3 che risolti nell’ordine costituiscono la soluzione.

• Ciascuno dei sottoproblemi può a sua volta essere scomposto nuovamente.

P1 P2 P3

P

Progettare Algoritmi

• Fino a quando si prosegue con queste successive scomposizioni?

• Si prosegue fino al livello di operazioni elementari(operazioni aritmetiche, confronti, ecc.) o fino ad incontrare problemi già risolti in precedenza da noi (es. algoritmi di somma, ricerca, ordinamenti,…) o da altri (funzioni matematiche sin(x), gestione di array,…).

Progettare Algoritmi

• Nel linguaggi ad alto livello (Pascal, C, Java,..) ci sono degli enunciati (comandi) che organizzano le istruzioni con precisi significati: strutture di controllo.

• Sono essenzialmente tre: sequenza, scelta (decisioni), ciclo (iterazioni).

• Teorema di Bohm-Jacopini 1966: ogni algoritmo può essere scritto utilizzando unicamentequeste tre strutture.

Strutture di controllo

(9)

Strutture di controllo

• Una struttura di controllo è un blocco di istruzioni caratterizzato da un unico punto di ingresso e un unico punto di uscita e che si può schematizzare nel modo seguente:

S S

Strutture di controllo

• In tale modo l’algoritmo si può schematizzare:

S1

Si Si+1

Sn

prima 2)

successiva 3)

ultima 4)

……

……

Sequenza

• Si chiama sequenza una funzione che ha per dominio un sottoinsieme finito dei ℕ e il cui codominio acquisisce l’ordine posizionale degli elementi di ℕ .

• Indichiamo con Nk = {1, 2, 3, .., k}

s : Nk→ A

• Il codominio di s

{s(1), s(2), … s(k)} ma solitamente si scrive:

s1,s2, s3, …. sk

• si+1è il successivo di si, come posizione e non come valore

Sequenza

• La struttura di controllo sequenza rappresenta un gruppo di istruzioni eseguite secondo l’ordine posizionale (che nei linguaggi è l’ordine di scrittura delle istruzioni):

<istruzione 1>

<istruzione 2>

.………

<istruzione k>

• Cosa può essere <istruzione i> ?

(10)

Sequenza

• La generica istruzione può essere:

• assegnazione

• struttura di controllo

• operazione lettura/scrittura

• invocazione di un metodo

Esercizio

• Problema: Si consideri un trapezio T i cui angoli alla base maggiore sono rispettivamente 30° e 45°. Sono note le misure dei lati AB = 60 cm.(base maggiore) e AD = 20 cm. (il lato obliquo che forma 30° con la base). Calcolare il perimetro e l’area del trapezio.

D C

A K H B

Esercizio

• Analisi del problema.

• Vediamo di generalizzare alcuni dati:

possiamo voler risolvere il problema per dei valori generici di AB e AD (purché AB >AD).

• Non possiamo invece considerare un valore qualunque per gli angoli alla base, perché forniscono importanti relazioni per individuare le misure degli altri lati.

Esercizio

• Il problema propone già una scomposizione:

• I scomposizione:

• P si suddivide in

• P1 : calcolare il perimetro

p = AB+AD+DC+CB

• P2: calcolare l’area

a = (AB+DC)*CH /2

• Base di conoscenza: le formule sono note.

(11)

Esercizio

• Scomposizioni successive:

• P1 : calcolare il perimetro

• P1.1 calcolare DC

• P1.1.1 AK = AD * √ 3 /2

• P1.1.2 HB = CH = DK = AD/2

• P1.1.3 DC = AB-AK-HB

• P1.2 calcolare CB = HB * √2

• P1.3 p = AB+AD+DC+CB

• P2: calcolare l’area

• P2.1 CH = HB

• P2.2 a = (AB+DC)*HB/2

Esercizio per casa

• Esercizio. Scrivere in Java le istruzioni di questo algoritmo (sono tutte assegnazioni, ma bisogna rispettare l’ordine).

• Scegliere un nome per la classe, definire le variabili con il tipo double, assegnare i valori per i lati, utilizzare Math.sqrt(2) per il calcolo della radice quadrata (i dati relativi agli angoli sono serviti solo per trovare le relazioni).

• Esercizio. Calcolare la misura della diagonale del rombo equivalente agli 8/5 di un trapezio essendo nota la misura dell’altra diagonale.

Predicati

Predicati

• Un predicato è una funzione a valore nell’insieme B B B B

P: A →B B B B

x ∈ A P(x) può essere vero o falso

• x deve avere un valore, altrimenti il predicato non è valido.

Esempio. A = ℝℝℝℝ P(x) : x>5 P(7) è vero P(3) è falso

(12)

Struttura condizionale

Struttura condizionale

• Sia P un predicato (condizione); P viene valutato e sarà o vero o falso. Si vuole eseguire

<istruzione1> nel caso in cui P sia vero e

<istruzione2> nel caso in cui P sia falso, oppure può anche accadere che non dobbiamo fare nulla nel caso in cui P sia falso:

se P

alloraistruzione1 //P vero altrimenti istruzione2 //P falso //fine scelta

se P

allora istruzione1 //P vero //fine scelta

Struttura condizionale

• Esempio.

• Dati due numeri reali a e b, determinare il più grande dei due.

• Analisi.

• Tra le operazioni elementari sui numeri non c’è il calcolo del massimo, quindi dobbiamo utilizzare una struttura condizionale: il predicato sarà rappresentato dal confronto tra i due numeri; il confronto è una operazione elementare.

Struttura condizionale

• Progetto.

Algoritmo massimotradue

variabili a, b, max di tipo reale acquisire i dati per a e b e stamparli se a>b

allora max a altrimenti max ← b //fine scelta

stampare il valore di max //fine algoritmo

(13)

Struttura condizionale

• L’enunciato if. (par. 5.1)

• Sia P un predicato (condizione), la sintassi è:

if(P)

istruzione; //una sola istruzione if(P){//blocco di istruzioni

………

istruzione;

………

}

se P

allora istruzione //fine scelta

Struttura condizionale

• L’unica istruzione, o il blocco di istruzioni individuato dalla coppia di parentesi graffe, viene eseguita solo nel caso in cui il predicato P risulti vero.

• Esempio.

int a, b;

//assegnare i valori ad a e b // e stamparli if(a>b)

System.out.println(""""a e' maggiore """"

+ """" di b"""");

Struttura condizionale

• Sintassi con le due alternative; sia P la condizione:

if (P)

istruzione;

else

istruzione;

if(p){//blocco istruzione;

}

else{//blocco istruzione;

}

se P

allora istruzione altrimenti istruzione //fine scelta

Struttura condizionale

• Attenzioneal ;

• Dopo la parentesi graffa (prima di else) non ci deve essere il simbolo ;

• Se si mette il ; l’istruzione if termina e il compilatore segnala che c’è una elsesenza if if(x>0){

System.out.print(""""x = """" + x);

System.out.println(""""e' positivo"""");

};

else ...

il compilatore segnala:'else' without 'if'

(14)

Struttura condizionale

• Traduciamo l’algoritmo

massimotradue

public class Massimo2{

public static void main (String[] arg){

double a,b, max;

a=10; b=27; //assegnazione if (a>b)

max = a; //P vero else

Struttura condizionale

• Se vogliamo eseguire due istruzioni, dobbiamo usare un blocco:

if (a>b) {//P vero max = a;

System.out.println(""""a > b"""");

}

else {// P falso max = b;

System.out.println(""""b >= a"""");

}

System.out.println(" " " " max = """" + max);

Struttura condizionale

• L’istruzione

System.out.println("""" max = """" + max);

• viene eseguita con qualunque valore di P (vero, falso) e rappresenta il punto di prosecuzione dell’esecuzione dell’algoritmo.

• Ma se abbiamo più scelte come possiamo scriverle? In sequenzao annidate? (par. 5.3)

Struttura condizionale

• Scelte multiple. Siano P e Q due predicati, possiamo avere:

1) if insequenza if (P)

istruzione;

if(Q)

istruzione;

2) ifannidate

if(P) (oppure) if(P)

istruzione; if(Q)

else if(Q) istruzione;

(15)

Struttura condizionale

• Nel primo caso il predicato Q viene valutato in ogni caso: P vero e P falso.

• Nel secondo caso, il predicato Q viene valutato solo se P è falso; oppure solo quando P è vero.

• Quale delle due strutture scegliere dipende da ciò che si vuole realizzare nell’algoritmo.

L’ordine con cui si effettuano le scelte può essere fondamentale per l’algoritmo: un ordine diverso può dare un algoritmo errato.

Struttura condizionale

• Esempio. Supponiamo di voler stampare una stringa che esprima la ricchezza o meno di una persona in base al suo saldo bancario

povero medio ricco straricco

0 1000 50000 200000

• Dopo aver acquisito il valore del saldo (double) eseguiremo i vari confronti con i valori che individuano gli intervalli di minore o maggiore ricchezza stabilendo un valore da stampare (String).

Struttura condizionale

String s; double saldo = ...;

if(saldo >= 200000) s="straricco";

else if(saldo >= 50000) s="ricco";

else if(saldo >= 1000) s="medio";

else if(saldo >=0) s="povero";

else s="valore non valido";

System.out.println(s);

Struttura condizionale

• Casi di prova.

• Per essere sicuri che la struttura funzioni bene dobbiamo provare tutte le alternative, provando diversi valori per saldo:

-2 900 1000 1200 51000 200000 250000

• In corrispondenza a questi valori ci aspettiamo un valore per s: non valido, povero, medio, medio, ricco, straricco, straricco.

• Se nel predicato si mette > invece di

>= il secondo e il quarto valore di s cambiano.

(16)

Struttura condizionale

• Proviamo ad invertire l’ordine delle scelte:

if(saldo >= 1000) s="medio"; else if(saldo >= 0)

s="povero"; else if(saldo >= 50000)

s="ricco";

else if(saldo >=200000) s="straricco";

else s="valore non valido";

• Per saldo = 1200 si ha s= “medio” e le altre scelte non vengono eseguite; cosa accade per saldo = 51000?

Struttura condizionale

• Se mettiamo le if in sequenza otteniamo:

if(saldo >= 200000) s="straricco"; if(saldo >= 50000)

s="ricco"; if(saldo >= 1000)

s="medio"; if(saldo >=0)

s="povero";

//else s="valore non valido"; System.out.println(s);

• Se il saldo fosse 200000, verrebbe stampato l’ultimo valore perché le assegnazioni sono in sequenza .

Struttura condizionale

• Esercizio. Scrivere delle istruzioni che autorizzano il prelievo da un conto corrente solo sel’ammontare del prelievo è inferiore al saldo:

double saldo = 10000; //saldo iniziale double prelievo = 200;

if (prelievo <= saldo ) saldo = saldo - prelievo;

System.out.println(""""restano """" + saldo +

"""" euro"""");

Struttura condizionale

• E se il prelievo è superiore? Il programma deve dare una risposta anche nel caso in cui il prelievo sia superiore al saldo e ….

impedirlo: il saldo non può essere negativo.

if (prelievo <= saldo ) saldo = saldo - prelievo;

if (prelievo > saldo)

System.out.println("impossibile: conto scoperto");

• In questo algoritmo c’è un errore logico.

(17)

Struttura condizionale

• Se il prelievo viene eseguito ma supera la metà del saldo, la seconda if viene eseguita e verrà stampato “impossibile”, perché il saldo è stato modificato.

• Eseguiamo, invece, le istruzioni in alternativa:

if (prelievo <= saldo ) saldo = saldo - prelievo;

else

System.out.println("impossibile:"

+ " conto scoperto");

Struttura condizionale

• If annidate e il problema dell’else sospeso.

• Siano P e Q due predicati; non essendoci un costrutto “endif” si pone il seguente problema:

if(P) if(Q)

istruzione1;

else istruzione2;

• La else a quale delle due if si riferisce?

(Errori comuni 5.2)

Struttura condizionale

• Esempio.

int a,b,c;

a=9; b=7; c=4;

if (a>b) //vero if(b<c) //falso

b=b+1;

else b=b-1;

System.out.println("""" b = """" + b);

• Quale valore viene stampato per b? 6 o 7?

• La else si riferisce alla prima if o alla seconda?

Struttura condizionale

• La regola del linguaggio è la seguente:

• La else si riferisce alla if più vicina (interna) quindi b=6.

• Si può alterare la regola inserendo della parentesi graffe:

int a,b,c; a=9; b=7; c=4;

if (a>b){

if(b<c) b=b+1;}

else b=b-1;//si riferisce alla prima if System.out.println("""" b = """" + b); //7

(18)

Un po’ di stile

• Quando scriviamo una struttura è bene utilizzare una modalità di scrittura che faciliti la lettura dell’algoritmo, rientrando di alcuni spazi a sinistra le istruzioni che si riferiscono al vero e al falso, ed incolonnando le else sotto la parola if.

• Però questo stile non risolve l’attribuzione della else in caso di più strutture condizionali, che si ottiene con l’inserimento (o meno) della coppia di parentesi graffe.

Operatori di confronto

Operatori di confronto

• Abbiamo visto con i tipi numerici degli operatori di relazionecon i quali si potevano effettuare confronti: (par. 5.2.1)

< > <= >= == !=

• Nel caso di operatori composti con due simboli nonsi devono introdurre spazi.

• Errore frequente: confondere

= (assegnazione) a = 3;

== (confronto di uguaglianza) if (a == 3)

Operatori logici

(19)

Operatori logici

• I predicati si possono comporre con degli operatori(connettivi) logici (booleani):

(par. 5.4.3)

&&(and) || (or) ! (not)

• Sia P un predicato, e sia D il sottoinsieme di A per il quale P(x) è vero:

P : A →

B B B B

D = { x | P(x)}

Operatori logici

• Congiunzione: and && e

• Siano P e Q due predicati e siano A e B gli insiemi associati al vero.

• P e Q è vero ⇔⇔⇔⇔ sono entrambi veri P Q P e Q

V V V V F F

F V F A ∩∩∩∩ B intersezione

F F F

Operatori logici

• Disgiunzione: or || o

• Siano P e Q due predicati e siano A e B gli insiemi associati al vero.

• P o Q è falso ⇔⇔⇔⇔ sono entrambi falsi P Q P o Q

V V V V F V

F V V A ∪ B ∪∪ unione F F F

Operatori logici

• Negazione: not ! non

• Sia P un predicato e sia A l’insieme associato al vero.

• P vero ⇒⇒⇒⇒ non P falso e viceversa P non P

V F

F V A complementare

(20)

Operatori logici

• Nella logica classica si considerano anche altri operatori:

• nand (not and) complementare dell’intersezione

P Q P nand Q V V F V F V

F V V A ∩∩∩∩ B F F V

Operatori logici

• nor (not or) complementare dell’unione

P Q P nor Q

V V F

V F F

F V F A ∪ B∪∪ F F V

Operatori logici

• xor (or esclusivo) differenza simmetrica P Q P xor Q

V V F V F V

F V V (A ∪∪∪∪ B) - (A ∩∩∩∩ B) F F F

Operatori logici

• Leggi di De Morgan (Argomenti av. 5.5) A ∪ B = A ∩∪∪ ∩∩ B

A ∩ B = A ∪∩∩ ∪∪ B

• L’algebra booleana, che considera solo i valori vero e falso, ha trovato applicazione nella progettazione dei circuiti elettronici: F(a1, a2, … an) funzione con n≥ 1 ingressi rappresenta una porta logica: dispositivo che produce l’uscita 0 o 1 a seconda della funzione logica che rappresenta. Ad esempio una “porta and” avrà in uscita 1 se e solo se tutti gli ingressi sono 1.

(21)

Operatori logici

• In tale modo si costruiscono gli “addizionatori di bit”

per realizzare circuiti (più porte logiche collegate tra loro) per eseguire la somma; ad esempio consideriamo due possibili ingressi

0 + 0 = 0

0 + 1 = 1

1 + 0 = 1

1 + 1 = 0 e riporto 1 primo secondo bit in uscita ingresso ingresso

S. Congiu: Calcolatori elettronici

Porte logiche: not or and

S. Congiu: Calcolatori elettronici

Porte logiche: nand Uso di and e or

• Problema. Dati tre numeri reali a, b, c stabilire se sono i lati di un triangolo e di quale triangolo si tratta.

• Analisi.

1. Per essere delle misure di segmenti devono essere tutti e tre positivi.

2. Per essere i lati di un triangolo, ciascuno deve essere minore della somma degli altri due

3. Per essere un triangolo equilatero tutti e tre i lati devono essere uguali

4. Per essere isoscele due lati sono uguali (si hanno tre possibili coppie)

5. Se il triangolo non è né equilatero né isoscele allora è scaleno.

(22)

Uso di and e or

1. Per essere delle misure di segmenti devono essere tutti e tre positivi, pertanto and:

a>0 e b>0 e c>0 (a> 0) && (b>0) && (c>0)

2. Per essere lati di un triangolo, ciascuno deve essere minore della somma degli altri due, pertanto and:

a<b+c e b<a+c e c<a+b (a<b+c) && (b<a+c) && (c<a+b)

Uso di and e or

3. Per essere equilatero tutti e tre i lati devono essere uguali:

a = b e b = c (a == b) && (b == c)

• Attenzione: non si scrive a==b==c

• Analogamente: la scrittura matematica 0 ≤ x ≤ 1

• si scrive componendo con and due predicati:

(0 <= x) && (x <= 1)

Uso di and e or

4. Per essere isoscele due lati devono essere uguali e si hanno tre possibili coppie, pertanto or:

a=b o b=c o a=c

(a == b) || (b == c) || (a == c)

5. La condizione è in alternativa alle condizioni 3. e 4. .

Operatori con i bit

• In Java esistono operatori binari che agiscono sui singoli bit: & (and), | (or), ^ (or esclusivo) e un operatore unario – (negazione).

• Queste operazioni si possono applicare a variabili booleane o intere. Quando agiscono su variabili intere, l’operazione è applicata ai

singoli bit. (Appendice I)

• Il significato è quello dell’operazione rappresentata:

0&0 è 0 (falso); 0 | 1 è 1(vero); …

(23)

Operatori con i bit

• Vediamo come opera su variabili intere.

Esempio. (Appendice I)

46 & 13 il risultato in decimale sarà 12 Dobbiamo infatti pensare alla rappresentazione

binaria dei due numeri e poi agire con & sui singoli bit: consideriamo 32 bit (tipo int) 46 = 00….0101110; 13 = 00…. 0001101

• L’operatore inizia ad agire dal bit più a destra.

Operatori con i bit

• Pensiamo ai bit incolonnati:

0 0 …. 0 1 0 1 1 1 0 0 0 …. 0 0 0 1 1 0 1 &

0 0 …. 0 0 0 1 1 0 0

• Il numero binario 000…. 001100 corrisponde al numero decimale 2 3+ 2 2= 12.

• Con or 46 | 13 abbiamo il numero binario 00…. 0101111 che corrisponde a

2 5+ 2 3+ 2 2+ 2 1+ 2 0= 32 + 8+4+2+1 = 47

Operatori con i bit

• Esistono anche altri operatori, detti di scorrimento:

<<, >>, >>>.

• Questi operatori spostano i bit verso sinistra (<<) e verso destra (>> lascia il segno, >>> sposta anche il bit del segno).

• Lo spazio “lasciato vuoto” viene riempito con 0.

• In questo corso non useremo questi operatori sugli interi e come operatori logici useremo solo&&, ||, !.

Associatività e precedenza degli

operatori

(24)

Associatività degli operatori

• In espressioni senza le parentesi è necessario stabilire l’ordine con cui vengono valutate le espressioni.

• Gli operatori seguono delle regole di precedenzae associatività. (Appendice E)

• Indichiamo con op un generico operatore binario che coinvolge le variabili x, y, z e consideriamo questa espressione

x op y op z

Associatività degli operatori

• Si dice che un operatore associa a sinistra se l’espressione

x op y op z

viene valutata nel modo seguente:

(x op y) op z

• Invece si dice che un operatore associa a destrase viene valutata

x op(y op z)

Associatività degli operatori

• Ad esempio, gli operatori aritmetici associano a sinistra:

x + y + z

viene valutata nel modo seguente:

(x + y) + z analogamente per *, \, %, -

• Anche gli operatori logici seguono delle regole di precedenza e associatività in espressioni senza le parentesi.

Precedenza degli operatori

• Per gli operatori logici la valutazione viene fatta da sinistra a destra, associano a sinistra:

x && y && z (x && y) && z

• Si possono costruire espressioni e predicati componendo operatori logici, aritmetici e di confronto.

(25)

Precedenza degli operatori

• L’operatore not precede gli operatori aritmetici, seguono poi gli operatori di confronto, l’operatore and ed infine all’operatore or.

• Attenzione. Altri linguaggi utilizzano precedenze diverse; nei casi dubbi è sempre consigliabile utilizzare le parentesi.

Precedenza degli operatori

• Valutazione pigra dei predicati.

(Argomenti av. 5.4)

• In una espressione booleana composta da due o più predicati che utilizzano gli operatori &&

e ||, la valutazione si ferma non appena si può decidereil valore di verità dell’espressione.

• Nel predicato P && Q, se P è falso Q non viene valutato.

• Nel predicato P || Q, se P è vero Q non viene valutato.

Precedenza degli operatori

• Esempio.

if((x>5 && x<10) || (y<7)) nel caso x=8, il predicato y<7 non verrà valutato, essendo vero il primo.

• E bene però non approfittare di tale regola considerando un possibile secondo predicato privo di significato: il predicato composto non è più simmetrico.

• Se si vuole eseguire il confronto completo, si possono usare gli operatori & e |.

Precedenza degli operatori

• Introducendo le parentesi rotonde possiamo modificare la precedenza nella valutazione:

if (!(x < 0 || x > 10))

// vero se 0≤ x ≤ ≤10 : x>=0 and x<=10 : // non(x<0 or x>10) leggi di De Morgan

if (!(x < 0) || x > 10)

// vero se x ≥ 0 oppure se x>10 //e quindi vero se x ≥ 0 : non(x<0)

(26)

Errori con gli operatori

• Espressioni matematiche del tipo:

0 ≤ x ≤≤ 10 0<=x<=10 //NO x e y ≤ 0 x && y <=0 //NO

devono essere scritte componendo due predicati:

1. x>=0 && x<=10 2. x<=0 && y<=0

• Perché il compilatore segnala errore?

Errori con gli operatori

• Caso 1. Il compilatore analizza l’espressione logica e trova due operatori di confronto, quindi esamina il primo confronto (x<=0) da sinistra stabilendo che il risultato sarà un valore booleano; successivamente esamina il secondo operatore relazionale (<=10): dei due operandi, il primo è di tipo boolean, mentre il secondo è di tipo int e segnala un errore:

operator <= cannot be applied to boolean,int

Errori con gli operatori

• Caso 2. Il compilatore analizza l’espressione logica e trova un operatore di confronto <= che ha la precedenza sull’operatore booleano &&, il risultato del confronto sarà un valore di tipo boolean e si troverà ad applicare l’operatore

&& ad un operando di tipo numerico (x) ed uno di tipo boolean, segnalando:

operator && cannot be applied to int,boolean

Leggere dati in

ingresso

(27)

Leggere dati in ingresso

• Nei programmi visti finora abbiamo dato un valore iniziale ai dati eseguendo delle assegnazioni: in tale modo avremo sempre la stessa elaborazione ad ogni esecuzione:

• massimo tra due numeri, acronimo, trapezio, …

• Se vogliamo eseguire il programma su altri dati, è necessario modificare il codice sorgente (in particolare, le inizializzazioni delle variabili) e compilarlo di nuovo, prima di eseguirlo.

Leggere dati in ingresso

• Per poter scrivere programmi generali, dobbiamo permettere all’utente di inserire dei propri dati: i programmi devono perciò ricevere dati in ingresso.

• Lo scambio di informazioni tra l’utente e il programma e viceversa è rappresentato da un flusso di dati.

• Quando l’ingresso è rappresentato dalla tastiera si dice che il programma viene eseguito in maniera interattiva: durante l’esecuzione il programma si ferma in “attesa”

di ricevere i dati (input).

Leggere dati in ingresso

• Il flusso di dati per l’uscita è un oggetto di nome System.out. Per System.out, di tipo PrintStream, è disponibile un metodo print che trasmette le informazioni (stringhe, numeri, caratteri, …).

• Il flusso di dati per l’ingresso è un oggetto di nome System.in. Per l’oggetto System.in, di tipo InputStream, le cose sono più complicate perché questa classe non possiede metodi comodi per la ricezione di dati numerici e stringhe.

Leggere dati in ingresso

• La classe InputStream ha un metodo in grado di leggere un byte alla volta (oppure sequenze di byte) ma non altri dati.

• Quando introduciamo i dati, introduciamo delle sequenze di caratteri che rappresentano il valore di un tipo di dato: ciò che viene inserito da tastiera viene codificato in binario e successivamente ricostruito prima come gruppo di caratteri (stringa) e poi interpretato come valoredi una variabile del tipo di dato.

(28)

Leggere dati in ingresso

• Esempio.

3 3 intero

7.5 7.5 reale

ciao 0100110…01 ciao stringa

true true booleano

• Occorrono dei metodi per poter interpretare i diversi tipidi dato in ingresso.

• Vedremo due classi per gestire la lettura: la classe Scanner e la classe Bufferedreader, ciascuna con i suoi metodi.

Leggere dati in ingresso

• La classe Scanner rende abbastanza agevole la lettura dei dati. (par. 4.7)

• Il flusso di dati di ingresso System.in viene utilizzato per costruire un oggetto di tipo Scanner (“scansionatore”), sul quale possono agire dei metodi per leggere l’intero

“successivo”, il reale “successivo”, la stringa

“successiva”, il booleano “successivo”.

Leggere dati in ingresso

• Con l’istruzione

Scanner in = new

Scanner(System.in);

si costruisce un oggetto in con il quale si possono invocare i metodi nextDouble, nextInt, nextBoolean, next per acquisire rispettivamente il prossimo valore di:

un numero in virgola mobile, un numero intero, un logico e una stringa.

Leggere dati in ingresso

• I valori sono separati da spazi o da tabulazioni, se stanno sulla stessa riga, oppure da un fine riga (premere il tasto Invio).

• È possibile anche leggere l’intera riga, come stringa, utilizzando il metodo nextLine.

• I metodi precedenti, applicati all’oggetto in, restituiscono rispettivamente un valore di tipo double, int, boolean e String.

(29)

Leggere dati in ingresso

• La classe Scanner fa parte del pacchetto java.utilche deve essere importato:

import java.util.Scanner;

• Vedremo più avanti come acquisire le informazioni da un file di dati, invece che da tastiera: il file con le informazioni dovrà essere costruito, prima di mandare in esecuzione il programma.

Leggere dati in ingresso

• Nella modalità di inserimento da tastiera, quando si incontra una operazione di “lettura”

il programma “resta in attesa” dei valori da acquisire.

• Per ricordare “all’utente” i valori di quali variabili devono essere introdotti, può essere utile inserire delle “stampe” che ricordino il nome di tali variabili.

• Si deve sempre ricordare che l’acquisizione è sequenziale.

Leggere dati in ingresso

• Esempio.

System.out.println(""""inserire un"""" +

"

" "

" numero reale, un intero, """" +

"""" un booleano e una stringa"""");

double a = in.nextDouble();

int b = in.nextInt();

boolean c = in.nextBoolean();

String d = in.next();

//inserire valori per a,b,c,d

Leggere dati in ingresso

• I progettisti della classe Scanner hanno usato la localizzazione.

• Significa che il comportamento del programma è legato alla configurazione del sistema su cui viene fatto eseguire.

• A seconda della configurazione (anglosassone o italiana) un valore di tipo double dovrà essere inserito nella forma:

parteIntera.parteDecimale  ad esempio 4.35 parteIntera,parteDecimale ad esempio 4,35

(30)

Leggere dati in ingresso

• Per uniformità con quello che avviene in altri linguaggi (si possono voler usare gli stessi dati in programmi scritti in linguaggi diversi), conviene inserire i dati reali con il punto decimale. Per questo si deve esplicitamente richiedere la localizzazione anglosassone:

import java.util.Locale;

e prima di eseguire la lettura inserire:

in.useLocale(Locale.US);

Leggere dati in ingresso

• È importante non confondere i metodi next e nextLine: entrambi restituiscono una stringa, ma il primo restituisce come stringa il gruppo di caratteri che termina con uno spazio, o fine riga o tabulazione, mentre il secondo restituisce l’intera riga, che può essere composta da un gruppo di parole anche separate da spazi.

• Questo può essere utile se si vogliono inserire parole composte (ad esempio, un cognome composto da due parole).

Leggere dati in ingresso

• Esempio.

String citta = in.nextLine();

// La Spezia

String stato = in.next();

// Italia

Assegnazione tra

riferimenti

(31)

Assegnazioni tra riferimenti

• Con l’istruzione

Point p1 = new Point(1,0);

• abbiamo costruito un oggetto punto a cui sono associate due aree di memoria: l’oggetto e la variabile p1.

p1è una variabile il cui tipoè la classe Point e contiene il riferimento all’area di memoria che rappresenta l’oggetto creato.

Assegnazioni tra riferimenti

• Le aree di memoria di p1e dell’oggettosono diverse e le abbiamo indicate con bordi diversi: queste aree che si trovano in zone diversedella memoria:

oggetto variabile di un

tipo base p1

Assegnazioni tra riferimenti

• I tipi base e i riferimenti a oggetti stanno in una zona della memoria chiamata Stack: la loro locazione di memoria è assegnata in fase di compilazione.

• Gli oggetti stanno in una zona della memoria chiamata Heap: l’area di memoria viene creata durante l’esecuzione del programma tramite l’operatore new che ne restituisce il riferimento(che dovrà essere assegnato ad una variabile oggetto del tipo della classe).

Assegnazioni tra riferimenti

• Il riferimento è vincolato al suo tipo.

• In una variabile di tipo NomeClasse si possono memorizzare riferimenti solo del tipo NomeClasse:

Point p1 = new Point(1,0);//esatto

String punto =

new Point(5,10);//ERRATO

(32)

Assegnazioni tra riferimenti

• Nei tipi base potevamo fare:

int a, b;

a=5;

b=a;

• Avevamo quindi due variabili intere contenenti lo stesso valore.

• È possibile eseguire assegnazioni anche con i riferimenti?

Assegnazioni tra riferimenti

• Consideriamo l’istruzione Point p2;

p2 è una variabile di tipo Point e quindi può contenere riferimenti ad oggetti di quel tipo e possiamo fare:

p2 = p1;

p2

Assegnazioni tra riferimenti

• Ha lo stesso significato di: b=a ? Non proprio ….

• Le variabili p1 e p2 sono due variabili distinte, ma l’oggetto è uno solo.

p1 p2

Assegnazioni tra riferimenti

• La variabile p2, con l’assegnazione, riceve il riferimento di p1 e quindi “vede” la stessa area di memoria:

• Non viene creato un nuovo oggetto, perché nonè stato fatto new.

x=1 y=0 x=1 y=0 p1

p2

(33)

Assegnazioni tra riferimenti

• Attraverso p2 possiamo cambiare lo stato dell’oggetto:

p2.translate(3,1);

System.out.println(p1.getX());

System.out.println(p1.getY());

// nuove coordinate x=6, y=3

• I due riferimenti possono entrambi agire sull’oggetto e modificarlo.

Assegnazioni tra variabili di tipo base

• Per le variabili dei tipi base, invece, si hanno due diverse aree di memoria, ciascuna con un suo valore:

int a=10;

int b;

10

b a

Assegnazioni tra riferimenti

• Consideriamo le seguenti assegnazioni:

b=a;

a=7;

• Le variabili a e b sono diverse: a=7 e b=10.

• Anche p1 e p2 sono due variabili diverse, ma l’oggetto che “vedono” è unico.

10 b 7 a

Conversioni e

approssimazioni

(34)

Conversioni e approssimazioni

• In Java si può assegnare ad una variabile double un valore intero, mentre non è possibile il viceversa, e neppure assegnare un valore long ad una variabile di tipo int (anche se il valore è valido per int):

int a = 10; double b = 15;

float c; long x = 40;

a = b; //ERRORE

c = b; //ERRORE

a = x; //ERRORE

Conversioni e approssimazioni

• Ma se fosse necessario?

• Per convertire un tipo “superiore” ad uno

“inferiore” si esegue una esplicita istruzione che garantisce al compilatore che la eventuale perdita di precisione è voluta: si effettua cioè una conversione forzata (cast).

• Sintassi.

(nometipo) espressione (par. 4.1)

Conversioni e approssimazioni

• Esempio.

a = (int) b;

c = (float) b;

a = (int) x;

• Nel caso di a e b, se b avesse dei decimali il valore di b sarebbe troncato:

b = 5.8;

a = (int) b; //a vale 5

Conversioni e approssimazioni

• Se invece vogliamo un arrotondamento (a deve valere 6) usiamo il metodo round della classe Math

x = Math.round(b); //x è long

• Il metodo round restituisce un valore di tipo long e quindi è necessario un cast per assegnarlo ad a:

a = (int) Math.round(b);

(35)

Conversioni e approssimazioni

• Il metodo round è un metodo statico e per invocarlosi utilizza il nome della classe:

Math.round(argomento)

• Altri metodi della classe:

sqrt, sin, cos, abs, exp, log,…

(par. 4.4 Tabella 2)

• La classe Math appartiene a java.lang e quindi è automaticamente caricata.

Conversioni e approssimazioni

• Se i numeri di tipo long o double non rappresentano bene i calcoli che si vogliono eseguire, si possono utilizzare le classi BigInteger e BigDecimal ed eseguire le operazioni con gli oggetti di quelle classi:

(Argomenti av. 4.1) Esempio.

double d1 = 4.35*100;

System.out.println("d1 = " + d1);

// d1 = 434.99999999999994

Conversioni e approssimazioni

• I numeri reali del calcolatore sono approssimati;

alcuni numeri con rappresentazione decimale finita hanno rappresentazione binaria periodica.

BigDecimal x =

new BigDecimal("4.35");

BigDecimal y =

new BigDecimal("100");

BigDecimal z = x.multiply(y);

/* si deve usare un metodo per fare il prodotto */

System.out.println("z =" + z);

// z=435.00

Conversioni e approssimazioni

• Le classi BigInteger e BigDecimal appartengono al pacchetto java.math e pertanto devono essere importate:

import java.math.BigDecimal;

import java.math.BigInteger;

• Si potrebbe anche scrivere:

java.math.BigDecimal x = new

java.math.BigDecimal("4.35");

ma è uno stile poco usato (solitamente si utilizza importe si scrive solo la classe).

(36)

Costanti

L’uso delle costanti

• Questo programma effettua un cambio di valuta

• Un utente potrebbe chiedersi quale sia il significato del numero 0.71 usato nel programma, ma anche se questo valore non varierà nel tempo.

public class Convertimonete {

public static void main(String[] arg){

double dollaro = 2.35;

double euro = dollaro * 0.71;

} }

L’uso delle costanti

• Così come si usano nomi simbolici descrittivi per le variabili, è opportuno assegnare nomi simbolici anche alle costanti utilizzate nei

programmi. (par. 4.2)

• Si hanno dei vantaggi:

1. si aumenta la leggibilità

2. si evitano molte sostituzioni, se il valore della costante deve cambiare

3. non lo si confonde con altri valori.

L’uso delle costanti

1. Aumenta la leggibilità: chiunque usi il nostro programma capisce che 0.71 è il fattore di conversione

public class Convertimonete1 {

public static void main(String[] arg){

final double EURO_DOLLARO = 0.71;

double dollaro = 2.35;

double euro = dollaro*EURO_DOLLARO;

} }

(37)

L’uso delle costanti

2. Si evitano molte sostituzioni: il valore può cambiare e lo dobbiamo cambiare in ogni istruzione in cui viene usato:

public class Convertimonete2{

public static void main(String[] arg){

final double EURO_DOLLARO = 0.90;

double dollaro1 = 2.35;

double euro1 = dollaro1*EURO_DOLLARO;

double dollaro2 = 3.45;

double euro2 = dollaro2*EURO_DOLLARO;

} }

L’uso delle costanti

3. Si può confondere con altri valori:

public class Convertimonete3{

public static void main(String[] arg){

final double EURO_DOLLARO = 0.75;

double dollaro1 = 2.35;

double euro1 = dollaro1*EURO_DOLLARO;

double costocaffe = 0.90;

} }

Definizione di costante

• Sintassi.

final nometipo NOME_COSTANTE = espressione;

• NOME_COSTANTE è il nome che vogliamo dare alla costante

• In Java si usa la seguente convenzione: dare alle costanti un nome usando tutte le lettere maiuscole (es. MAX_VALUE nei tipi numerici)

• I nomi composti si ottengono attaccando le parole successive alla prima con un carattere di sottolineatura.

Definizione di costante

• Le costanti sono identificate dalla parola chiave final.

• In fase di definizione si deve specificare il tipo della costante ed attribuire un valore (espressione): tale valore non può più essere modificato.

• Il tentativo di modificare il valore della costante, dopo l’inizializzazione, produce un errore(semantico) durante la compilazione.

(38)

Struttura iterativa

Struttura iterativa

• Sia P un predicato (condizione); nel caso in cui P sia vero vogliamo eseguire ripetutamente un gruppo di <istruzioni>, che non dovrà più essere eseguito nel caso in cui P sia falso.

fintantoché P eseguire

<istruzioni> //P vero //fine ciclo

// dopo la fine della struttura P è falso

Struttura iterativa

• Problema. Stampare i primi cinque numeri naturali in ordine decrescente.

• Analisi.

Dobbiamo ripetere la stampa di un numero, che varia in un intervallo di valori:

1. individuare il predicato P

fino a quando si prosegue

2. individuare le istruzioni da ripetere

• cosa dobbiamo fare

Struttura iterativa

• Quali sono i numeri da stampare?

• 1, 2, 3, 4, 5 ma nell’ordine inverso:

5, 4, 3, 2, 1

• Possiamo pensare di partire dal più grande (5) e ad ogni iterazione calare di una unità, quindi l’iterazione sarà:

stampa numero esegui numero-1

• Quando ci fermiamo? Lo 0 non è compreso, quindi fintantoché numero è maggiore di zero, ripetiamo le istruzioni.

(39)

Struttura iterativa

• Progetto.

algoritmo stamparovescia variabili a intero a ←← 5

finché a > 0 eseguire stampa a a ← a-1 //fine struttura

• Questa struttura termina?

Struttura iterativa

• Scriviamo i valori che la variabile a assume durante il funzionamento dell’algoritmo:

valore predicato stampa

a > 0

a = 5 V 5

a = 4 V 4

a = 3 V 3

a = 2 V 2

a = 1 V 1

a = 0 F il ciclo termina

Struttura iterativa

• Osserviamo che il ciclo è stato eseguito 5 volte e che il predicato è stato valutato 6 volte:

5 vero + 1 falso

• Qual è il comportamento di un ciclo “finché”?

• Tale ciclo:

• può non essere mai eseguito

• può essere eseguito una o più volte

• deve terminare.

Struttura iterativa

• Si deve fare molta attenzione quando si scrivono i cicli, perché si possono introdurre gravi errori.

• Cosa cambia se iniziamo con a = -5 ?

• Il predicato a > 0 è falso e quindi il ciclo non viene mai eseguito.

• Se quello che volevamo era la stampa dei numeri, questo è un errore logico: il compilatore non segnalerà errore ma il programma non eseguirà le operazioni richieste.

Riferimenti

Documenti correlati

Se i valori aumentati sono tali per cui nel problema originale della partizione l’unica soluzione possibile ` e una con due insiemi di uguale cardinalit` a, la trasformazione

A sua volta il personale dell’aggiudicatario deve seguire le indicazioni anche verbali del personale dell’Amministrazione giudiziaria addetto.. DA COMPILARE DA PARTE

Nella risoluzione degli esercizi ` e possibile utilizzare tutti i risultati enunciati a lezione, anche quelli non dimostrati.

Per il problema dell’esercizio 2, M AXCU T sia dia il codice di un algoritmo che individua la soluzione ottima facendo tutte le

Essi sono essenzialmente funzione dell area A, del perimetro P e della lunghezza dell asta principale L..

Se M è una matrice numerica quadrata nxn (quindi con eguale numero di righe e di colonne), possiamo calcolare il prodotto righe per colonne MxM ottenendo ancora una matrice

Dunque possiamo implementare una versione molto più efficiente dell’algoritmo facendo variare semplicemente

[r]