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