Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Programmazione strutturata e metodi, Appunti di Elementi di Informatica

Corso di Laboratorio di Matematica e informatica del prof. Fabio Sartori. Programmazione strutturata. Metodi.

Tipologia: Appunti

2021/2022

In vendita dal 05/06/2023

rossimartaa
rossimartaa 🇮🇹

4.3

(18)

156 documenti

1 / 10

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
PROGRAMMAZIONE STRUTTURATA
I METODI
Fino ad ora abbiamo sempre considerato i programmi come una sequenza di istruzioni senza una
struttura ben precisa. L’unica chiara limitazione che abbiamo dato è il blocco, delimitato da una
coppia di parentesi graffe; esso ci consente di andare in profondità (blocco dentro a un altro blocco),
ma non permette di espandersi in larghezza.
problema!
ad esempio questo può non andare bene per il concetto di visibilità della variabile, non ci
permette di considerare programmi allo stesso livello
Come possiamo quindi aumentare la potenza espressiva di un programma?
Introduciamo un metodo sintattico che ci permette di dividere un programma in sottoprogrammi
Esempio
- calcolo dell’area di un quadrato
va bene quanto fatto fin’ ora: voglio risolvere un problema 1 algoritmo 1
programma
creo un programma in cui dichiaro la variabile int lato e inserisco l’istruzione di calcolo
dell’area del quadrato come latoxlato
- calcolo dell’area di un quadrilatero
il programma creato prima non è più sufficiente, non permette di calcolare l’area di tutti i
quadrilateri: devo riuscire a uniformare il programma di modo che permetta di calcolare
l’area di tutti i quadrilateri.
- innanzitutto dichiaro 3 variabili: lato maggiore, lato minore, altezza
- inserisco un’istruzione di tipo Scanner con cui chiedo all’utente di quale
quadrilatero calcolare l’area
- l’algoritmo sarà un enorme if
una rappresentazione di questo tipo ha diversi problemi. Se voglio calcolare l’area del
rombo chiamerei lato maggiore e minore le due diagonali: attribuiamo semanticamente
un nome sbagliato a due entità completamente diverse, mentre il nostro obiettivo
dev'essere quello di scrivere programmi che rappresentano porzioni di realtà
errore logico (concettuale di rappresentazione)
Se pensassi di risolvere questo problema dichiarando le variabili direttamente nel blocco
dell’if, creo un altro problema “bloccando” le variabili all’interno di esso
Qual è quindi la soluzione? STRUTTURARE IL PROGRAMMA
Il programma viene diviso in tanti blocchi funzionali (sottoprogrammi), che stanno tutti sullo stesso
livello, ognuno dei quali è dedicato a una specifica funzione. Il main fa da collettore, invoca un certo
sottoprogramma quando ne abbiamo bisogno.
Ogni linguaggio di programmazione ha un diverso costrutto per dividere il programma in
sottoprogrammi; Java usa i metodi.
pf3
pf4
pf5
pf8
pf9
pfa

Anteprima parziale del testo

Scarica Programmazione strutturata e metodi e più Appunti in PDF di Elementi di Informatica solo su Docsity!

PROGRAMMAZIONE STRUTTURATA

I METODI

Fino ad ora abbiamo sempre considerato i programmi come una sequenza di istruzioni senza una struttura ben precisa. L’unica chiara limitazione che abbiamo dato è il blocco, delimitato da una coppia di parentesi graffe; esso ci consente di andare in profondità (blocco dentro a un altro blocco), ma non permette di espandersi in larghezza. → problema! ad esempio questo può non andare bene per il concetto di visibilità della variabile, non ci permette di considerare programmi allo stesso livello… Come possiamo quindi aumentare la potenza espressiva di un programma? Introduciamo un metodo sintattico che ci permette di dividere un programma in sottoprogrammi Esempio

