Funzioni e procedure



Documenti analoghi
Funzioni in C. Violetta Lonati

Concetto di Funzione e Procedura METODI in Java

Corso di Informatica

Introduzione alla programmazione in C

Sommario. Definizione di informatica. Definizione di un calcolatore come esecutore. Gli algoritmi.

INFORMATICA 1 L. Mezzalira

AA LA RICORSIONE

Strutture. Strutture e Unioni. Definizione di strutture (2) Definizione di strutture (1)

Università degli Studi di Cassino Corso di Fondamenti di Informatica Puntatori. Anno Accademico 2010/2011 Francesco Tortorella

Algebra di Boole: Concetti di base. Fondamenti di Informatica - D. Talia - UNICAL 1. Fondamenti di Informatica

FONDAMENTI di INFORMATICA L. Mezzalira

Allocazione dinamica della memoria - riepilogo

12 - Introduzione alla Programmazione Orientata agli Oggetti (Object Oriented Programming OOP)

Università di Torino Facoltà di Scienze MFN Corso di Studi in Informatica. Programmazione I - corso B a.a prof.

Tipi primitivi. Ad esempio, il codice seguente dichiara una variabile di tipo intero, le assegna il valore 5 e stampa a schermo il suo contenuto:

Fondamenti di Informatica e Laboratorio T-AB T-16 Progetti su più file. Funzioni come parametro. Parametri del main

Lezione 8. La macchina universale

void funzioneprova() { int x=2; cout<<"dentro la funzione x="<<x<<endl; }

risulta (x) = 1 se x < 0.

GESTIONE INFORMATICA DEI DATI AZIENDALI

Una funzione è detta ricorsiva se chiama, direttamente o indirettamente, se stessa. In C tutte le funzioni possono essere usate ricorsivamente.

La struttura dati ad albero binario

La selezione binaria

Funzioni funzione dominio codominio legge argomento variabile indipendente variabile dipendente

Siamo così arrivati all aritmetica modulare, ma anche a individuare alcuni aspetti di come funziona l aritmetica del calcolatore come vedremo.

APPUNTI DI MATEMATICA LE FRAZIONI ALGEBRICHE ALESSANDRO BOCCONI

Oggetti Lezione 3. aspetti generali e definizione di classi I

3 - Variabili. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo


(Esercizi Tratti da Temi d esame degli ordinamenti precedenti)

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

LE FUNZIONI A DUE VARIABILI

Inizializzazione, Assegnamento e Distruzione di Classi

Esercizi su. Funzioni

Excel. A cura di Luigi Labonia. luigi.lab@libero.it

Arduino: Programmazione

LINGUAGGI DI PROGRAMMAZIONE

INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI

Gli algoritmi: definizioni e proprietà

Automatizzare i compiti ripetitivi. I file batch. File batch (1) File batch (2) Visualizzazione (2) Visualizzazione

Definizione di nuovi tipi in C

Algoritmo. I dati su cui opera un'istruzione sono forniti all'algoritmo dall'esterno oppure sono il risultato di istruzioni eseguite precedentemente.

A intervalli regolari ogni router manda la sua tabella a tutti i vicini, e riceve quelle dei vicini.

Workland CRM. Workland CRM Rel /11/2013. Attività --> FIX. Magazzino --> NEW. Nessuna --> FIX. Ordini --> FIX

RICORSIVITA. Vediamo come si programma la soluzione ricorsiva al problema precedente: Poniamo S 1 =1 S 2 =1+2 S 3 =1+2+3

Organizzazione degli archivi

Cosa è un foglio elettronico

MANUALE MOODLE STUDENTI. Accesso al Materiale Didattico

Proof. Dimostrazione per assurdo. Consideriamo l insieme complementare di P nell insieme

Soluzione dell esercizio del 2 Febbraio 2004

Gestione della memoria centrale

Fondamenti di Informatica Ingegneria Clinica Lezione 19/10/2009. Prof. Raffaele Nicolussi

Ricorsione. (da lucidi di Marco Benedetti)

Laboratorio di Programmazione 1. Docente: dr. Damiano Macedonio Lezione 18 31/03/2014

Strutturazione logica dei dati: i file

Dall Algoritmo al Programma. Prof. Francesco Accarino IIS Altiero Spinelli Sesto San Giovanni

I sistemi di numerazione

Testi di Esercizi e Quesiti 1

Analisi sensitività. Strumenti per il supporto alle decisioni nel processo di Valutazione d azienda

I Thread. I Thread. I due processi dovrebbero lavorare sullo stesso testo

Le variabili. Olga Scotti

f(x) = 1 x. Il dominio di questa funzione è il sottoinsieme proprio di R dato da

Università di Roma Tor Vergata Corso di Laurea triennale in Informatica Sistemi operativi e reti A.A Pietro Frasca.

Codifica: dal diagramma a blocchi al linguaggio C++

COS È UN LINGUAGGIO? LINGUAGGI DI ALTO LIVELLO LA NOZIONE DI LINGUAGGIO LINGUAGGIO & PROGRAMMA

13 - Gestione della Memoria nella Programmazione Orientata agli Oggetti

Matematica generale CTF

Traccia di soluzione dell esercizio del 25/1/2005

10 - Programmare con gli Array

