M5-U6-I sottoprogrammi

Transcript

M5-U6-I sottoprogrammi
Modulo 5 – La programmazione
Unità 6 – I sottoprogrammi
Prof. Antonio Scanu
1 Top-down e Bottom-up
Quando un problema appare immediatamente complesso, per risolverlo possiamo individuare e analizzare i sottoproblemi più semplici che lo compongono, oltre alle loro interrelazioni. In questo modo è possibile articolare la progettazione dell'algoritmo complessivo in
una serie di algoritmi più semplici, che verranno poi opportunamente assemblati.
La programmazione, in effetti, non è solo un processo di ideazione e formulazione di algorit mi: esige attenzione ai minimi dettagli e l'adozione di opportune tecniche di analisi dei pro blemi da risolvere. Per questo, una buona metodologia di progettazione è quella che risolve
il problema per passi: partendo da un'analisi generale, si focalizza l'attenzione sui singoli
punti fondamentali che lo compongono, riducendo così le difficoltà.
Tale metodologia, di natura gerarchica, prende il nome di top-down, ossia "dall'alto verso il
basso". Gli aggettivi alto e basso si riferiscono al livello di dettaglio o astrazione. Il livello più
alto (top) è quello generale, chiamato problema principale; in esso si individuano i nodi fondamentali chiamati sottoproblemi.
Ciascun sottoproblema viene dettagliato a parte e, se complesso, può essere a sua volta
scomposto in ulteriori sottoproblemi più semplici. Si giunge così all'analisi e alla risoluzione
di tanti problemi elementari tramite algoritmi descritti a livello programmabile (il livello più
basso è down), le cui relazioni sono ricavate dalla descrizione a livello superiore. In sintesi,
si scende dal generale al particolare mediante affinamenti successivi.
La tecnica top-down, quindi, nasce come tecnica di analisi dei problemi e non come tecnica
di progettazione: il risolutore, infatti, utilizza tale metodologia per affrontare agevolmente il
processo risolutivo del problema.
All'atto dell'implementazione, poi, il programmatore deciderà se implementare singolarmente i vari sottoproblemi, o se accorparne alcuni e scomporne altri.
Per scomporre un problema in tanti sottoproblemi funzionali ci si sofferma su cosa debba
essere fatto e non sul come, che con tale metodologia viene affrontato poco e soltanto all'ultimo livello.
La tecnica top-down, quindi, parte dall'obiettivo e da esso fa scaturire la strategia più adatta
a raggiungere l'obiettivo stesso; valorizza, quindi, il perché e da esso fa dipendere il come,
ossia la strategia. Individua, pertanto, le risorse necessarie, precisa quelle disponibili e
identifica quelle mancanti, propone successivamente ogni risorsa mancante come sotto-obiettivo, ovvero come sottoproblema in cui ciascun sotto-obiettivo richiede una sotto-strategia risolutiva.
La metodologia bottom-up, ossia "dal basso verso l'alto", privilegia invece l'aspetto esecutivo rispetto a quello funzionale, procedendo dal particolare verso il generale. Il metodo bottom-up è una strategia induttiva e consente di concentrarsi subito sui punti cardine del problema che, però, potrebbero essere di difficile individuazione iniziale.
Nel modello top-down si affronta il problema osservandolo più in generale e poi rifinendo
ogni sua parte. Ad esempio, creo un modello di automobile e poi scendo nel dettaglio rifinendo ruote, motore e così via. Un approccio informatico è quello di servirsi di sottoprogrammi da definire in un secondo momento. È il classico approccio della programmazione
procedurale.
Nel modello bottom-up si affronta il problema preoccupandosi prima dei dettagli più semplici, fino ad arrivare al modello più complesso (esattamente il contrario del modello topdown). Ad esempio, creo una ruota, poi un motore e le altre parti e solo alla fine ottengo il
modello complesso di un'automobile.
Nella programmazione ad oggetti è utilizzato questo metodo, in quanto si ha la possibilità di
creare singoli oggetti indipendenti e quindi tramite questo approccio si sviluppa cominciando dalle classi base che poi si estendono o si collegano insieme a creare un programma
complesso.
Nel processo di sviluppo software, gli approcci top-down e bottom-up giocano un ruolo fondamentale. L'approccio top-down enfatizza la pianificazione e una completa comprensione
del sistema. È ovvio che la fase di codifica non può iniziare finché non si è raggiunto almeno
un sufficiente livello di dettaglio nella progettazione di una parte significante del sistema.
Questo, comunque, ritarda la fase di test delle ultime unità funzionali di un sistema fino a
quando una parte rilevante della progettazione non è stata completata.
Al contrario, l'approccio bottom-up enfatizza la codifica e la fase di test precoce, che può iniziare appena il primo modulo è stato specificato. Questo approccio, comunque, induce il ri-
schio che i moduli possano essere codificati senza avere una chiara idea di come dovranno essere connessi ad altre parti del sistema.
L'importante principio di riusabilità del codice è una delle muse ispiratrici di questo metodo.
I moderni approcci alla progettazione software comunemente combinano sia la tecnica topdown sia quella bottom-up. Benché l'analisi e la comprensione del sistema completo sia tipicamente considerata necessaria per una buona progettazione (e quindi tramite l'approccio top-down), nella maggior parte dei progetti software si cerca di fare uso di codice già esistente ad alcuni livelli (tendenza bottom-up).
2 Sottoalgortimi e sottoprogrammi
È possibile realizzare un sottoalgoritmo per ogni sottoproblema non più scomponibile.
Unendo, alla fine, tutti i sottoalgoritmi, si ottiene l'algoritmo che risolve il problema originale.
Il sottoalgoritmo, quindi, è una parte dell'algoritmo che risolve un particolare sottoproblema.
Tecnicamente, è una parte dell'algoritmo risolutivo che non può essere eseguita autonomamente, ma soltanto su richiesta (invocazione) e sotto stretto controllo dell'algoritmo o del
sottoalgoritmo che lo invoca, il quale per tale motivo, è denominato "chiamante".
Grazie a metodologie note con il nome di tecniche di programmazione modulare, è possibile
suddividere il procedimento generale che risolve un problema in:
 un algoritmo principale (main) che descrive globalmente il problema; s un insieme di