- calcolo dell’area di un quadrato va bene quanto fatto fin’ ora: voglio risolvere un problema → 1 algoritmo → 1 programma creo un programma in cui dichiaro la variabile int lato e inserisco l’istruzione di calcolo dell’area del quadrato come latoxlato - calcolo dell’area di un quadrilatero il programma creato prima non è più sufficiente, non permette di calcolare l’area di tutti i quadrilateri: devo riuscire a uniformare il programma di modo che permetta di calcolare l’area di tutti i quadrilateri. - innanzitutto dichiaro 3 variabili: lato maggiore, lato minore, altezza - inserisco un’istruzione di tipo Scanner con cui chiedo all’utente di quale quadrilatero calcolare l’area - l’algoritmo sarà un enorme if una rappresentazione di questo tipo ha diversi problemi. Se voglio calcolare l’area del rombo chiamerei lato maggiore e minore le due diagonali: attribuiamo semanticamente un nome sbagliato a due entità completamente diverse, mentre il nostro obiettivo dev'essere quello di scrivere programmi che rappresentano porzioni di realtà → errore logico (concettuale di rappresentazione) Se pensassi di risolvere questo problema dichiarando le variabili direttamente nel blocco dell’if, creo un altro problema “bloccando” le variabili all’interno di esso Qual è quindi la soluzione? STRUTTURARE IL PROGRAMMA Il programma viene diviso in tanti blocchi funzionali (sottoprogrammi), che stanno tutti sullo stesso livello, ognuno dei quali è dedicato a una specifica funzione. Il main fa da collettore, invoca un certo sottoprogramma quando ne abbiamo bisogno. Ogni linguaggio di programmazione ha un diverso costrutto per dividere il programma in sottoprogrammi; Java usa i metodi.

METODI

I metodi sono blocchi di istruzioni, sempre racchiusi tra parentesi graffe {} che svolgono una funzione che si vuole poter richiamare all’interno di un programma, senza riscriverli di volta in volta. Abbiamo già usato invocazioni o chiamate a metodi diverse volte:

  • System.out.print(…) / System.out.println(…)
  • Il metodo main: public static void main(String[] arg) esso è un blocco di istruzioni che ha la funzione di indicare il punto di partenza del programma e fare da collettore tra tutti gli altri metodi. I metodi sono codificati all’interno della classe a cui appartengono (sono infatti Sottoprogrammi di un programma dato), è possibile definire da zero a un numero elevato di metodi all’interno di una classe e l’ordine in cui sono definiti è ininfluente, l’unica cosa che conta è non inserire un metodo all’interno di un altro metodo. Definire un metodo comporta la definizione di
  • intestazione (o signature ): ci permette di invocare successivamente il metodo
  • body : definisce il comportamento del metodo, ossia la sequenza di istruzioni che devono essere eseguite. Esistono 2 tipi di metodi:
  • quelli che eseguono esclusivamente istruzioni, senza avere una forma di comunicazione con il blocco chiamante (ad esempio printline)
  • quelli che eseguono istruzioni e restituiscono un valore che verrà letto/utilizzato dal blocco chiamante Questo secondo tipo di blocchi presuppone quindi comunicazione tra il metodo che esegue un servizio e chi lo invoca, il valore che viene restituito deve essere “catturato” da chi invoca il servizio (ad esempio questo accade per il metodo classe Scanner.nextint che restituisce il valore letto dalla tastiera associato alla variabile)

BODY