Corso di Fondamenti di Informatica

Alcune regole di base per scrivere un programma in linguaggio C

Capitolo 2. Operazione di limite

Correttezza. Corso di Laurea Ingegneria Informatica Fondamenti di Informatica 1. Dispensa 10. A. Miola Novembre 2007

RIFERIMENTI ATTORI GLOSSARIO. ERRORI COMUNI REV. REQUISITI INGEGNERIA DEL SOFTWARE Università degli Studi di Padova

Sistemi Operativi MECCANISMI E POLITICHE DI PROTEZIONE. D. Talia - UNICAL. Sistemi Operativi 13.1

MECCANISMI E POLITICHE DI PROTEZIONE 13.1

Informatica. Rappresentazione dei numeri Numerazione binaria

Corrispondenze e funzioni

( x) ( x) 0. Equazioni irrazionali

MANUALE PARCELLA FACILE PLUS INDICE

Informatica 3. Informatica 3. LEZIONE 10: Introduzione agli algoritmi e alle strutture dati. Lezione 10 - Modulo 1. Importanza delle strutture dati

Corso di Laurea Ingegneria Informatica Fondamenti di Informatica

Matematica - SMID : Programmazione Febbraio 2009 FOGLIO RISPOSTE

Introduzione a ROOT. 1. Informazioni generali

La ricorsione. Politecnico di Milano Sede di Cremona

Linguaggi e Paradigmi di Programmazione

Struttura di un programma Java

Uso di base delle funzioni in Microsoft Excel

Laboratorio di Informatica

RISOLUTORE AUTOMATICO PER SUDOKU

Probabilità discreta

SISTEMI DI NUMERAZIONE E CODICI

APPUNTI DI MATEMATICA ALGEBRA \ INSIEMISTICA \ TEORIA DEGLI INSIEMI (1)

Le funzioni in C. I programmi C sono costituiti da definizioni di variabili e funzioni.

POLIAGE GUIDA RAPIDA

4 3 4 = 4 x x x 10 0 aaa

Programmazione I - Laboratorio