sottoalgoritmi che risolvono i singoli sottoproblemi.
 ogni algoritmo viene tradotto in un programma quando si utilizza un linguaggio di
programmazione. I sottoalgoritmi, una volta codificati, vengono chiamati sottoprogrammi.
Pertanto, si parla correttamente di programmi e sottoprogrammi quando si passa alla fase
di "codifica" in un linguaggio di programmazione. Nella nostra trattazione, anche al fine di
uniformarsi alla classica e consolidata terminologia in uso in ambiente informatico, utilizzeremo anche i termini programma principale e main per riferirci all'algoritmo principale e il
termine sottoprogramma per fare riferimento al sottoalgoritmo. È importante ricordare che
anche il main è un sottoprogramma, una sorta di sottoprogramma "speciale" per il fatto che
non può essere chiamato da nessuno e che è il primo a essere attivato (in modo automatico) in fase di esecuzione.
Applicare la tecnica top-down in modo eccessivo, ossia frammentare i vari sottoproblemi riducendoli a pochissime azioni ciascuno, è sconveniente. Il programma sarà costituito, in un
caso del genere, da tantissimi sottoprogrammi (spesso inutili e talvolta contenenti una sola
istruzione). Diverrà, così, eccessivamente frammentato, inefficiente e scarsamente leggibile. Non esiste una formula in grado di stabilire quanti sottoprogrammi occorrono per la risoluzione di un problema e quando utilizzarli. Possiamo fornire però alcune indicazioni generali che nascono dall'esperienza.
Analizziamo il seguente problema: preparare una torta gelato al cioccolato.
ALGORITMO Torta
INIZIO
Preparare la base per la torta
Preparare un gelato al cioccolato
Preparare la glassa al cioccolato
Farcire la torta con il gelato e la glassa
FINE
Tutte e quattro le azioni sono piuttosto complesse e anche di interesse generale: per questi
motivi conviene descriverle per mezzo di sottoprogrammi. Continuiamo l'affinamento, a partire dalla prima azione, cioè Preparare la base per la torta. Avremo:
SOTTOALGORITMO Preparare la base per la torta
INIZIO
Preparare l'impasto
Preparare la teglia
Preparare il forno
Infornare
Sfornare
FINE
L'attività Preparare l'impasto è di interesse generale, in quanto lo stesso impasto potrà essere utilizzato per molti altri tipi di torte: quindi si può descriverla, laddove fosse necessario,
tramite un sottoprogramma. Le altre (da Preparare la teglia a Sfornare) sono piuttosto sem plici e pertanto possono essere dettagliate immediatamente. Analogo ragionamento andrà
fatto per le altre azioni descritte nell'algoritmo principale.
Uno stesso sottoprogramma può essere richiamato più volte sia dal programma principale
sia dagli altri sottoprogrammi. Inoltre, può essere utilizzato all'interno di altri programmi nei
quali sia necessaria la stessa operazione; ciò consente la riusabilità del codice.
Conviene descrivere un'attività per mezzo di un sottoalgoritmo quando:
è di interesse generale
Non conviene descrivere un'attività per
mezzo di un sottoalgoritmo quando
è di è di scarso interesse generale
non è di interesse generale ma si presenta pur pur essendo di interesse generale
più volte all'interno del programma
non migliora la leggibilità del programma
o, addirittura, la peggiora.
pur pur essendo di scarso interesse generale più permette una maggiore leggibilità
del programma
Riepilogando, esistono ottimi motivi che spingono a utilizzare i sottoprogrammi. In particolare essi:
 migliorano la leggibilità del programma in maniera considerevole;
 permettono l'astrazione funzionale; quando il programmatore inserisce un'istruzione