Il corpo o body del metodo contiene la sequenza di istruzioni che deve essere eseguita nel momento in cui il metodo viene invocato. Si tratta quindi di un blocco che è racchiuso tra parentesi graffe {} e segue la definizione della signature o intestazione del metodo stesso. Se il metodo restituisce un valore, l’ultima istruzione che viene eseguita deve essere return valore, dove il valore scritto deve essere compatibile con il valore indicato nella signature. Si noti che return è un’istruzione bloccante (come il break): quando la inserisco, il metodo si interrompe, per questo va messa un’unica istruzione di return e alla fine, se fosse messa a metà del blocco, bloccherebbe tutta l’istruzione. Inoltre si ricordi che il return consente di ritornare un solo valore → limitazione dal punto di vista espressivo Se il metodo non restituisce valore non ho l’obbligo di inserire la keyword return. Un caso particolare si ha quando in un metodo che non restituisce valore nel void è presente return; o return ; → in questo caso all’istruzione return è stato assegnato un valore vuoto che è compatibile con void. non si tratta quindi di un errore di compilazione, il programma funziona comunque (NB per quiz) INVOCAZIONE L’intestazione di un metodo può essere usata per richiamare, attraverso l’operazione di “call” o “invocazione” il suo codice in uno o più punti del programma. Quando ciò accade l’esecuzione dell’invocante si ferma, esso passa in stato di attesa, mentre il controllo passa al metodo. Iniziano così ad essere eseguite le istruzioni del metodo invocato, che non è altro che un sottoprogramma, quindi quando il metodo invocato termina (per via di un return o per la fine delle istruzioni associate), il metodo invocante riprende l’esecuzione dal punto dopo che aveva invocato il metodo ; viene prima eventualmente assegnato il valore ritornato dal metodo. La definizione del metodo invocato può essere nella stessa classe dell’invocante o in un’altra → creazione di classi di libreria di metodi che invoco quando mi servono METODI STATICI Si tratta di metodi che non richiedono un oggetto per essere utilizzati e sono quelli che useremo maggiormente. Sono identificati dalla parola chiave static. Ho due possibilità circa dove scrivere il codice che implementa un metodo

  • Nella stessa classe in cui definisco il main
  • In una classe separata (ciò che faremo tipicamente)

Un metodo statico si invoca nel seguente modo: NomeClasse.nomeMetodo NB: se il metodo è definito nella stessa classe in cui viene invocato, il nome della classe puo' essere omesso. PARAMETRI ATTUALI Nel passaggio dei parametri (invocazione) sostituisco ai parametri formali i cosiddetti parametri attuali, dove la parola attuale va intesa nel significato del termine inglese actual, ossia concreto. Nei paramenti formali ho la dichiarazione delle variabili, quando invoco un metodo passo dei valori concreti, veri. Gli argomenti possono essere:

  • Costanti Es: System.out.print(“Inserire un numero intero maggiore di 0:”)
  • Variabili contenenti valori Es: System.out.println("Il massimo e' " + max); Essi vengono restituiti nell’esatto ordine con cui è stata fatta la dichiarazione del metodo: se ho dichiarato int, poi float e infine double li devo passare nell’invocazione in questo esatto ordine, altrimenti ho un errore di compilazione. Questo passaggio è molto delicato: vengono definiti i valori effettivi su cui il body andrà a lavorare durante le operazioni, si viene proprio a stabilire il contatto tra chiamante e chiamato. Il passaggio dei parametri attuali inoltre avviene per copia, quindi viene effettuata una copia dei valori passati.

Esempio : voglio calcolare il massimo tra due valori

Dove si dichiara il codice del metodo? Esso può essere scritto dove vogliamo all’interno della definizione di classe, l’importante è che non venga definito all’interno di un altro metodo. Ovviamente non può essere dichiarato all’esterno della classe. RECORD DI ATTIVAZIONE Cosa succede in memoria durante l’invocazione? In generale sappiamo che ogni programma per essere eseguito deve risiedere in memoria, e questo vale quindi anche per i metodi, che sono sottoprogrammi: perché l’invocazione abbia effetto deve essere caricata in memoria un’immagine dello stato del sottoprogramma, dove indicato tutto ciò che gli serve per funzionare. Se pensiamo quindi all’esempio del calcolo del massimo, all’inizio in memoria ho solo il main. Quando si passa a “calcola massimo” il main si blocca, viene creata un’area con tutte le informazioni che servono a “calcola massimo” per funzionare; quando esso ha terminato, l’area di memoria viene eliminata e il controllo torna a chi lo aveva invocato. → tecnica A PILA: il primo metodo ad andar via è l’ultimo ad essere stato invocato.