Esempio: dest = parolagigante, lettere = PROVA dest (dopo l'invocazione di tipo pari ) = pprrlogvgante

Corso di Informatica

I Problemi e la loro Soluzione. Il Concetto Intuitivo di Calcolatore. Risoluzione di un Problema. Esempio

Transcript:

Funzioni e procedure DOTT. ING. LEONARDO RIGUTINI DIPARTIMENTO INGEGNERIA DELL INFORMAZIONE UNIVERSITÀ DI SIENA VIA ROMA 56 53100 SIENA UFF. 0577234850-7102 RIGUTINI@DII.UNISI.IT HTTP://WWW.DII.UNISI.IT/~RIGUTINI/

Sottoprogrammi

Sottoprogrammi Nella creazione di un programma può accadere che problemi simili appaiano in varie parti diverse La soluzione più ovvia è quella di replicare il codice in ogni parte dove è necessario, ma questa soluzione non è certamente la più comoda ed efficace: - Se il codice ha un errore, infatti, esso sarà replicato in tutte le parti in cui quel codice è stato copiato - Se quella parte di codice viene aggiornata, sarà necessario aggiornare tutte le parti in cui è stato copiato Molto poco flessibile e manutenibile: - uso di sottoprogrammi

Sottoprogrammi Sottoprogrammi: parti di codice separate dal flusso principale del programma che possono essere richiamate laddove sia richiesta tale funzionalità senza dover replicare il codice ogni volta Un sottoprogramma deve essere innanzitutto definito, cioè deve essere dichiarato da qualche parte ciò che esso esegue, su cosa lo esegue e cosa restituisce (se restituisce qualcosa)

Sottoprogrammi Una volta definito, il sottoprogramma può e deve essere implementato Implementare: scrivere il corpo del sottoprogramma, dichiarando le variabili necessarie e scrivendo le istruzioni che permettono di eseguire il compito per cui è stato progettato Una volta dichiarato e implementato, si può far riferimento a tale sottoprogramma ogni volta che nel codice è richiesta la funzionalità implementata nel sottoprogramma

Struttura completa di un programma C

Struttura di un programma La struttura di un programma C deve includere: - Una parte di direttive - Un programma principale (il main) che a sua volta è costituito da una parte dichiarativa (dichiarazione variabili) ed una esecutiva (istruzioni) A questa struttura possiamo aggiungere nuovi elementi derivati dalla scomposizione del problema in sottoprogrammi: Una parte dichiarativa globale Una serie di definizioni di sottoprogrammi

Struttura di un programma In particolare: - Parte dichiarativa globale: questa parte è compresa tra la parte di direttive ed il programma principale (il main). Essa contiene la dichiarazione di tutti gli elementi che sono condivisi (ossia usati in comune) dal programma principale e dai sottoprogrammi. Tali elementi possono essere costanti, tipi, variabili ed altro ancora. Per esempio se nella parte dichiarativa troviamo la dichiarazione int x; ciò significa che alla variabile x possono avere accesso il programma principale e tutti i suoi sottoprogrammi. - Una serie di definizioni di sottoprogrammi: in questa parte sono dichiarate tutti i sottoprogrammi che poi sono implementati nel progetto.

Struttura di un programma La nuova struttura dei programmi appena costruita introduce una importante novità: diversi elementi (variabili, tipi ecc ) possono essere dichiarati in posti diversi Vedremo in effetti che anche i sottoprogrammi possono avere la propria parte dichiarativa. Ma allora: - Perché abbiamo bisogno di dichiarare diversi elementi in posti diversi? - Esistono delle regole che dicono quali elementi possono essere usati dai sottoprogrammi?

Struttura di un programma Le domande precedenti avranno risposta più avanti Per adesso limitiamoci a dire che: - Il programma principale e tutti i sottoprogrammi possono utilizzare tutti e soli gli elementi dichiarati nella propria parte dichiarativa, nonché quelli che sono stati dichiarati nella parte dichiarativa globale

Funzioni e procedure

Funzioni e procedure Ma cosa sono di preciso i sottoprogrammi? Ogni sottoprogramma è un blocco più o meno lungo di codice che riceve in ingresso alcuni parametri (anche nessuno) e può restituire un valore: I parametri di ingresso sono variabili in cui sono inseriti i valori nel momento in cui il sottoprogramma è utilizzato nelle varie parti del codice Il sottoprogramma utilizza questi parametri per eseguire il suo compito ed eventualmente restituisce a sua volta al chiamante un valore

Funzioni e procedure Formalmente esistono due tipologie di sottoprogrammi: - Le funzioni, che in analogia con le funzioni matematiche, ricevono alcuni valori in ingresso e restituiscono un valore in uscita - Le procedure, che ricevuti alcuni dati in ingresso, eseguono un compito che non può concettualmente essere associato ad una funzione (es. stampa, aggiornamento ecc ) La distinzione è formale ma utile dal punto di vista didattico: Le procedure, in effetti possono essere assimilate a funzioni che non restituiscono alcun valore, ma eseguono semplicemente un compito

Funzioni

Funzioni Dal punto di vista matematico, una funzione è una regola matematica che prende valori da un dominio e genera valori appartenenti ad un codominio: - Somme, funzioni aritmetiche, ecc In informatica una funzione è molto simile alla definizione matematica: una funzione utilizza alcune variabili ognuna di un certo tipo (=dominio) e resituisce un valore (=codominio)

Funzioni Per costruire ed usare come sottoprogrammi operazioni non già disponibili nel linguaggio sono necessari due passi: - La definizione della funzione - La chiamata della funzione

Dichiarazione di funzione Un sottoprogramma di tipo funzione viene chiamato brevemente funzione La dichiarazione di una funzione è costituita da: - Una testata (o header) - L implementazione racchiusa tra parentesi graffe e costituita a sua volta da: La parte dichiarativa (detta parte dichiarativa locale) La parte esecutiva (detta corpo della funzione)

Header La testa contiene le informazioni più rilevanti per il corretto utilizzo della funzione ed è costituita a sua volta da: - Tipo del risultato - Identificatore della funzione (nome della funzione) - Lista dei parametri, su cui la funzione è applicata. In termini matematici la lista dei parametri costituisce il dominio della funzione mentre il tipo del risultato ne è il codominio

Lista dei parametri La lista dei parametri è racchiusa tra due parentesi tonde, aperta e chiusa e consiste in una successione di dichiarazione di parametri formali, separate tra loro da una virgola. Ogni dichiarazione di parametro formale è costituita a sua volta da un identificatore di tipo di dato che può essere sia un tipo predefinito (int, char, double ) sia un tipo di dato definito dall utente (struct, enum, ). I parametri formali si chiamano così poiché non hanno valore proprio ma sono semplicemente dei riferimenti simbolici agli argomenti delle funzioni

Valore di ritorno Il valore di ritorno (risultato) di una funzione è il tipo di dato del risultato della funzione stessa Esso può essere di tipo predefinito o definito dall utente. In particolare una funzione non può restituire un array o funzioni ma può restituire un puntatore a qualsiasi tipo.

Dichiarazione di funzione La sintassi comune di una dichiarazione di funzione è la seguente: Dove [tipo_di_dato] [nome_della_funzione] ([lista di parametri]) [lista di parametri] = lista di dichiarazioni di variabili visibili dall interno e dall esterno della funzione double potenza( int x, int y); questa riga di codice dice che da qualche parte esiste una funzione potenza che riceve in ingresso due interi, esegue qualcosa e ritorna un double.

Dichiarazione locale Dopo la testata seguono le dichiarazioni locali in cui sono definiti tutti gli oggetti necessari alla realizzazione della funzionalità desiderata Esse seguono le stesse leggi per la costruzione della parte dichiarativa di un programma Si possono trovare quindi dichiarazioni di tipi, di costanti, di variabili e di altri sottoprogrammi

Corpo della funzione e return Infine si trova il corpo della funzione costruito con le medesime regole sintattiche del programma principale Al suo interno però si trova normalmente una o più istruzioni di return la cui sintassi è definita dalla parola chiave return seguita da una espressione. L istruzione return assegna alla variabile risultato (che può essere immaginata come identificata dal nome dellafunione) il valore dell espressione coinvolta nel return e fa sì che l esecuzione della funzione termini. L espressione coinvolta nell istruzione return è quindi del tipo del codominio della funzione (ovvero il tipo di dato restituito dalla funzione): il suo valore nell istante di esecuzione dell istruzione return sarà il risultato della funzione

Istruzione return Nel corpo di un sottoprogramma possono essere presenti più istruzioni return ma ovviamente solo la prima che si incontra durante l esecuzione del flusso di istruzioni nel sottoprogramma viene eseguita Se nessuna istruzione return è presente nel corpo della funzione o se quelle presenti non vengono eseguite (perché in rami condizionali non utilizzati), il sottoprogramma termina in corrispondenza del simbolo } che conclude il corpo della funzione: in tal caso però il risultato della funzione è indefinito e ciò provoca una segnalazione di errore.