di chiamata di sottoprogramma, astrae dalla realtà del sottoproblema, cioè si disinteressa, in quel momento, della sua realizzazione, perché gli interessa solo che cosa
fare e non come farlo;
 consentono di scrivere meno codice e così occupano meno memoria; si evita, infatti,
di riscrivere più volte sequenze di istruzioni identiche in punti diversi del programma;
 sono riutilizzabili. Molto spesso accade che il sottoproblema da affrontare sia già
stato risolto altrove; se abbiamo già il sottoalgoritmo risolutivo, possiamo riutilizzarlo
senza riscriverlo. Per poter essere facilmente riutilizzabile, il sottoalgoritmo deve
avere una forte coesione e pochi collegamenti con l'esterno, deve, cioè, risolvere il
suo compito e avere pochi dati in comune con l'esterno.
3 Le funzioni
Una funzione è un sottoprogramma contenente le istruzioni che risolvono uno specifico problema, che, quando il sottoprogramma viene attivato da un'istruzione di chiamata, restituisce un valore. Il valore della funzione deve essere usato come elemento di un'istruzione.
Le funzioni restituiscono un risultato, oltre a svolgere un'azione; per questo motivo la funzione può essere richiamata in un'assegnazione a una variabile oppure all'interno di una gene rica espressione.
#include<iostream>
using namespace std;
tipodidato nomefunzione (<lista dei parametri>);
Prototipo della Funzione
int main(){
…......................;
variabile=nomefunzione(<listavariabili>);
Chiamata della
Funzione
….....................;
return 0;
}
tipodidato nome funzione(<lista dei parametri>)
{
….............;
return(valore);
}
Definizione della Funzione
dove:



tipodidato è uno dei tipi di dato usati in C++;
<ListaParametri> è così strutturata:
 TipoParametro1 parametro1, TipoParametro2 parametro2, …....,TipoParametroN
parametroN
 void (nel caso non si passino dei parametri).
Valore:
 un numero diretto o un carattere diretto
 costante
 variabile
3.1.1 Esempio : Somma tra due numeri
#include <iostream>
using namespace std;
int somma (int x, int y);
int main ()
{
int a,b,z;
cout<<”inserisci a”;
cin>>a;
cout<<”inserisci b”;
cin>>b;
z = somma (a,b);
cout << "Il risultato e' " << z;
return 0;
}
int somma (int x, int y)
{
int r;
r=x+y;
return r;}
3.1.2 Esempio: calcolo della media di due studenti e rilevazione
dello studente migliore (ogni studente ha tre voti)
#include <iostream>
using namespace std;
int media(void);
char maggiore(int x,int y);
int main ()
{
int a,b;
char c;
a = media();
b = media();
c = maggiore(a,b);
cout << "il migliore è" << c;
return 0;
}
int media()
{
int m,numero,i,somma;
for(i=0;i<3;i++){
cout<<”inserisci numero”;
cin>>numero;
somma=somma+numero;}
m=numero/3;
return m;
}
char maggiore(int x,int y)
{
if(x>y)
return ('a');
else
return ('b');
}
4 Ambienti locale e globale
Durante l'implementazione di un sottoprogramma occorre definire tutte le risorse necessarie al suo funzionamento, vale a dire il suo ambiente.
Con il termine ambiente di un sottoprogramma definiamo l'insieme delle risorse (variabili,
costanti, sottoprogrammi, parametri) alle quali esso può accedere.
Per il momento, diciamo che l'ambiente è costituito:
 dall'ambiente locale, cioè dalle risorse dichiarate all'interno del sottoprogramma (risorse locali);
 dall'ambiente globale, ossia dalle risorse utilizzabili da tutti i sottoprogrammi (risorse
