Metodi e programmazione procedurale

Transcript

Metodi e programmazione procedurale
Metodi
Rosario Pugliese
[email protected]
Università di Firenze
Metodi – p.1/40
Sotto-programmi
Necessità di scomporre problemi/programmi complessi.
Sotto-programma: blocco di istruzioni che svolgono un
compito specifico, raggruppate insieme come un’unica entità
a cui viene assegnato un nome.
Il nome può successivamente essere utilizzato come
sostituto dell’intero blocco di istruzioni (detto corpo).
Vantaggi:
Risparmio di un inutile lavoro di scrittura
(uno stesso blocco di istruzioni compare più volte).
Migliore organizzazione del pensiero/programma.
Modularità (un sotto-programma è come una scatola
nera, i dettagli implementativi possono essere ignorati).
Metodi – p.2/40
Metodi e loro dichiarazione
Nella terminologia Java, un sotto-programma è detto
metodo.
Due tipi di metodi:
Funzione: restituisce un singolo valore.
tipo Output nome Metodo ( lista Parametri )
blocco
Procedura: esegue un’azione senza restituire un valore.
void nome Metodo ( lista Parametri )
blocco
Definizione: specifica l’interfaccia input/output ed il nome
del metodo.
Invocazione: nome del metodo seguito da elenco dei dati in
input (separati da virgole e racchiusi tra parentesi tonde).
Metodi – p.3/40
Funzioni ed istruzione di ritorno
Una funzione deve specificare il valore che restituisce.
Istruzione return: seguita da una espressione il cui tipo
coincide con quello specificato nella dichiarazione della
funzione.
Metodi – p.4/40
Numero di Nepero
e = 1 + 11 +
1
1·2
+
1
1·2·3
+
1
1·2·3·4
+ ···
Fatto: la somma dei primi n + 1 termini della sommatoria a
destra di = approssima e entro un fattore additivo pari a
1
2
(1
+
1·2···n·(n+1)
n+2 ).
Metodi – p.5/40
Istruzione return e flusso di esecuzione
Un’istruzione return deve essere inclusa in ogni possibile
cammino di esecuzione.
Non sempre basta includere un’istruzione return.
Il compilare segnala il fatto che potrebbe esistere un
cammino nel diagramma di flusso del metodo che non
termina con un’istruzione return.
Metodi – p.6/40
Procedure
Non devono necessariamente includere un’istruzione
return.
Convenzione per i nomi dei metodi: verbi per procedure e
nomi (con eventuali aggettivi) per funzioni.
Metodi – p.7/40
Parametri formali ed argomenti
Parametro formale: usato nella definizione di un metodo
come un segnaposto per un valore che sarà inserito al
momento in cui il metodo sarà invocato.
Parametro attuale o Argomento: il valore inserito al
momento dell’invocazione.
Numero e tipo dei parametri formali e degli argomenti
devono coincidere.
Passaggio per valore: l’argomento viene prima valutato ed il
valore restituito è usato per inizializzare il parametro formale.
Se l’argomento è una variabile, il valore (e non il nome)
della variabile viene inserito nel parametro formale.
Nel caso di tipi di dato primitivi, questo è l’unico
meccanismo di sostituzione; per array e classi viene usato
un meccanismo diverso.
Metodi – p.8/40
Massimo tra tre numeri
Metodi – p.9/40
Estrazione di numeri casuali
Metodo congruenziale lineare: si basa sulla relazione di ricorrenza
xn+1 = p1 · xn + p2 (modn)
dove p1 , p2 ed n sono costanti opportune.
Il valore iniziale x0 è detto seme della generazione.
Java fornisce un proprio generatore di numeri casuali con proprietà
statistiche eccellenti.
Metodi – p.10/40
Variabili locali
Dichiarate all’interno di un blocco.
Se il blocco è il corpo di un metodo, si parla di variabili
locali del metodo.
Quando l’esecuzione del blocco termina, tutte le variabili
dichiarate al suo interno spariscono.
Non visibili all’esterno di un blocco.
Messaggio di errore
cannot find symbol
symbol : variable numero
Metodi – p.11/40
Variabili locali
Cosa succede se si aggiunge la dichiarazione
int numero = 1;
prima del blocco?
Messaggio di errore
numero is already defined
Metodi – p.12/40
Ambito di visibilità di una variabile
Zona di programma in cui ci si può riferire alla variabile.
Si estende dal punto della sua dichiarazione fino alla fine del
blocco che la contiene.
Non è possibile avere due variabili locali con nomi identici
ed ambiti di visibilità sovrapposti, a meno che una non sia
dichiarata nel corpo di un metodo
Metodi – p.13/40
Variabili locali e metodi
Metodi – p.14/40
Metodi ed array
Metodi – p.15/40
Confronto di array
Abbiamo già visto che per controllare se due array sono uguali
non è sufficente usare direttamente l’operatore di uguaglianza ==.
Metodi – p.16/40
Array come parametri formali
Quando si specifica un parametro di tipo array, viene
specificato il tipo degli elementi, ma non il loro numero.
La lunghezza dell’array passato come argomento ad un
metodo, può essere diversa ad ogni invocazione dello
stesso metodo (vedi esempio precedente).
La variabile length può essere usata per evitare errori
dovuti ad indici fuori dominio.
Un metodo può cambiare i valori degli elementi di un array
passato come argomento.
Il nome di un array, infatti, identifica una locazione di
memoria in cui è conservato l’indirizzo della locazione di
memoria del primo elemento dell’array.
Metodi – p.17/40
Array come parametri formali
Metodi – p.18/40
Modifica di un argomento di tipo array
Metodi – p.19/40
Ordinamento di array
Compito molto frequente.
Selection sort: algoritmo semplice, non efficiente ma facile
da capire.
Cerca nell’array l’elemento con il valore più piccolo e lo
scambia con il primo elemento.
Cerca tra gli elementi dell’array, escluso il primo,
l’elemento con il valore più piccolo e lo scambia con il
secondo elemento.
Ripete la ricerca e lo scambio fino a che tutti gli elementi
sono al posto giusto.
Definiremo un metodo che implementa tale algoritmo nel
caso di array di int (facilmente adattabile ad altri tipi di dato
su cui sia definita una relazione d’ordine).
Metodi – p.20/40
Esempio di selection sort
Metodi – p.21/40
Implementazione del selection sort
Metodi – p.22/40
Programmazione procedurale
Pratica di scomporre un programma in sotto-programmi o
procedure.
Basata su divide et impera: scomporre il problema da
analizzare in sotto-problemi più semplici da risolvere, fino a
raggiungere sotto-problemi elementari.
Metodi – p.23/40
Struttura del programma
Ad ogni sotto-problema corrisponde una procedura
progettata appositamente o predefinita in una libreria.
Procedure autonome ed indipendenti tra di loro (per favorire
il riuso), ma che si possono richiamare vicendevolmente.
Se A usa B, A non può essere eseguita fino a quando B non sia
implementata, ma può essere implementata in parallelo.
Programma = procedura principale + procedure asservite
Metodi – p.24/40
Integrale di una funzione y = f (x)
Approssimato considerando i punti xi = a + i · h per 0 ≤ i ≤ n,
dove h = (b − a)/n, e sommando, per ogni i con 0 ≤ i < n, le
aree di tutti i rettangoli che hanno per base la lunghezza del
segmento compreso tra xi ed xi+1 (ovvero, h) e per altezza il
valore della funzione nel punto xi (ovvero, f (xi )).
Metodi – p.25/40
Calcolo di π
Banco di prova: π uguale all’integrale di f (x) =
nell’intervallo compreso tra 0 ed 1.
4
1+x2
Metodi – p.26/40
Pre-condizioni e post-condizioni
È facile verificare che la realizzazione di una procedura (es.
tramite un metodo Java) è conforme alla specifica della sua
interfaccia; più difficile è verificare la correttezza
dell’implementazione.
Pre-condizione: requisito che deve essere soddisfatto da chi
invoca un metodo. Se viene violata, il metodo non è
responsabile per il risultato non corretto.
Post-condizione: garanzia fornita dal metodo.
Il valore restituito dal metodo è calcolato correttamente.
Il sistema si troverà in un determinato stato dopo che
l’esecuzione del metodo è terminata.
Pre-condizioni e post-condizioni sono termini contrattuali tra un
metodo e chi lo invoca.
Metodi – p.27/40
Un programma strutturato
Stampare tutti i fattori primi (con eventuali ripetizioni) di un
numero intero dato in input e compreso tra 2 e 1000.
Le tre procedure asservite
inputNumero: richiede all’utente un numero intero
compreso in un determinato intervallo.
numeroPrimo: stabilisce se l’argomento è primo.
fattorePrimo: restituisce il più piccolo fattore primo
dell’argomento.
Metodi – p.28/40
La procedura principale
Metodi – p.29/40
Le procedure asservite
Metodi – p.30/40
Ricorsione
Spesso risulta naturale progettare una procedura che invoca
se stessa su dei sotto-casi (es. ordinamento di array).
Direttamente ricorsiva: contiene un’invocazione di se
stessa.
Indirettamente ricorsiva: contiene un’invocazione di
un’altra procedura, la quale la invoca direttamente o
indirettamente.
Caso base: istruzione che provoca la terminazione.
Vantaggi: formulazione e dimostrazione di correttezza degli
algoritmi ricorsivi sono generalmente semplici.
Svantaggi: ogni chiamata ricorsiva richiede la
memorizzazione di variabili locali e dello stato attuale della
computazione (ricorsione profonda richiede notevoli risorse
di memoria e di tempo).
Metodi – p.31/40
Ricorsione indiretta
Metodi – p.32/40
Metodi ricorsivi e numerologia
Numero del destino: somma le cifre della data di nascita e si
ripete fino ad ottenere una sola cifra.
Metodi – p.33/40
Schemi di ricorsione
Siano S un insieme di istruzioni e P una qualunque
combinazione delle istruzioni indicate tra parentesi quadre.
Condizione di terminazione come prima istruzione:
A = if(E){P [S, A]}.
Condizione di terminazione dopo un certo numero di
istruzioni:
A = P [S, if(E){A}].
Schemi falsamente ricorsivi:
A = if(E){S; A}
e
A = S; if(E){A}.
Sostituibili con i seguenti schemi iterativi:
A = while(E){S}
e
A = do{S}while(E);
Metodi – p.34/40
Numeri di Fibonacci