Esempi di funzioni double fatturato_totale(elencofatture ef) { double totale; int cont; } totale=0; for (cont=0; cont < ef.numfatture;cont++) { totale=totale+ef.sequenza[cont].importo; } return totale; Questa funzione restituisce il valore del totale degli importi delle fatture appartenenti ad un elenco di fatture (ElencoFatture è un tipo di dato definito dall utente tramite struct). Si noti l uso delle variabili locali cont e totale che sono utilizzate esclusivamente allo scopo di memorizzare valori all interno della funzione

Esempio di funzione int RadiceIntera (int par) { int cont=0; } while (cont*cont <= par) { cont = cont +1; } return (cont - 1); Questa funzione produce la radice quadrata intera del parametro par, cioè il massimo intero il cui quadrato non sia superiore al valore di par. Si noti come se par è negativo, il risultato è -1 e questo valore può essere utilizzato per segnalare un uso improprio della fuinzione

Chiamata delle funzioni Una funzione viene applicata ad una serie di valori appartenenti al dominio della funzione e restituisce un risultato. Più precisamente, la sintassi di una chiamata di funzione consiste nel nome della funzione stessa seguito dalla lista dei parametri attuali racchiusa tra parentesi tonde. I parametri attuali indicano i valori degli argomenti rispetto ai quali la funzione deve essere calcolata. Ogni parametro può quindi essere una espressione qualsiasi, eventualmente contenente una chiamata di funzione.

Chiamata delle funzioni La corrispondenza tra parametri segue l ordine della dichiarazione: al primo parametro formale viene fatto corrispondere il primo parametro attuale di ogni chiamata e via di seguito. Da questo segue che il numero ed il tipo di dato di parametri attuali in ogni chiamata deve essere lo stesso di quello dei parametri formali. Il tipo dei parametri è fissato una volta per tutte dalla dichiarazione della funzione come abbiamo visto precedentemente.

Chiamata delle funzioni Esempi: x = sin (y) cos(pigreco alfa); x = cos (atan(y) beta ); RisultatoGestione = FatturatoTotale(ArchivioFatture) SommaCosti(ArchivioCosti); Det1 = Determinante(Matrice1); Det2 = Determinante(MatriceInversa(Matrice1)); TotaleAssoluto = Sommatoria(lista1) + Sommatoria(lista2); ElencoOrdinato = Ordina(Elenco); ecc

Prototipo delle funzioni All interno di un programma C una funzione può essere chiamata purchè risulti definita oppure dichiarata. Definizione e Dichiarazione non sono termini equivalenti: la dichiarazione di una funzione (normalmente chiamata prototipo) si limita a richiamare la testata nel modo spiegato precedentemente ed ha lo scopo di facilitare la compilazione e la comprensione del programma.

Prototipo delle funzioni Talvolta infatti la chiamata di una funzione precede nel codice la definizione della funzione (ossia l implementazione) mentre altre volte le funzioni utilizzate da un programma sono definite in file propri del sistema C. Le funzioni di libreria sono esempi di funzioni richiamabili da diversi programmi: - La loro definizione è contenuta in file messi a disposizione dal creatore della libreria e non è necessario ridefinire tali funzioni internamente al programma che desidera richiamarle

Parte dichiarativa del programma e delle funzioni Riconsiderando quanto detto circa le parti dichiarative del programma principale e delle funzioni di un programma C, esse possono contenere i seguenti elementi: - Dichiarazioni di costanti - Dichiarazioni di tipo - Dichiarazioni di variabili - Prototipi delle funzioni

Procedure

Procedure Non sempre le funzionalità di cui abbiamo bisogno possono essere descritte come funzioni matematiche Per esempio, supponiamo di voler stampare un elenco di fatture. Ciò deve essere fatto in maniera parametrica poiché può capitare di voler stampare elenchi diversi in momenti diversi: abbiamo bisogno di una operazione StampaFatture che ha in comune con una funzione il fatto di avere le fatture da stampare come parametri ma, diversamente da essa, ha come scopo non il calcolo di un valorema la produzione di un tabulato di stampa

Le procedure Vi sono alcuni casi in cui l effetto dell operazione non è ben descritto come il calcolo di una operazione matematica: - Inserimento di una nuova fattura in un archiviofatture: il risultato atteso non è un nuovo archiviofatture ma l aggiornamento di una variabile già esistente. - Ordinare una archivio di fatture secondo per esempio la data di emissione. L effetto di un sottoprogramma Ordina(archivio) non deve restituire un risultato ma deve modificare archivio in modo che gli elementi in esso contenuti siano ordinati per data. In conclusione in diverse occasioni il compito di una operazione non (o non è soltanto) quello di produrre un risultato di un certo tipo, ma è modificare lo stato, cioè il contenuto, del programma che ne chiede l esecuzione: in tali casi il sottoprogramma utilizzato è chiamato procedura