globali).
Programma Esempio.
E’ possibile dichiarare delle variabili dentro le funzioni. Esse sono dette variabili locali e
sono visibili solo dentro la funzione in cui vengono dichiarate. Perciò è possibile dichiarare
variabili con lo stesso nome in funzioni (ambienti) diversi. Per il sottoprogramma A le variabili locali sono Z1,Z2,Z3.
E’ possibile dichiarare delle variabili globali e sono visibili e accessibili in tutto il programma. Nel nostro caso Y1,Y2,Y3.
In C++ il Main è considerato un sottoprogramma. Le variabili dichiarate nel Main sono considerate variabili locali del Main.
Un corretto stile di programmazione impone di minimizzare l'uso dell'ambiente globale e di
privilegiare quello locale. L'uso di variabili globali è, come già detto, fortemente sconsigliato
e comunque richiede parsimonia, poiché rischia di generare interazioni di difficile controllo
tra diverse parti di un programma. L'abuso di variabili globali può rendere difficile isolare gli
errori (in gergo bug) e inoltre indica che il progetto di un programma non è stato pensato attentamente.
L'utilizzo delle variabili locali permette di:
 agevolare la lettura del programma, in quanto mette in evidenza in quale ambito
hanno significato le risorse;
 individuare facilmente errori commessi, in quanto ci si sofferma solo sulle risorse locali nell'ambito di quel sottoprogramma.
Le variabili globali
Le variabili locali
sono allocate (e inizializzate - in ogni
caso)
sono allocate (e inizializzate, se richiesto)
quando si entra nel sottoprogramma
rimangono allocate per tutta la durata
del programma
rimangono allocate solo per la durata
del sottoprogramma
vengono inizializzate solo una volta
vengono inizializzate tutte le volte che
viene eseguito il sottoprogramma
5 Le regole di visibilità
È ormai chiaro che all'interno di un programma ogni oggetto ha un suo campo di validità
(scope), ossia un ambito in cui può essere usato e riconosciuto. È pertanto necessario definire delle regole per determinare il campo di visibilità degli oggetti globali e locali di un programma. Si parte dai seguenti principi:
1. Gli oggetti globali sono accessibili a (visibili in) tutto il programma.
2. Un oggetto dichiarato in un sottoprogramma ha significato solo in quel sottoprogramma e in tutti quelli in esso dichiarati. L'ambiente di un sottoprogramma, quindi, include anche tutte le risorse dei sottoprogrammi che contengono il sottoprogramma
stesso (ambiente non locale).
3. Un oggetto non può essere usato se non è stato prima dichiarato.
Nella descrizione di un algoritmo, può succedere che una variabile sia dichiarata con lo
stesso nome (il tipo potrebbe anche non essere uguale) a livello globale e a livello locale all'interno di un sottoprogramma. Si tratta di due variabili diverse che occupano diversi spazi
di memoria, anche se hanno uguale nome.
Nel caso di omonimia si applica la regola di sovrapposizione e il concetto di shadowing, secondo il quale la variabile locale, durante l'esecuzione del sottoprogramma che la contiene,
"oscura" (maschera) l'omonima variabile più esterna, impedendone la visibilità.
6 Le procedure
La procedura è un sottoprogramma contenente le istruzioni che risolvono uno specifico problema. L'esecuzione di una procedura viene attivata dall'apposita istruzione di chiamata (invocazione). Essa utilizzando il passaggio per riferimento può modificare il valore di variabili
che sono locali in un altro sottoprogramma.
Vediamo subito la differenza con le procedure.
 La procedura permette l'aggregazione di una sequenza di operazioni elementari in
