Sottoprogrammi
Un linguaggio senza parametri puo'esistere? Si (es. assembly)
Astrazione
- identificare proprietà importanti di cosa si vuole descrivere
- concentrarsi sulle questioni rilevanti e ignorare le altre
- cosa è rilevante dipende dallo scopo del progetto
Voglio asseganare a un gruppo di istruzioni un nome e fare fare a questo gruppo di istruzioni delle op su dei paramentri che passo. Il codice diventa piu astratto e piu leggibile
Nota: I LP sono essi stessi astrazioni del calcolatore sottostante
Astrazione sul controllo e sui dati
Astrazione sul controllo
Sono per es: sottoprogrammi, blocchi, parametri
double P (int x) {
double z;
/* CORPO DELLA FUNZIONE
return expr;
}

Astrazione del controllo
Fornisce astrazione funzionale al progetto:
- ogni componente fornisce servizi al suo ambiente
- la sua astrazione descrive il comportamente esterno
- e nasconde i dettagli interni necessari a produrlo
Interazione limitata al comportamento esterno
Comunicazione attraverso:
- parametri
- ambiente globale (ma distrugge l’astrazione)
Astrazione sui dati
Tipo di dato = valori e operazioni (integer = [-maxint..maxint] e ` {+,-,*,div,mod}`)
La rappresentazione (implementazione) dei dati delle operazioni inaccessibile all’utente, perché protetta da una capsula che la isola
Nota: impossibile nel linguaggi più vecchi
- FORTRAN, Pascal, C
Parametri
Terminologia
- dichiarazione/definizione
int f (int n) {return n+1}n si dice parametro formale
Nota: Una funz comunica con il chiamante (return)
Modalita' di passaggio dei parametri
Per valore:
- il val dell'attuale e'assegnato al formale, che si comporta come una var locale
- pragmatica: main -> proc
- attuale qualsiasi; modifiche al formale non passano all'attuale
Per riferimento:
- viene passato un riferimento (indirizzo) all'attuale; i riferimenti al formale sono referimenti all'attuale (aliasing)
- pragmatica: main <-> proc
- attuale: variabile; modifiche al formale passano all'attuale
Es passaggio per valore:
void foo (int x) { x = x+1; }
…
y = 1;
foo(y+1);
dopo foo y vale comunque 1.
- Il formale x è una var locale (sulla pila)
- Alla chiamata, l’attuale y+1 è valutato ed il valore è assegnato al formale x
- Nessun legame tra x nel corpo di foo e y nel chiamante
- Al ritorno da foo, x viene distrutto (tolto dalla pila)
- Non è possibile trasmettere info da foo al chiamante mediante il parametro
- Costoso per dati grandi: copia
- Java, Scheme, Pascal (default), C e Java (unico modo);
Es: passaggio per riferimento
void foo (reference int x){ x = x+1;}
…
y = 1;
foo(y);
dopo foo y vale 2.
- viene passato un riferimento (indirizzo; puntatore)
- Il formale x è un alias di y
- L’attuale deve essere un L-valore (“una variabile”)
- Al ritorno da foo, viene distrutto il (solo) legame tra x e l’indirizzo di y
- Trasmissione bidirezionale tra chiamante e chiamato
- Efficiente nel passaggio, ma indirezione nel corpo
- Pascal (var); in C simulato passando un puntatore…
Passaggio per risultato:
simile al passaggio per riferimento.
void foo (result int x) {x = 8;}
…
y = 1;
foo(y);
- Il formale x è una var locale (sulla pila)
- Al ritorno da foo, il valore di x è assegnato all’attuale y
- Nessun legame tra x nel corpo di foo e y nel chiamante
- Al ritorno da foo, x viene distrutto (tolto dalla pila)
- Non è possibile trasmettere info dal chiamante a foo mediante il parametro
- Costoso per dati grandi: copia
- Ada: out
Passaggio per valore/risultato:
Insieme valore+risultato. Pragmatica: main ↔ proc
Esiste(-va) in Algol-W: void foo (value-result int x) { x = x+1; } … y = 8; foo(y); qui y vale 9 • Il formale x è a tutti gli effetti una var locale (sulla pila) • Alla chiamata, il valore dell’attuale è assegnato al formale • Al ritorno, il valore del formale è assegnato all’attuale • Nessun legame tra x nel corpo di foo e y nel chiamante • Al ritorno da foo, x viene distrutto (tolto dalla pila) • Costoso per dati grandi: copia • Ada: in out (ma solo per dati piccoli; per dati grandi passa riferimento)
Valore e riferimento: morale
Passaggio per valore: • semantica semplice: il corpo non ha necessità di conoscere come la procedura verrà chiamata (trasparenza referenziale) • implementazione abbastanza semplice • potenzialmente costoso il passaggio; efficiente il riferimento al parametro formale • necessità di altri meccanismi per comunicare main ← proc • Passaggio per riferimento: • semantica complessa; aliasing • implementazione semplice • efficiente il passaggio; un po’ più costoso il riferimento al parametro formale (un indiretto)
Valore, e non riferimento
I vantaggi del passaggio per valore:
- in particolare la trasparenza referenziale suggeriscono linguaggi con passaggio solo per valore più meccanismi separati per ottenere il passaggio per riferimento: puntatori in C variabili in modello a riferimento (tipi classe) in Jav
Passaggio per nome
Regole di copia:
- una chiamata alla procedura P e' la stessa cosa che eseguire il corpo di P dopo aver sostituito i parametri attuali al posto dei parametri formali
La macro espansione viene realizzata in modo semanticamente corretto In algol-w era il default:
int y;
void foo (name int x) {x= x + 1; }
…
y = 1;
foo(y);
foo esegue y=y+1
Caso piu complicato
conflitto di variabili!
Le due variabili y e y rossa sono diverse. fie incrementa l'attuale (y) attraverso il formale x e crea e distrugge la nuova y rossa
Soluzione:
Viene passata una coppia: <exp, amb>
• exp è il parametro attuale (testo, non valutato)
• amb è l’ambiente di valutazione (in scoping statico)
• Ogni volta che il formale è usato, exp è valutata in amb

Altro esempio:
void fie (int x, int y) {
x = x+1;
y = 1;}
…
int i = 1;
int[] A = new int[5];
A[1]=4;
fie (i,A[i]);
Passaggio per nome
il corpo della proc viene riscritto al posto della chiamata e sostituisco le var con quelle in input
La i e A[i] sono valutate nel main (il loro amb)
quand arrivo al punto A[i] (ovvero y) = 1 visto che prima i e' stato incrementato allora andro' a modificare anche la i di A[i] quindi diventa A[2] = 1
f(nome x)
y = x
y = x
y = x
z = 1
f(z++)
con passaggio per nome la sostituzione viene fatta ogni volta che viene trovato il param corrispondente
quindi diventa:
f(nome x)
y = z++
y = z++
y = z++
z = 1
f(z++)
quindi y diventa 4 (o 3, in base a quando viene fatto z++)
Implementazione passaggio per nome
Come passare la coppia <exp,env>? – un puntatore al testo di exp – un puntatore di catena statica (sullo stack) al record di attivazione del blocco di chiamata
cioè una chiusura (perché chiude un’espressione: elimina le sue variabili libere legandole nell’ ambiente del chiamante)
Le chiusure servono a passare funzioni come argomenti ad altre procedure Parametro per nome = funzione nascosta senza argomenti che valuta il parametro nell’ambiente del chiamante (un thunk, nel gergo Algol)

Funzioni di ordine superiore
Alcuni linguaggi permettono di: – passare funzioni come argomenti di procedure – restituire funzioni come risultato di procedure • Entrambi i casi: come gestire l’ambiente della funzione ? • Caso più semplice – Funzioni come argomento – Occorre un puntatore al record di attivazione all’interno della pila: passa una chiusura ! • Caso più complicato – Funzione restituita da una chiamata di procedura – Occorre mantenere il record di attivazione della funzione restituita: disciplina a pila non funziona!!

in scope dinamico la x dentro la f puo velere 7 (se pero si valuta lo scope di f quando viene chiamata e non quando viene passata) quindi guardo il suo ambiente quando viene chiamata non passata. Questa regola si chiama regola di shallow binding
Altrimenti (nel caso in cui io valuti l'ambiente della funz quando essa viene passata e non quand o viene chiamata), ovvero alla riga g(f) quindi la x vale 5. Questa regola si chiama deep binding