Procedura Le procedure sono del tutto simili alle funzioni. Hanno una parte dichiarativa composta da: - Header - Dichiarazioni variabili locali - Corpo La differenza è che una procedura, quindi, in generale, non restituisce un valore e quindi l header ha come valore di ritorno un tipo speciale: void Il tipo void può essere impiegato come tipo dei parametri formali nel caso non esistano parametri passati alla funzione o procedura

Chiamata delle procedure La chiamata ad una procedura è una istruzione uguale alla chiamata ad una funzione. Consiste ne: - Identificatore della procedura - Elenco dei parametri attuali fra parentesi tonde La differenza sta nel fatto che tale chiamata non produce come risultato un valore ma solamente la modifica di uno o più parametri attuali. I parametri attuali indicano i valori degli argomenti con i quali deve essere eseguita la procedura: ogni paramtero può essere costituito da un qualsiasi genera di espressione

Chiamata delle procedure La corrispondenza tra parametri formali e parametri attuali segue le stesse regole della chiamata delle funzioni: il rpimo parametro formale viene assegnato al primo parametro attuale e così via. Esempio: Typedef struct { String indirizzo; double ammontare; Data DataFattura; } DescrizioneFatture;

Chiamata delle procedure Typedef struct { int NumFatture; DescrizioneFatture Sequenza[MaxNumFatt]; } ElencoFatture; ElencoFatture ArchivioFatture; void inseriscifattura(descrizionefatture Fattura); boolean Precede(Data d1, Data d2); void main() { Data DataOdierna; DescrizioneFatture Fattura1,Fattura2; inseriscifattura(fattura1); if (Precede(Fattura2.DataFattura,DataOdierna)) { inseriscifattura(fattura2); } }

Chiamata delle procedure void inseriscifattura(descrizionefatture fattura) { if (ArchivioFatture.NumFatture == MaxNumFatture) { printf( L archivio è pieno ); } else { ArchivioFatture.NumFatture=ArchivioFatture.NumFatture+1; ArchivioFatture.Sequenza[ArchivioFatture.NumFatture-1]=fattura; } }

Passaggio di parametri

Passaggio di parametri Cosa succede quando si passa una variabile come parametro ad una funzione? Ossia, se all interno della funzione modifico il valore di quel parametro, il valore della variabile originaria viene modificato? La risposta è: dipende!! Esistono due modi per il passaggio dei parametri: - Per valore - Per riferimento

Passaggio di parametri per valore Quando è richiesto che i valori delle variabili non siano modificati dal corpo della funzione, quello che avviene è che il valore della variabile viene copiato nella variabile-parametro della funzione. In questo modo: - ogni modifica alla variabile locale non si riflette sulla variabile esterna - ogni modifica fatta alle variabili locali, però, va persa all uscita dalla funzione e solo il risultato ritornato è visibile dall esterno della funzione Questo metodo di passaggio dei parametri va sotto il nome di passaggio per copia o per valore

Passaggio di parametri per riferimento Quando invece è richiesto che le variabili interne alla funzione siano non una copia di quelle esterne, ma la stessa cosa, si parla di passaggio per riferimento: - In questo caso, la variabile locale assume il riferimento in memoria alla variabile esterna - Quindi ogni modifica ad essa è una modifica alla cella di memoria originale e permane anche alla chiusura dalla funzione Normalmente questa situazione è richiesta quando si ha un sottoprogramma che deve effettuare delle operazioni su un dato e non ritorna alcun valore: ES. aggiornamento del fatturato totale, ecc in questo caso il fatturato totale, una volta aggiornato deve rimanere tale, quindi è necessario operare l aggiornamento sulla variabile originale e non su una copia del valore

Passaggio di parametri Notiamo che il passaggio per valore richiede la copia della variabile nella nuova variabile locale, mentre il riferimento ha un costo costante, viene copiato comunque un indirizzo di memoria - Se stiamo passando dati strutturati particolarmente complessi, array di grandi dimensioni, ecc il passaggio per valore può essere molto più lento del passaggio per riferimento E anche vero però che il passaggio per valore è molto più safety rispetto all altro, dato che qualsiasi modifica, anche quella non voluta ed erronea non si riflette sul dato originario

Passaggio di parametri Il metodo utilizzato per il passaggio dei parametri cambia da linguaggio a linguaggio: - Il C e C++ utilizzano il passaggio per valore, ma forniscono dei tipi di dato che permettono di realizzare il passaggio per riferimento (puntatori e riferimenti) - Il JAVA utilizza il passaggio per riferimento. Il passaggio per valore può comunque essere effettuato costruendo ogni volta una copia del parametro e passare quella alla funzione.

Funzioni e procedure La differenza sul metodo di passaggio dei parametri stabilisce un altra fondamentale differenza tra una funzione ed una procedura: - Funzione: lo scopo principale è quello di utilizzare alcuni valori in ingresso per produrre un valore di uscita, senza modificare null altro passaggio per valore - Procedura: lo scopo principale è quello di operare delle operazioni sui parametri in ingresso che abbiano una semantica tale da poterle racchiudere in un corpo unico passaggio per riferimento Comunque la differenza è solamente formale: - In C++ o in JAVA non esistono metodi per dichiarare una funzione piuttosto che una procedura. In realtà il primo prevede solamente funzioni, il secondo procedure.