una unica macro-operazione dotata di nome e di argomenti. In quanto costrutto sintattico, la procedura introduce regole di visibilità che nascondono a un osservatore
esterno la sua struttura interna. Semanticamente essa realizza i suoi effetti modificando parametri ricevuti dal chiamante o un ambiente esterno che può consistere in
un insieme di variabili, dispositivi di I/O, file e così via. La procedura è un'astrazione
della nozione di istruzione: non è altro che una istruzione complessa che può essere
utilizzata ovunque possa esserlo una istruzione semplice: il compito principale di
una procedura è quello di modificare il contenuto di locazioni di memoria.
 La funzione può essere vista come una procedura con restrizioni di tipo semantico:
essa non effettua alcuna azione visibile sul mondo circostante, non modifica gli
argomenti ricevuti dal chiamante e realizza i suoi effetti restituendo un risultato. La
funzione è un'astrazione della nozione di operatore e può essere utilizzata in qualunque valutazione di espressione: ha il compito di fornire un valore, anche se non è
escluso che, nel fornirlo, assuma anche i comportamenti della procedura (noi nella
nostra trattazione lo escluderemo!).
Le procedure sono sempre associate a un nome simbolico che viene indicato nella prima
istruzione del sottoprogramma e in ogni istruzione di chiamata del medesimo.
In C++, non esistono le procedure essere sono un caso specifico di funzione. La discussione sulla differenza in C++ dei due tipi di sottoprogrammi è lunga è complessa e non verrà
trattato. Si può comunque utilizzare la sintassi seguente per simulare una procedura.
#include<iostream>
using namespace std;
void nomeprocedura (<lista dei parametri>);
procedura
int main(){
…......................;
nomeprocedura(<listavariabili>);
….....................;
return 0;
}
void nomeprocedura (<lista dei parametri>)
{
….............;
procedura
}
Prototipo della
Chiamata della procedura
Definizione della
dove <ListaParametri> è così strutturata:
 TipoParametro1 parametro1, TipoParametro2 parametro2, …....,TipoParametroN parametroN
void (nel caso non si passino dei parametri).
Stare attenti ai punti e virgola.

Il linguaggio C, concede la massima flessibilità nell'ordine di scrittura dei sottoprogrammi,
che possono essere definiti prima o dopo il programma principale, purché prima del main
venga fatta una dichiarazione "forward" (o prototipo).
7 I parametri
Per rendere efficace l'uso delle procedure bisogna approfondire il concetto di parametro.
I parametri sono oggetti caratterizzati da:
 un identificatore;
 un tipo;
 un valore;
 una posizione;
 una direzione (input/output) che dipende dalla modalità di passaggio del parametro.