Fn = 
n
se n = 0 oppure n = 1,
Fn−1 + Fn−2 altrimenti.
Metodi – p.35/40
Calcolo ricorsivo di F5
Schema delle invocazioni del metodo ricorsivo per il calcolo
del numero di Fibonacci di indice 5 (sesto numero).
Inconveniente: calcolo dello stesso numero di Fibonacci
eseguito più di una volta.
Numero di invocazioni (e tempo d’esecuzione) cresce
esponenzialmente rispetto all’indice del numero da calcolare.
Metodi – p.36/40
Calcolo iterativo dei numeri di Fibonacci
Il calcolo del numero di Fibonacci di indice n richiede la
memorizzazione solo dei numeri con indici n − 1 e n − 2.
Tempo d’esecuzione lineare (per calcolare il numero di
indice n occorrono n iterazioni).
Metodi – p.37/40
Ricorsione & Iterazione
Ogni definizione di metodo che coivolge chiamate ricorsive
può essere riscritta in modo da realizzare lo stesso compito
senza usare ricorsione.
La versione ricorsiva è generalmente meno efficente.
La ricorsione andrebbe evitata quando esiste una versione
iterativa ovvia ed andrebbe usata solo in quei casi
intrinsecamente ricorsivi in cui l’uso della ricorsione rende
più chiara la lettura del programma.
Metodi – p.38/40
Torri di Hanoi
Tre torri (numerate 1, 2, 3) ed n dischi diversi, inizialmente
posti nella torre s in ordine decrescente di grandezza.
Portare i dischi nell torre d, rispettando le regole seguenti:
Nessun disco si trova mai sopra uno più piccolo.
Si può spostare solo un disco alla volta.
Il disco spostato da una torre è sempre quello in cima.
I dischi sono sempre collocati su una torre.
Soluzione ricorsiva
Sposta (ricorsivamente) n − 1 dischi da s a 6 − s − d.
Sposta un disco da s a d.
Sposta (ricorsivamente) n − 1 dischi da 6 − s − d a d.
Metodi – p.39/40
Soluzione ricorsiva delle torri di Hanoi
Nel caso di 3 dischi dalla torre 1 alla 3:
Sposta
Sposta
Sposta
Sposta
Sposta
Sposta
Sposta
disco
disco
disco
disco
disco
disco
disco
da
da
da
da
da
da
da
1
1
3
1
2
2
1
a
a
a
a
a
a
a
3
2
2
3
1
3
3
Metodi – p.40/40