Passaggio di parametri per valore Spazio di memoria del chiamante int a copia Spazio di memoria del chiamato par1 Programma chiamante chiamata di funzione valore di ritorno modifica locale Funzione chiamata

Passaggio di parametri per riferimento Spazio di memoria del chiamante int a Programma chiamante riferimento modifica chiamata di funzione valore di ritorno Spazio di memoria del chiamato par1 modifica locale Funzione chiamata

La funzione main

Il corpo del programma Adesso che abbiamo acquisito il concetto di funzione è possibile descrivere la struttura completa di un programma (procedurale) Un programma procedurale è composto da: - Dichiarazioni di variabili - Dichiarazioni di funzioni (prototipi) - Implementazioni delle funzioni (anche su più file) - Corpo del programma Ma cosa s intende per corpo del programma? - Il corpo del programma è a sua volta una funzione globale particolare che utilizzando tutte le altre funzioni ed i dati a disposizione, assembla il tutto per eseguire il compito ell algoritmo

La funzione main In definitiva, un programma non è altro che una funzione dal prototipo particolare e fissato: - int main(int argc,char[] argv) C e C++ Questa funzione accetta due parametri in ingresso e restituisce un valore intero in uscita: - I due parametri in ingresso permettono di leggere eventuali parametri passati da linea di comando all applicazione - Es. il comando copy in windows accetta due parametri - copy file_a file_b argc=2, argv=[file_a,file_b] - Il valore di ritorno permette di stabilire se l esecuzione del programma è andata a buon fine o meno. Solitamente un programma ritorna 0 se esce correttamente, 1 se vi sono stati errori