In prima approssimazione possiamo quindi dire che quando un programma viene compilato, il compilatore Java crea un’area di memoria dedicata, detta record di attivazione, per ogni metodo M dichiarato nel programma. Tale record di attivazione ha ampiezza sufficiente a memorizzare dati indispensabili durante l'esecuzione del metodo, quali, ad esempio, le variabili locali di M (cioè le variabili dichiarate nel corpo di M), i parametri, ed il risultato, se non è di tipo void. Quando, durante l'esecuzione del programma, il metodo M viene chiamato, una copia R del record di attivazione di M viene inserita nella pila dei record di attivazione (in cima alla pila). Lo spazio di R destinato a memorizzare i parametri viene immediatamente aggiornato inserendo il valore dei parametri che sono stati passati da chi ha chiamato il metodo. Il valore delle variabili locali che vengono usate dal metodo sarà memorizzato nell'apposito spazio di R riservato alle variabili. L'eventuale risultato sarà anch'esso memorizzato nell'apposito spazio riservato in R. Quando il metodo termina, R viene rimosso dalla pila dei record di attivazione; se il metodo ritorna un valore, nel record di attivazione del chiamante viene copiato il valore di ritorno. Il programma terminerà quando non avrò più record di attivazione, e l’ultimo ad essere eliminato sarà quello del main (che come detto prima ha una funzione di controllo, definisce la sequenza con cui i sottoprogrammi sono invocati). Il record di attivazione ha ampiezza sufficiente a memorizzare dati indispensabili durante le esecuzioni del metodo, quali:

  • le variabili locali di M, cioè le variabili dichiarate nel corpo di M
  • i parametri di M
  • il risultato di M, se M non ha risultato di tipo void
  • l'indirizzo di ritorno (Address Return) al metodo che ha chiamato M, se M non è il metodo main. Questo indirizzo dovrà contenere l'indirizzo di memoria dell'istruzione del metodo che ha chiamato M che dovrà essere eseguita non appena M terminerà. (l’indirizzo di ritorno è l’unica parte che ha riferimento al record di attivazione chiamante, tutto il resto rimane confinato nel record di attivazione del metodo. Quindi quando tolgo R del metodo appena eseguito scrivo in R chiamante il valore ritornato e lascio posto all’ultimo R rimasto sulla pila)
  • il puntatore al record di attivazione del metodo che ha chiamato M, se M non è il metodo main. Questo puntatore serve per creare un collegamento tra i record di attivazione. I collegamenti consentono di strutturare i record di attivazione in una pila Note : Le variabili locali e i parametri risiedono in zone diverse all’interno del record di attivazione. Tutto il passaggio di valori avviene per copia: no relazione tra i parametri attuali che vengono passati tra chiamante e chiamato e il valore che viene ritornato → per questo alla fine le posso eliminare senza problemi, non ho perdita di informazione. Il meccanismo avviene per ogni metodo! Ogni metodo ha il suo record di attivazione che viene caricato sulla memoria al momento dell’invocazione.

Variabili locali:

  • possono essere dichiarate ovunque all’interno di un metodo
  • devono comunque essere dichiarate almeno nell’istruzione prima del loro utilizzo
  • Sono accessibili da qualunque istruzione all’interno del blocco (le graffe identificano blocchi di codice) in cui sono dichiarate Due variabili dichiarate con lo stesso scope non possono avere lo stesso nome METODI E SCOPING Un metodo definisce, ovviamente, un blocco di istruzioni. Tale blocco, essendo dichiarato al di fuori del main, crea uno spazio di visibilità delle variabili nuovo. Nell’esempio MaxSeq, i parametri formali valore1 e valore2 avevano lo stesso nome dei parametri attuali valore1 e valore2, ma appartenevano a record di attivazione differenti. Quindi quando il metodo viene chiamato nel main, cosa succede? Non ho errore. Appartengono a due spazi di visibilità diversi che sono paralleli, allo stesso livello, ma disgiunti, quindi possiamo dare loro lo stesso nome. (NB per i quiz: no errore di compilazione)