Grazie a essi si stabilisce come debba avvenire l'input dei dati (al sottoprogramma) e l'output dei risultati (al sottoprogramma chiamante).
L'identificatore e il tipo dei parametri sono noti al
momento della dichiarazione del sottoprogramma, ma il valore è noto solo all'atto della chiamata. I parametri permettono, quindi, di gestire la
comunicazione del sottoprogramma con l'esterno. All'atto della chiamata del sottoprogramma
occorre specificare i parametri attuali, ossia le
informazioni reali che devono essere trasmesse
a esso. I valori di tali parametri saranno accolti
dal sottoprogramma per mezzo dei parametri
formali dichiarati nell'intestazione.
Ciò vale anche per le funzioni.
Un sottoprogramma parametrizzato lavora con variabili fittizie nel nostro caso i parametri
formali a e b), che vengono collegate al programma principale solo al momento della chiamata del sottoprogramma. È per questo motivo che tali parametri vengono detti formali.
Per i parametri attuali occorre indicare solo il nome; per i parametri formali, invece, è necessario indicare il nome e il tipo. È importante tenere presente che il numero, il tipo e l'ordine
dei parametri attuali devono essere sempre uguali a quelli dei corrispondenti parametri formali. Nel nostro esempio, infatti, la chiamata della procedura associa il parametro attuale p
di tipo Intero al parametro formale a (anch'esso, ovviamente, di tipo intero) e il parametro
attuale q di tipo intero al parametro formale a di tipo intero.
I parametri attuali e i parametri formali possono anche avere casualmente lo stesso nome,
ma si consiglia di utilizzare nomi diversi (per evitare inutili confusioni).
Spesso, all'atto della dichiarazione di un sotto- programma, nasce il problema della scelta
dei parametri. Suggeriamo di rispettare le seguenti regole:
1. identificare i dati essenziali e provenienti dall'esterno di cui il sottoprogramma ha bisogno;
2. identificare l'output che il programma chiamante vuole ottenere dal sottoprogramma.
8 Passaggio di parametri
Con passaggio o trasmissione dei parametri intendiamo l'operazione con la quale il valore
dei parametri attuali viene associato (trasmesso) a quello dei parametri formali.
Il passaggio dei parametri può avvenire secondo due distinte modalità, ognuna rispondente
a esigenze diverse:
1. passaggio per valore o per copia;
2. passaggio per indirizzo o referenza.
Nel passaggio dei parametri per valore si ha soltanto una copia dei valori dei parametri attuali nei rispettivi parametri formali. Durante l'esecuzione del sottoprogramma, qualsiasi
modifica apportata ai parametri formali sarà visibile solo all'interno del sottoprogramma e
non verrà riportata sui parametri attuali (che continueranno, così, a conservare il valore iniziale). I parametri formali vengono considerati come variabili locali il cui valore, al ritorno dal
sottoprogramma, si perde.
Nel passaggio dei parametri per valore, all'atto della chiamata del sottoprogramma, viene
allocata un'area di memoria utilizzata per contenere i parametri formali. Si ha, così, una du plicazione dei dati relativi ai parametri. Al passaggio, i parametri formali verranno inizializzati con il valore dei rispettivi parametri attuali. In questo modo, il processore opera su questa
nuova area di memoria lasciando inalterati i parametri attuali. Al rientro dal sottoprogramma, quest'area viene rilasciata, proprio come avviene per le variabili locali (a cui i parametri
formali sono assimilabili.
In C++ il passaggio per valore avviene di default
Vediamo come funziona la seguente procedura per l'ordinamento di due variabili.
#include <iostream>
using namespace std;
void scambia(int x,int y);
int main ()
{
int a,b;
cout<<"inserisci a";
cin>>a;
cout<<"inserisci b";
cin>>b;
scambia(a,b);
cout<<"i numeri ordinati sono"<<a<<" "<<b;
return 0;
}
void scambia(int x,int y)
{
int temp;
temp=x;
x=y;
y=temp;
}
È evidente che il programma, pur essendo corretto in ogni suo punto, non risolve il problema dato: le variabili A e B, infatti, non hanno subito il dovuto scambio. Il motivo per il quale
l'algoritmo precedente non produce il risultato corretto è dato dal fatto che è stato utilizzato
un passaggio di parametri per valore. Per questo tipo di problemi, quando cioè vogliamo
che il sot toprogramma restituisca il valore dei parametri formali (cioè alteri il valore dei pa rametri attuali), dobbiamo servirci di un passaggio per indirizzo.
Nel passaggio dei parametri per indirizzo i parametri formali contengono l'indirizzo di memoria dei parametri attuali. Di conseguenza, una modifica dei parametri formali provoca
una modifica dei corrispondenti attuali. Con questo tipo di passaggio, quindi, si può perdere
il contenuto originale dei parametri attuali.
In C++ il passaggio per indirizzo viene indicato anteponendo il simbolo & al parametro o
alla lista di parametri formali interessati
Tecnicamente, il passaggio per indirizzo differisce da quello per valore in quanto, all'atto
della chiamata del sottoprogramma, non viene allocata nessuna area di memoria per valori
dei parametri formali. Poiché essi si riferiscono alla stessa area di memoria allocata per i
parametri attuali, la stessa cella di memoria sarà individuabile con due nomi distinti (alias).
In questo tipo di passaggio non viene trasmesso il valore dei parametri attuali, bensì l'indirizzo della cella di memoria a essi assegnata; di conseguenza, la modifica di un parametro
comporterà la modifica dell'altro.
Consideriamo l'esempio precedente, ma questa volta effettuando un passaggio per indirizzo.
#include <iostream>
using namespace std;
void scambia(int &x,int &y);
int main ()
{
int a,b;
cout<<"inserisci a";
cin>>a;
cout<<"inserisci b";
cin>>b;
scambia(a,b);
cout<<"i numeri ordinati sono"<<a<<" "<<b;
return 0;
}
void scambia(int &x,int &y)
{
int temp;
temp=x;
x=y;
y=temp;
}
La tabella seguente illustra la situazione della memoria durante l'esecuzione.
Ora il risultato è quello che attendevamo!
Concludendo, vediamo quali sono le regole generali da seguire per scegliere la modalità del
passaggio dei parametri.
 È opportuno effettuare un passaggio per valore quando l'informazione è solo di input
per il sottoprogramma.
 È opportuno effettuare un passaggio per indi- rizzo quando vogliamo la restituzione
del valore del parametro formale (parametro di output),
Riportiamo di seguito alcune utili regole da seguire per una corretta progettazione di procedure con parametri.
 Le procedure devono comunicare con l'esterno esclusivamente tramite parametri.
 Non si deve utilizzare un numero eccessivo di parametri (se ciò dovesse accadere,
forse sarebbe utile rivedere la progettazione).



Le procedure non devono utilizzare le variabili globali e, in particolar modo, non devono modificarle
Le procedure che risolvono un certo problema non devono usare al loro interno istruzioni di input/output: devono comunicare i dati solo attraverso i parametri.
La gestione dell'input/output deve avvenire tramite apposite procedure.
9 ESERCITAZIONE N. 7 (Funzioni)
1. Scrivere una funzione che visualizzi sullo schermo:
CORSO DI LAUREA IN INFORMATICA
ESAMI
1. Analisi
2. Algebra
3. Programmazione
4. Fondamenti
Scegliere un esame:
e quindi renda al programma chiamante la scelta effettuata. Visualizzare dal
main la scelta.
2. Scrivere un programma che legge due numeri interi a e b diversi da zero.
Inserire in esso una funzione che riceva i due interi letti e renda
0 se a e b hanno lo stesso segno
1 se a e b hanno segno differente
Visualizzare dal main il risultato.
3. Inserire nel programma precedente una seconda funzione che riceva i due
interi a e b ed un altro intero x e renda:
a+b per x=1
a-b per x=2
a*b per x=3
a/b per x=4
0 in tutti gli altri casi.
Visualizzare dal main i risultati resi.
4. Scrivere un programma che legge due numeri maggiori di zero. Inserire in esso
una funzione che riceva i due interi letti e renda il valore maggiore.
Visualizzare dal main il risultato.
5. Un polinomio di grado 2 in x e’ espresso da ax 2+bx+c.
I coefficienti a, b, c assumono valori interi.
Scrivere una funzione che riceve a, b e c ed un valore x e calcola il valore del
polinomio in x.
Modificare la funzione in modo da tabulare (calcolare e visualizzare) i valori
dell’incognita nell’intervallo [0, 2] con passo 0.1.
Per migliorare la precisione potete usare il tipo double per x e per il valore
calcolato dalla funzione.
6. Scrivere un programma che genera casualmente un intero a compreso tra 2 e 6,
e un intero b compreso tra 1000 e 2000.
Costruire una funzione che riceve a e b e calcola e visualizza
o Il valore delle potenze di a con esponente intero > 1 che sono inferiori o
uguali a b
o Quante sono queste potenze.
Il numero delle potenze dovrà essere restituito e visualizzato dal main.
7. Scrivere un programma che definisce tramite una define la costante P=3.141 .
Costruire una funzione che calcoli il valore di Pigreco tramite approssimazione
della seguente somma
Pigreco = 4 - 4/3 + 4/5 - 4/7 + 4/9 – 4/11 …
e restituisca al main quanti termini della serie sono necessari per raggiungere il
valore P.
8. Due classi (corso A e B) hanno 20 alunni. Si vuole saper quale classe ha la media
dei voti in Informatica più alta. Creare un programma contenente le seguenti
funzioni:
o una funzione che non riceve nessun valore e restituisce la media di una
classe
o nuna funzione che ricevute le due medie restituisca il nome del corso che ha
la media più alta.
Visualizzare dal main il nome del corso che ha la media più alta.
9. Una società ha tre filiali (A, B, C) ognuna con un numero di dipendenti diverso,
ognuno con uno stipendio diverso. Si vuole sapere il totale degli stipendi di ogni
singola filiale e il totale degli stipendi di tutte le filiali. Scrivere un programma
utilizzando
o Una funzione che riceve il nome della filiale e il numero dei dipendenti e
restituisca la somma degli stipendi di quella filiale. Inoltre la funzione stampi il
nome della filiale e la somma degli stipendi.
O Una funzione che ricevute tutte le somme restituisca la somma totale degli
stipendi.
Visualizzare dal main la somma totale degli stipendi.
10 ESERCITAZIONE N. 9 (Procedure, passaggio per indirizzo)
1. Scrivere una funzione che riceve il valore del raggio di un cerchio e calcola
restituendolo al main il valore della circonferenza e dell’area del cerchio.
Visualizzare dal main i valori resi.
2. Scrivere un programma contenente una funzione che generi casualmente 50
numeri positivi e restituisca al main il numero dei pari e dei dispari.
3. Scrivere un programma contenente una funzione che riceve la misura delle due
basi e dell’altezza di un trapezio isoscele e restituisce il perimetro e l’area del
trapezio. Costruire anche una seconda funzione che calcoli la misura del lato
obliquo.
4.
In una pizzeria si vendono pizze, bibite e dolci. Il listino è:
•
Pizze semplici 2,5 €
•
Pizze farcite 3 €
•
Bibite 3 €
•
Dolci 6 €
Scrivere un programma che acquisito nel main il numero dei prodotti acquistati
da tre clienti (inserire le quantità per singolo cliente, utilizzare un ciclo per
calcolare le quantità totali), calcoli il costo totale e quello singolo dei dolci.
Inoltre si calcoli quale sarebbe il costo totale se fosse attiva seguente
promozione:
•
•
se il numero delle pizze e delle bibite è uguale e si è speso più di 20 € si fa
uno sconto di 5 €
altrimenti fa pagare tutti i dolci 4 €
I risultati devono essere restituiti al main e visualizzati solo nel main.
5. Un comune vuole calcolare quali sarebbero le entrate possibili se fosse
introdotta una nuova tassa sui 50 tra terreni e case accatastati. Scrivere un
programma contenente una procedura che
•
Generi casualmente un numero 0 o 1. 0 corrisponde a casa e 1 a terreno.
•
Se viene generato un terreno si generi un numero casuale intero positivo
minore di 400 m2 che rappresenta i metri quadri del terreno.
•
Conti il numero delle case, dei terreni con metratura minore di 200 m 2 e di
quelli con metratura maggiore. Tali risultati vanno resi al main.
Scrivere una funzione che calcoli le possibili entrate sapendo che la tassa è
pari a:
•
20 € per i terreni inferiori a 200 m2
•
40 € per i terreni maggiori di 200 m 2
•
50 € per ogni casa.
6. Scrivere un programma per la gestione di un conto corrente. Sul conto possono
essere svolti tre tipi di operazioni: 1-versamento, 2-prelievo, 3-emissione
assegni.
Dopo aver introdotto il saldo iniziale, si potranno inserire le varie operazioni
digitandone il tipo e l’importo (numero positivo) su cui eseguire l’operazione.
Ogni operazione sarà gestita da una funzione o procedura diversa diversa.
In seguito, a richiesta dell’utente, il programma dovrà fornire:
•
Il numero dei versamenti effettuati e la somma totale versata;
•
Il numero dei prelievi effettuati e la somma totale prelevata;
Il numero degli assegni emessi e la somma totale prelevata per mezzo degli
stessi;
•
Il saldo finale.
•
7. In una biblioteca si possono affittare fino a 10 libri per cliente. I maggiorenni
pagano 2 € a libro, i minorenni 1 € a libro. Creare un programma che permetta la
gestione della libreria. Si costruisca una funzione che permetta di inserire l’età
dei clienti e il numero di libri presi e restituisca:
•
Il numero totale dei libri presi,
•
Il numero dei libri presi dai maggiorenni.
•
Il numero dei libri presi dai minorenni.
Si costruisca una seconda funzione che calcoli il guadagno totale della libreria.
I risultati devono essere visualizzati nel main.
11 Giochi
1. Ottimizzare il gioco del settemezzo utilizzando le funzioni.
2. Fra amici:
Un giocatore a turno tiene banco e ha a disposizione un mazzo di carte da
quaranta che deve mescolare piu’ volte e far levare a turno ai compagni partendo da sinistra.
Deve essere fissata una somma. Il banchiere distribuisce una carta scoperta a tutti partendo dal compagno di destra, poi ne assegna una anche a sè.
Chi ha avuto una carta di valore uguale a quella del banchiere non vince e non
perde, chi invece ne ha una di valore superiore (il seme non conta) riceve una
somma dal banchiere; chi ne ha una di valore inferiore paga una somma.
L’asso ha il valore più alto, il due quello più basso.
Nel caso venga usato un mazzo di carte da 52, dopo l’asso valgono in misura
decrescente il re, la donna, il fante, il dieci, il nove, l’otto, il sette, il sei, il
cinque, il quattro il tre il due.