Variabili globali e locali La dichiarazione di una variabile normalmente può avvenire in qualsiasi punto del programma, sempre però prima che la variabile sia utilizzata per la prima volta In particolare, la variabile può essere dichiarata fuori da qualsiasi funzione oppure all interno di una funzione (o procedura : - Nel primo caso, ambito globale, la variabile esiste in ogni parte del programma (con le debite eccezioni), a partire dal momento in cui il compilatore legge la riga di dichiarazione e la istanzia - Nel secondo caso, ambito locale, invece, la variabile esiste solamente all interno della funzione in cui è stata dichiarata e sarà deallocata all uscita della funzione stessa

Visibilità di variabili (scope) Ma cosa accade se una variabile locale ha lo stesso nome di una variabile globale? Quando la funzione è chiamata, con quel nome a quale variabile ci riferiamo, quella globale o quella locale? Risposta: la variabile più vicina, è quella a cui viene fatto riferimento con il nome ( nesting rule ) - Il campo di visibilità di una variabile (scope) è sempre quello ultimo in cui la variabile è stata dichiarata, anche se due variabili portano lo stesso nome Quindi durante l esecuzione di tutta la procedura, ogni riferimento alla variabile A sarà risolto con un riferimento alla variabile A locale alla procedura. Tale riferimento cessa non appena la procedura e lo scope relativo ad essa terminano

Lo spazio di memoria globale Per tutto quello che si è detto, anche le variabili dichiarate nella funzione principale sono variabili locali alla funzione main: - Un programma allora è composto da più spazi di memoria distinti, di cui solamente uno, lo spazio di memoria globale è condiviso in tutto il progetto Lo spazio di memoria globale è composto dalle variabili dichiarate fuori da ogni funzione, compresa la funzione principale (il main())

Schema di un programma procedurale Spazio di memoria del main Spazio di memoria globale Spazio di memoria delle funzioni f 1 int main(int,int) f 2 Chiamate di funzione Accessi alla memoria globale f n

Le funzioni nella CPU

Cambio di contesto Ma cosa succede a basso livello quando viene chiamata una funzione? La chiamata di una funzione genera un cambio di contesto (context switch) nella CPU: - Lo stato attuale della CPU deve essere salvato da qualche parte affinchè una volta eseguita la procedura sia possibile riprendere correttamente l esecuzione delle istruzioni - Anche i registri devono essere salvati dato che le istruzioni della procedura non devono assolutamente modificare i valori eterni alla procedura stessa Lo stato del processo viene allora salvato sullo stack di sistema

Lo stack Una parte di memoria centrale viene gestita come stack di sistema (pila) Alla chiamata di una procedura: - il contesto attuale viene inserito in cima allo stack: operazione push - Prepara la CPU ad eseguire la nuova procedura: Setta l istruzione da eseguire, il registro dati e quello indirizzi con i parametri passati alla procedura - La procedura esegue le istruzioni - Quando la procedura termina, il contesto in cima allo stack viene ripristinato: Operazione pop Lo stack memorizza le chiamate a procedura e le richiama in ordine inverso di memorizzazione

Stack Alla chiamata della funzione f, il contesto della funzione in esecuzione attualmente viene memorizzato in cima allo stack CPU f3 push f4 funzione in esecuzione f2 f1 main() stack f3 f2 f1 main() stack

Stack Quando poi la funzione termina, viene ripristinato l ultimo contesto inserito nello stack, la funzione f(t-1): pop f3 CPU f f2 funzione in esecuzione Funzioni chiamate e terminate f1 main() stack

Stack Quando anche la funzione f(t-1) termina, viene ripristinato il contesto di f(t-2), e così via pop f2 CPU f3 f1 funzione in esecuzione f Funzioni chiamate e terminate main() stack

Stack L ultimo contesto nello stack sarà quello relativo alla funzione main(). Quando questo termina, l applicazione termina. pop main() funzione in esecuzione CPU f1 f2 f3 empty stack f Funzioni chiamate e terminate

La ricorsione

La ricorsione Si parla di ricorsione quando all interno di una funzione P, viene richiamata P stessa, normalmente su un diverso set di dati La chiamata ricorsiva può essere sia esplicita che implicita: - Esplicita la funzione P è richiamata esplicitamente all interno del corpo di P stesso - Implicita la funzione P è richiamata attraverso la chiamata ad un altra funzione Q che contiene la chiamata a P A prima vista la cosa può apparire sorprendente dato che una funzione (o procedura) è dedita a risolvere un sottoproblema: - La ricorsione è come dire che per risolvere il sottoproblema P è necessario risolvere il sottoproblema P stesso!!

La ricorsione In realtà la programmazione ricorsiva a profonde radici concettuali derivanti dal fatto che per molti problemi la soluzione per un caso generico può essere ricavata sulla base della soluzione di un caso più semplice dello stesso problema : - L ordinamento di un array, il calcolo del fattoriale, ecc sono tipici problemi di questo tipo

Es. somma come ricorsione La ricorsione è nascosta in moltissime forme del ragionamento umano. Per esempio la somma tra due numeri può essere vista come ricorsione dell operatore successore(): - successore(x)=numero successivo ad x Algoritmo ricorsivo per l addizione - x+0 = x; - x+y = x+successore(y-1) = successore(x+y-1) - - 1+3 = successore(1+2) = successore(sucessore(1+1)) = - = successore(successore(successore(1+0))) = - = successore(successore(successore(1))) = - = successore(successore(2)) = successore(3) = 4

Es. 1 Pesate Problema: date un gruppo di palline di forma identica e tutte di peso identico tranne una di peso maggiore delle altre, trovare la pallina più pesante utilizzando una bilancia ed il minor numero di pesate (supponiamo di avere un numero di palline multiplo di 3) Algoritmo: - Dividi il gruppo in tre gruppi di egual numero di palline e pesa due di questi gruppi - Se i due gruppi hanno lo stesso peso scartarli entrambi, altrimenti scartare il gruppo non pesato e quello sulla bilancia con il peso minore (la pallina cercata si troverà nel gruppo con peso maggiore) - Dividi il gruppo rimasto in tre sottogruppi e ripeti il procedimento finché i sottogruppi non contengano una sola palline: in tal caso l ultima pesata indica quale delle tre palline rimaste è quella cercata

Es. 1 Pesate Notiamo come l ultimo punto sia la ripetizione dell algoritmo su un set ridotto di dati fino a che non ci troviamo di fronte ad un caso notevole, di cui possiamo dare una immediata soluzione: - tre gruppi di una pallina ciascuno Per avere una visione più informatica dell algoritmo: - Se il gruppo di palline consiste di una sola pallina, la pallina è la soluzione cercata, altrimenti - Dividi il gruppo di palline in tre sottogruppi e poni due sottogruppi sulla bilancia - Se i due gruppi hanno peso uguale scartarli entrambi, altrimenti scartare il gruppo non pesato e quello di peso minore - Applica l algoritmo pesate al sottogruppo individuato

Es. 2 Successione di Fibonacci La successione di numeri di Fibonacci fu definita dal matematico come modello di crescita dei conigli in allevamento: - f0=0 - f1=1 - fn=fn-1 + fn-2, per n> 1 Es. calcolando f per i primi 6 numeri: - f0=0 - f1=1 - f2=f1+f0=1+0=1 - f3=f2+f1=1+1=2 - f4=f3+f2=2+1=3 - f5=f4+f3=3+2=5 -

Es. 3 Fattoriale Il fattoriale di un numero è la produttoria di tutti i numeri fino al numero stesso, escluso lo 0: - 5! = 5 x 4 x 3 x 2 x 1 = 120 Il fattoriale può essere espresso in forma ricorsiva: - 0! = 1 - n! = n x (n-1)! Infatti: - 5!=5 x (4 x 3 x 2 x 1) = 5 x 4! Anche il fattoriale è un tipico esempio di algoritmo ricorsivo

Es. 4 ricerca in un vettore Un esempio tipico di algoritmo di ricerca dati in un vettore ordinato è il seguente: - Se il vettore è composto da un solo elemento, confronta l elemento con D e se è uguale ritorna D altrimenti ritorna non trovato - Seleziona l elemento centrale P del vettore - Confronta il dato D da cercare con l elemento centrale P - Se D<P, ripeti l algoritmo sulla prima metà del vettore, altrimenti ripetilo sulla seconda

Es. 5 Sommatoria La sommatoria di una sequenza di n numeri può essere vista come la somma numero n-esimo alla sommatoria dei rimanenti (n-1) numeri: 0 i= 0 n a i = 0 n 1 a = a + a i n i i= 1 i= 1 La sommatoria comunque può essere risolta semplicemente con un ciclo, risparmiando così il carico aggiuntivo della ricorsione

Caso generale Si può generalizzare il concetto di ricorsione. Affinché un algoritmo sia scrivibile in forma ricorsiva, è necessario che: - l algoritmo abbia una soluzione banale, f(0)=0 - l algoritmo al passo n possa essere espresso come funzione del valore al passo (n-1) La condizione banale è necessarie per evitare una ricorsione infinita: Se infatti non esiste una condizione in cui siamo in grado di dare la soluzione, le iterazioni non terminano mai In questo caso, solitamente si verifica un errore di stack overflow, ovvero pila piena

Ricorsione e stack La ricorsione viene realizzata mantenendo nello stack la sequenza delle chiamate alla procedura che non possono essere risolte fino al caso banale Una volta trovata la soluzione banale questa viene passata a ritroso a tutte le istanze dell algoritmo sospese nello stack fino alla istanza finale che restituisce il risultato finale

Successione di Fibonacci Scriviamo l algoritmo ricorsivo per la successione di Fibonacci: - int fibonacci(int n) { - int ris=0; - if (n==0) return 0; - else if (n==1) return 1; - else ris = fibonacci(n-1) + fibonacci(n-2); - }

Fattoriale e sommatoria Scriviamo l algoritmo ricorsivo per il calcolo del fattoriale: - int fatt(int n) { - int ris=0; - if (n==0) return 1; - else ris = n * fatt(n-1); - return ris; - } Scriviamo l algoritmo ricorsivo per la sommatoria: int sum(int a[]) { int ris=0; if (vuota(a)==true) return 0; else ris = testa(a) + coda(a); return ris; }

Fattoriale 1 y=fatt(5) CPU fatt(2) push fatt(1) funzione in esecuzione fatt(3) fatt(4) fatt(5) stack fatt(2) fatt(3) fatt(4) fatt(5) stack

Fattoriale 2 y=fatt(5) pop fatt(2) CPU fatt(1)=1 fatt(3) funzione in esecuzione Funzioni chiamate e terminate fatt(4) fatt(5) stack

Fattoriale 3 y=fatt(5) pop fatt(5) funzione in esecuzione CPU fatt(4)=24 fatt(3)=6 fatt(2)=2 empty stack fatt(1)=1 Funzioni chiamate e terminate

Programmazione ad oggetti

Paradigma ad oggetti Come detto, i linguaggi di programmazione procedurali sono stati quasi tutti sostituiti dalle rispettive versioni ad oggetti (tranne nei casi in cui la programmazione ad oggetti non è consigliata, ad esempio programmazione di basso livello, S.O., programmazione PLC ecc ) Ma cos è questo diverso tipo di programmazione? In cosa differisce dalla programmazione classica?

Object Oriented Programming (OOP) La maggior parte dei programmi utili non manipola soltanto numeri e stringhe, ma gestisce dati più complessi e più aderenti alla realtà: - Conti bancari, informazioni sugli impiegati, forme grafiche L idea della programmazione ad oggetti è di rappresentare questi dati così come sono anche nel linguaggio di programmazione: gli oggetti o classi La programmazione ad oggetti estende al massimo l idea di dato strutturato presente nella programmazione classica: - una classe (o oggetto) è un tipo di dato strutturato a cui sono associate variabili e funzioni

Object Oriented Programming (OOP) Il programmatore individua degli oggetti nella applicazione che deve progettare: - Conto corrente, client e server, documento, ecc Ogni oggetto ha delle proprietà e delle capacità (o funzioni) L applicazione è poi costruita facendo interagire correttamente gli oggetti esistenti nell ambiente che si va a progettare Ma la funzione main() che fine ha fatto? Se non si usano più le procedure/funzioni globali, come si rende eseguibile un programma? Cioè qual è il corpo principale dell applicazione? Due filosofie differenti per rispondere a queste domande: il C++ ed il JAVA

Object Oriented Programming(OOP) Il C++ ha mantenuto comunque una certa compatibilità con i linguaggi procedurali: - in C++ è ancora possibile infatti definire funzioni globali, variabili globali e quindi la funzione main() rimane una funzione definita globalmente e cercata dal compilatore per rendere eseguibile un programma Il JAVA ha seguito un altro approccio: In JAVA non esistono funzioni globali, cioè esterne da qualsiasi oggetto, ma ogni funzione o variabile deve appartenere ad un oggetto (o meglio classe): è possibile però definire una funzione main() per ogni classe e rendere eseguibile la classe stessa In compilazione, vengono creati degli oggetti eseguibili (non un unico programma). La scelta di cosa far partire viene fatta dall utente selezionando l oggetto eseguibile desiderato.

Object Oriented Programming(OOP) Es di oggetti JAVA ObjectA: -datoy -funzione1 -funzione2 -main() ObjectB: -datox -funzione1 -funzione2 -funzione3 -main() Normalmente quello che accade in JAVA è che viene creato un oggetto che rappresenta l applicazione e che contiene la funzione main(): - Eseguire l applicazione significa lanciare il main() di tale oggetto