Lezione 1 - Imperativo o procedurale - Home

Lezione 1 - Imperativo o procedurale
Corso — Programmazione
— Paradigmi di programmazione
Marco Anisetti
e-mail: [email protected]
web: http://homes.di.unimi.it/anisetti/
Università degli Studi di Milano — Dipartimento di informatica
Paradigmi di programmazione
• Un paradigma di programmazione è un modello
concettuale che fornisce la “struttura” di un programma.
• I principali paradigmi di programmazione sono:
Programmazione
Programmazione
Programmazione
Programmazione
imperativa o procedurale
funzionale
logica
orientata agli oggetti
Paradigma imperativo o procedurale(1)
• Programmazione imperativa: Sono linguaggi ``action
oriented'' ovvero la computazione è vista come una
sequenza di azioni.
• Un programma è composto da istruzioni che realizzano
trasformazioni di stato.
• Uno stato è identificato da tutti i valori di un certo insieme
di variabili in un certo stadio dell'esecuzione.
• Esempi C, Pascal etc.
• E' il paradigma che abbaimo usato fino ad ora
Paradigma imperativo o procedurale(2)
• L' assegnazione è l'elemento fondamentale
• Il programma che scriviamo non è la stessa cosa della
computazione che avviene (elementi strutturati e
invarianti)
• Rapporto tra programma statico ed esecuzione dinamica
• Invarianti e blocchi strutturati
• Invarianti servono soprattutto per pensare al programma
non solo per valutarne la correttezza a posteriori
Paradigma imperativo o procedurale:
esempio
• Esempio di problema affrontato con la programmazione
imperativa
• Consideriamo il problema della ricerca lineare in un array.
• La ricerca lineare prevede di scorrere tutto l'array e di
fermarsi se trovo l'elemento
• Quindi ho due test per determinare quando il programma
si arresta
• Provate a scrivere lo pseudocodice relativo avrete diverse
possibilità contenenti due test
[esempio]
while (esistono altri elementi da valutare) do
if (elemento trovato in posizione i) then return i
else considera l'elemento sucessivo
endwhile
return (0) element not found
Paradigma imperativo o procedurale(4)
• Il fatto di strutturare diversamente i dati (array) permette
di usare invarianti differenti
• Di consequenza permette di sviluppare programmi
differenti
• Approccio con sentinella: considero l'array con elementi
che partono dal secondo elemento mentre nel primo
metto l'elemento da cercare
[esempio con sentinella]
A[0]=x; i=n;
while x<>A[i] do
i=i-1
endwhile
return (i)
Lezione 2 - Funzionale parte I
Corso — Programmazione
— Paradigmi di programmazione
Marco Anisetti
e-mail: [email protected]
web: http://homes.di.unimi.it/anisetti/
Università degli Studi di Milano — Dipartimento di informatica
Programmazione funzionale(1)
• Ha le sue radici nel lambda calcolo che Alonso Church ha
inventato negli anni 30
• Un programma è una funzione che viene valutata per
ottenere un risultato
• Il principio della programmazione funzionale pura è: ``il
valore di una espressione dipende solamente dai valori
delle sue sub-espressioni, se ne esistono''
• Quindi gli assegnamenti non sono considerati, dato che
possono cambiare valori alle variabili
• La programmazione funzionale pura è una
programmazione senza assegnamenti
• Non si considerano le tecniche di salvataggio dei valori
della macchina sottostante
• I cambi del valore di una variabile durante la valutazione
di una espressione sono considerati come degli ``side
effect''
• Esempi storici ML, Lisp, Scheme etc.
Programmazione funzionale(2)
• La potenza di questo paradigma stà nell'uso diretto di
funzioni e ricorsione
• Gestione dell'allocazione implicita
• Storage allocato se serve e deallocato se non serve più
(garbage collection)
• Funzioni come ``first-class values''. Le funzioni hanno lo
stesso status di ogni altro valore
• Una funzione può essere il valore di una espressione, può
essere passata come argomento e può essere messa in
una struttura dati o in una variabile, può essere restituita
come valore di ritorno da altre funzioni
Programmazione funzionale(2)
• Le funzioni possono avere dei nomi, come in ogni altro
linguaggio, o essere definite anonimamente (a volte
anche a run-time)
• Esempio Javascript: (function() alert(Hello World!);)();
• Si basa sulle computazione di espressioni
log n: funzione logaritmica applicata a n
2+4: funzione + applicata a 2 e 4
Massimo tra x e y: if (x > y) then x else y (espressioni
contenenti condizioni e definizione di funzioni)
Programmazione funzionale vs imperativa
• Il punto di forza della programmazione funzionale è a
mancanza di effetti collaterali delle funzioni
• Si ha una migliore verifica del codice e migliore
ottimizzazione
• Nella programmazione imperativa avviene il cambio dello
stato del programma attraverso assegnazioni
• Nella programmazione funzionale i valori non vengono
calcolati cambiando lo stato del programma ma
costruendo nuovi stati a partire dai precedenti
• Vedremo un esempio famoso per chiarire questo tipo di
concetto
• Non può per definizione dare luogo a bug a run-time. Al
momento della compilazione o della prima esecuzione o
funziona o non funziona (non esistono eccezioni)
Effetti collaterali di una funzione
• Una funzione ha un effetto collaterale quando modifica un
valore o uno stato al di fuori del proprio scopo
• Esempio: se modifico una variabile globale o una statica,
quando modifico il valore di un argomento, o scrivo su un
file o chiamo funzioni con effetti collaterali
• La programmazione imperativa vive degli effetti collaterali
delle funzioni
• La programmazione funzionale tende a limitarli (più
difficili le operazioni di input output)
Trasparenza referenziale(1)
• La trasparenza referenziale significa che una espressione
(anche una funzione quindi) può essere sostituita con il
suo valore
• Se considero la funzione f(sqr(2),sqr(2)) allora posso
evitare di calcolare due volta sqr(2) se ho la trasparenza
referenziale
• Per avere la trasparenza referenziale una funzione deve
essere senza effetti collaterali e deve essere puramente
funzionale (restituire sempre lo stesso output a fronte
degli stessi input)
Trasparenza referenziale(2)
• A volte si parla anche di high order functions quando una
funzione prende una o più funzioni come input e da come
output una funzione
• Esempio f(x)= x+4, la funzione high order doppia è
definita come: doppia(f, 8) = f(f(8)) = (8 + 4) + 4
• In programmazione imperativa è possibile fare qualche
cosa si analogo però sfruttando delle specifiche
caratteristiche del linguaggio
• Esempio in C si può passare un puntatore ad una funzione
come parametro
Programmazione funzionale: esempio del
linguaggio Little Quilt
• Esempio tratto dal Ravi Sethi < LittleQuilt >
• Le espressioni sono formate dando dei nomi ai valori e
operazioni su di essi
• Nell linguaggio d'esempio si manipola solo un tipo di
valore, ovvero un oggetto geometrico chiamato quilt.
Little Quilt: valori base
• Due oggetti primitivi quadrati chiamati quilt
• Essi hanno:
Direzione o orientazione fissa
Altezza, larghezza e texture
Little Quilt: operazioni
• Operazioni primitive: turn (rotazione oraria di angolo
retto ) e sew (attaccare un quilt alla destra di un altro
della stessa altezza)
• Regole: un quilt è (solamente) un pezzo primitivo,
oppure un pezzo primitivo ruotato oppure un pezzo
primitivo attaccato ad un altro primitivo
Little Quilt: costanti ed espressioni
• Il primo passo nel costruire questo linguaggio è dare i
nomi alle primitive e alle operazioni
• I quilt verranno chiamati a e b rispettivamente
• Le operazioni le abbiamo già nominate turn e sew
• Le espressioni le possiamo definire con la seguente BNF
<exp> ::= a | b | turn(<exp>) | sew(<exp>,<exp>)
• L'espressione sew(turn(turn(b)),a) è valida? se si cosa
produce?
Little Quilt: costanti ed espressioni
Little Quilt: Funzioni definite dall'utente
• Estendere le espressioni permettendo di definire funzioni
da quilts in quilts
• Esempi:
fun unturn(x) = turn(turn(turn(x)))
fun pile(x,y) = unturn(sew(turn(y),turn(x)))
• Una volta definite acquisiscono lo stesso status delle
built-in functions
Little Quilt: Dichiarazioni locali
• Let − expressions o let − bindings permette alle
dichiarazioni di apparire nelle espressioni
• Let <declaration> in <expression> end
• Esempio:
let fun unturn(x) = turn(turn(turn(x)))
fun pile(x,y) = unturn(sew(turn(y),turn(x)))
in
pile(unturn(b),turn(b))
end
Little Quilt: Esempio
• Vediamo un esempio di come creare dei quilt più
complessi usando pile
Little Quilt: nomi per valori definiti
dall'utente
• Per scrivere espressioni lunghe in termini di espressioni
semplici
val < name >=< expression >
• Si usano assieme al let-binding
let val x = E1 in E2 end
• Riscriviamo il pile
let val bnw = unturn(b)
in
pile(bnw,turn(b))
end
Little Quilt: Esempio
• BNF per il linguaggio visto fino ad ora
< exp >::= a|b < exp >::= turn(< exp >)|sew(< exp > , < exp >)
< exp >::= let < declarations > in < exp > end
< declarations >::=< declaration > | < declaration ><
declarations >
< declaration >::= fun < name > (< formals >) =< exp >
< formals >::=< name > | < name > , < formals >
< exp >::=< name > (< actuals >)
< actuals >::=< exp > | < exp > , < actuals >
< declaration >::= val < name >=< exp > < exp >::=< name >
Conclusioni
• Introduzione ai concetti della programmazione funzionale
• Esempio storico del libro di Ravi Sethi
• Introduzione a costrutti di un programma funzionale
• Forma mentale della programmazione funzionale
• Differenze con la programmazione imperativa
Lezione 3 - Funzionale parte II
Corso — Programmazione
— Paradigmi di programmazione
Marco Anisetti
e-mail: [email protected]
web: http://homes.di.unimi.it/anisetti/
Università degli Studi di Milano — Dipartimento di informatica
Tipi: valori e espressioni(1)
• Entriamo più nel dettaglio circa la natura di valori,
funzioni, espressioni e nomi
• Un tipo consiste di un insieme di elementi chiamati valori
assieme a un set di funzioni chiamate operazioni
• Consideriamo dei metodi per definire dei valori strutturati
come prodotti, liste e funzioni
• I valori strutturati come le liste possono essere usati
liberamente come i valori base tipo interi e stringhe
• I valori sfruttano la macchina sottostante ma non
dipendono da essa
< type − exp >::=< type − name > | < type − exp >→<
type − exp >
| < type − exp > ∗ < type − exp > | < type − exp > list
Tipi: valori e espressioni(2)
• I valori strutturati sono utili per ritagliare la nozione di
valore per una specifica applicazione
• In seguito specificheremo la strutturazione come il set di
valori ed i relativi set di operazioni
• Ci concentreremo sulle operazioni per la costruzione e
l'ispezione di elementi di un set
• Tipi base: se i valori sono atomici ovvero se i valori sono
trattati come un tuttuno senza struttura interna
Tipi: valori e espressioni(3)
• Operazioni sui tipi base: Non avendo struttura interna
l'unica operazione definita è quella di confronto per
uguaglianza
Tecnicamente una operazione è una funzione, quindi per
esempio il test di uguaglianza tra interi è una funzione
definita su una coppia di interi a valori nei booleani.
E' un elemento di tipo int ∗ int → bool
• Prodotto di tipi: Il prodotto di due tipi A ∗ B consiste
nelle coppie ordinate scritte come (a,b) con a ∈ A e b ∈ B
Il prodotto di n tipi è una tupla (n-upla)
• Projection functions: Sono associate alle coppie e
servono ad estrarre i due elementi
fun first(x,y) = x
fun second(x,y) = y
Tipi: Liste
• Una lista è una sequenza finita di elementi
• Un tipo A list consiste in tutte le liste di elementi dove
ogni elemento appartiene al tipo A
• Esempio: int list
• Gli elementi di una lista vengono elencati tra parentesi
quadrate e separati con la virgola ['rosso','blu','marrone']
• Il linguaggio funzionale ML prevede operazioni sulle liste
tipo: null(x) che ritorna true se è vuota e false altrimenti
la lista, hd(x) che ritorna l'elemento di testa, tl(x) che
ritorna la lista tranne la testa eccetera
• Esiste anche l'operatore :: è associativo destro e costruisce
la lista indicando testa e coda (1::[2,3])
Tipi: Funzioni
• Il tipo A → B è l'insieme di tutte le funzioni da A a B
• Funzione totale: A → B se è definita per ogni elemento di
A, quindi associa un elemento di B per ogni elemento di A
• A è il dominio e B è il range della funzione
• Funzione parziale: Non è definita per ogni elemento di A
Applicazione delle funzioni e dichiarazione
• L'applicazione dell'insieme A → B prende una funzione f
definita da A → B un elemento a di A e produce un
elemento b di B
• L'applicazione di una funzione f ad un argomento a è
scritta come f a (in ML)
• Abbiamo già visto di cosa son formate le funzioni nella
programmazione imperativa e come vengono usate e
dichiarate
• In programmazione funzionale si tratta solitamente di una
sintassi del tipo: fun <nome> <parametri formali> =
<corpo>
• Esempio: fun successore n = n+1
• L'uso della funzione chiamato applicazione è in forma
prefissa e segue la sintassi <nome> <parametri attuali>
• Esempio: successore (2+3)
Funzione ricorsiva
• Le funzioni ricorsive sono il sale della programmazione
funzionale
• Vale la definizione già vista
• Usiamo le funzioni viste sulle liste per scrivere la funzione
che calcola la dimensione di una lista
• fun lenth(x) = if null(x) then 0 else 1+lenth(tl(x))
Innermost evaluation
• Consideriamo la applicazione <nome> <parametri
attuali>
• Innermost:
Valuto l'espressione in <parametri attuali>, sostituisco il
risultato per i formali nel corpo della funzione
Valuto il corpo
Ritorno il risultato
• Nell'esempio di successore (2+3) calcolo 2+3 e lo
sostituisco a n parametro formale che vale quindi 5
• Ogni valutazione del corpo di una funzione si chiama
attivazione (analogamente al record di attivazione nello
stack)
• Innermost significa che in una serie di funzioni a cascata
si valuta dall'interno es f(g(d(x)))
• I parametri vengono valutati sempre anche se in quello
specifico caso non servono (analogo alla chiamata per
valore)
Selezione
• Il costrutto di selezione viene visto come la possibilità di
valutare selettivamente una parte di una espressione o un
altra
• if <condizione> then <expr1> else <expr2>
• Il valore di questo costrutto è il valore di <expr1> o di
<expr2> a seconda di <condizione> (non ci sono
assegnamenti!)
Outermost evaluation
• I parametri sono valutati solo quando serve
• Outermost:
Sostituzione dei formali con gli attuali nel corpo della
funzione
Valutazione del corpo
Ritorno del valore
• I parametri non sono valutati prima della sostituzione al
posto dei formali ma solo quando serve (da sinistra a
destra)
Esempio la funzione 91
• La funzione 91 è la seguente funzione:
• fun f(x) = if x > 100 then x-10 else f(f(x+11))
• Vediamo cosa succede per il caso outermost e innermost
per la valutazione di f(100)
Esempio innermost
f(100) = if 100>100 then 100-10 else f(f(100+11))
= f(f(100+11))
= f(f(111))
= f( if 111> 100 then 111-10 else f(f(111+11)) )
= f(111-10)
= f(101)
= if 101>100 then 101-10 else f(f(101+11))
= 101 – 10 = 91
Esempio outermost
f(100) = if 100>100 then 100-10 else f(f(100+11))
= f(f(100+11))
= if f(100+11)>100 then f(100+11)-10 else
f(f(f(100+11)+11))
valuto la prima f(100+11)
f(100+11) = if 100+11> 100 then 100+11-10 else
f(f(100+11+11))
= if 111>100 then 100+11-10 else f(f(100+11+11))
= 100+11-10
= 111-10 = 101
f(100)= if (101> 100) then f(100+11)-10 else
f(f(f(100+11)+11))
=f(100+11)-10
=91
Innermost vs. outermost
• Se esitesse una implementazione della tecnica outermost
che riconoscesse il fatto di aver già calcolato f(100+11)
allora si eviterebbe di fare molti calcoli in aggiunta
• In questo esempio una implementazione non ottimizzata
di outermost risulta non efficiente
• Esempio: consideriamo la funzione fun or(x,y) = if x
then true else y
Se ci fosse la chiamata or (A,B) nel caso innermost dovrei
valutare prima A e B poi passarli come parametri formali
Nel caso or(true,B) con B che non termina porta alla non
terminazioni nel caso outermost la sostituzione non
comporta la valutazione quindi verrebbe if true then true
else B
Quindi B non viene valutato e il programma termina
Comportamento simile allo short circuit
Operatori short circuit
• In alcuni linguaggi tipo il funzionale ML esistono operatori
che espressamente fanno uso di questo concetto
• andalso: A andalso B è falsa se A è falso e vera se A e B
sono veri
• orelse: A orelse B è vera se A è vera e falsa se A e B
sono falsi
Scopo lessicale
• Una occorrenza locale o bound di una variabile può essere
rinominata senza cambiare il significato del programma
• Queste due funzioni sono equivalenti fun successor(x) = x
+ 1 fun successor(n) = n + 1
• Le regole per definire lo scopo lessicale usano il codice
vicino alla dichiarazione della funzione per determinare il
contesto dei nomi non locali
• Si tratta di controlli statici
• Esempio fun add(x) = x + y; la y non è un parametro il
suo valore deve essere determinato dal contesto
Scopo lessicale: val bindings
• Consideriamo la seguente forma di espressione con l'usilio
del let
let val x=E1 in E2 end
• L'occorrenza della x alla destra del let si chiama binding di
x
• Tutte le occorrenza di x in E2 si dicono nello scopo di
questo binding
• Esempio: let val x=2 in let val x=x+1 in x*x end end
Scopo lessicale: fun bindings
• Consideriamo: let fun f(x)=E1 in E2 end
• Il binding del parametro formale x è visibile solo nelle
occorrenze di x in E1
• Il binding della funzione f è visibile sia in E1 ( sarebbe
ricorsione) che in E2
Controllo dei tipi
• Come nel caso di un linguaggio imperativo si tratta di un
insieme di regole per associare un tipo ad una espressione
• Inferenza di tipo
• Equivalenza di tipi
• Overloading: un simbolo è overloaded se ha diversi
significati a seconda del contesto (a volte difficile da
risolvere e richiede tipi espliciti)
• Coercion: conversione implicita da un tipo ad un altro
data dal linguaggio
• Polimorfismo, tipi parametrici es le operazioni sulle liste di
elementi di qualsiasi tipo basta che siano liste (operatori
hd e tl visti in precedenza)
Modellare la semantica del binding (1)
• Analogamente alla semantica operazionale con la nozione
di stato posso specificare meglio la nozione di binding
usando una forma premessa/conseguenza più specifica
• E ::= let x = E1 in E2
• Consideriamo due operazione sul contesto o environment
env che mappa un valore su una variabile (analogo al
concetto di scopo)
• bind(x,v,env) è un nuovo contesto che mappa la variabile
x al valore v
• lookup(x,env) è il valore della variabile x nel contesto env
Modellare la semantica del binding (2)
• env → E : v significa che nel contesto env l'espressione E
ha valore v
• env → x : lookup(x,env) significa che nel contesto env la
variabile x ha valore lookup(x,env)
• La regola per l'espressione let si può scrivere come:
env→E1 :v1 bind(x,v1 ,env)→E2 :v2
env→let x=E1 inE2 :v2
• Ovvero il valore dell'espressione let x = E1 in E2 è v2 nel
contesto env se:
E1 vale v1 nel contesto env
E2 vale v2 nel contesto dove x è bound a v1
Grammatica ad attributi per il let
• L'attributo val denota il valore, env per denotare il
contesto che determina il mapping sul valore
E. val := E2 . val ovvero il valore di E è il valore di E2
E1 . env := E. env ovvero il contesto di binding di E1 è lo
stesso contesto di E
E2 . env := bind(x,E1 . val,env) ovvero in E2 x è associata al
valore di E1
Approfondimenti: ricorsione(1)
• Ricorsione lineare: se una attivazione di f(a) può
attivare al più una singola attivazione di f
• Esempio: fun fattoriale(n)= if n=0 then 1 else
n*fattoriale(n-1)
• Fasi della ricorsione:
• Winding: iniziano le attivazioni
• Unwinding: le attivazioni rilasciano il controllo (simile al
LIFO)
Approfondimenti: ricorsione(2)
• Ricorsione tail: se restituisce un valore (senza richiedere
ricorsione) oppure il risultato di una attivazione ricorsiva
• Esempio: fun g(n,a)=if n=0 then a else g(n-1, n*a)
• La ricorsione viene realizzata (crea valore) tutta nella fase
di winding
Conclusioni
• Ci ha permesso di riflettere su molti concetti già visti ed
applicarli ad un paradigma differente
• Apre la mente verso strategie di programmazione più
astratte e lontane dal modo di operare della macchina
• Avvicina a concetti di programmazione ad oggetti moderni
• Si trova in tanti linguaggi attuali che generalmente
ibridano paradigmi differenti (es Javascript Ruby etc)
• Permette di risolvere in maniera molto elegante certi tipi
di problemi e semplifica il debug
• Riflessioni su alcuni esempi di analisi semantica applicata
a costrutti funzionali
Lezione 4 - Logico e orientato agli oggetti
Corso — Programmazione
— Paradigmi di programmazione
Marco Anisetti
e-mail: [email protected]
web: http://homes.di.unimi.it/anisetti/
Università degli Studi di Milano — Dipartimento di informatica
Linguaggi dichiarativi e procedurali
• Procedurali: istruzioni che indicano al calcolatore come
svolgere un determinato compito. Dati - istruzioni risultato
• Dichiarativi: indicano cosa deve essere fatto ma non
come. Lasciano al calcolatore la scelta del come farlo
Programmazione logica(1)
• Descrivere la conoscenza che si ha sul problema piuttosto
che il procedimento che genera la sua soluzione
• Occorre rappresentare bene le entità in gioco in relazione
all'applicazione usando un linguaggio logico
• Il problema è descritto da oggetti o entità, da relazioni tra
loro e da proposizioni che esprimono proprietà logiche
sulle relazioni
• Problema descritto come un insieme di formule logiche
• Sfrutta un linguaggio di natura dichiarativa
• Un programma in questo paradigma è un insieme di fatti e
regole e la sua esecuzione equivale alla realizzazione di
una dimostrazione (deduzione).
• Si forniscono i fatti (informazioni vere) le regole che
compongono i fatti o risultati di altre regole
• Si interroga il programma per trovare la soluzione
Programmazione logica(2)
• Programmazione classica: Algoritmi + Strutture dati =
Programmi
Dipendenza tra dati e algoritmo
• Programmazione logica: Algoritmo = Logica(fatti +
regole) + Controllo (risoluzione)
Dati e controllo indipendenti
• La programmazione logica viene di solito affiancata allo
studio del linguaggio Prolog
Programmazione logica(3)
Fatti:
Every psychiatrist is a person.
Every person he analyzes is sick.
Jacques is a psychiatrist in Marseille.
Interrogazioni:
Is Jacques a person?
Where is Jacques?
Is Jacques sick?
Fondamenti della programmazione ad
oggetti: Raggruppare dati e operazioni
• Scomposizione di problemi in sottoproblemi
• Astrazione: procedurale, modulare, dei dati, di oggetti
• Astrazione procedurale: abbiamo già visto è una delle
prime ad essere state applicate. Permette di concentrarsi
su una sezione del codice alla volta
• Astrazione modulare: è stata sfruttata per molto tempo
prima di concretizzarsi in un paradigma di
programmazione. Abbiamo visto le basi della modularità
associata alla progettazione.
Collezione di dati e procedure che agiscono sui dati. E'
meglio organizzare moduli attorno ai dati che attorno alle
azioni sui dati (astrazione dei dati)
• Astrazione dei dati: è un tipo definito dal
programmatore (stack, albero ecc.).
• Astrazione degli oggetti: è il paradigma di
programmazione orientato agli oggetti
Linguaggio e astrazione
• Supporto del linguaggio: L'astrazione richiede che il
linguaggio di programmazione permetta di nascondere
delle informazioni rendendo il programma più semplice da
leggere
• Un linguaggio supporta uno stile di programmazione se lo
stile può essere espresso, controllato ed implementato in
modo efficiente
• Un linguaggio permette uno stile di programmazione se
posso con qualche trucco arrivare a programmare con i
dettami dello stile ma non ho il pieno supporto del
linguaggio
• La possibilità di nascondere informazioni supporta varie
forme di astrazione e di solito si realizza con la definizione
della visibilità dei nomi
• Alcuni linguaggi supportano nativamente la modularità
come ad esempio il Pascal (unit - interface implementation)
Astrazione procedurale
• Nel momento in cui una procedura è stata definita, la sua
implementazione può essere astratta (non importa più il
suo corpo ma quello che fa e l'interfaccia).
• Comportamento di una procedura: cosa fa vista dal di
fuori
• Implementazione di una procedura: nasconde i
dettagli implementativi di come il comportamento è
ottenuto
• Prendiamo ad esempio un parser che fa anche la
valutazione. Sappiamo che è costituito da uno scanner
(analisi lessicale e divisione in token) e da un parser con
valutazione (valutazione grammaticale e del valore
semantico)
• Il dividere in procedure aiuta la progettazione, i moduli
sono ancora più utili dato che scanner e parser lavorano
su dei dati
Astrazione modulare
• Un modulo è una collezione di dichiarazioni che includono
variabili e procedure
• Il modulo agisce come una black-box con cui il resto del
programma interagisce attraverso la sua interfaccia
• Una interfaccia è un sottoinsieme delle dichiarazioni del
modulo
• Le interfaccie e le implementazioni del modulo possono
considerarsi private o pubbliche
• Ruolo: modulo organizzato attorno ai dati. Il ruolo è
generale non specifico di procedure che sono
implementate nel modulo
• Interfaccia: se il ruolo è chiaro si definisce meglio la sua
interfaccia. Le procedure di interfaccia determinano il
comportamento del modulo per il resto del programma
• Implementazione: Nasconde nelle parti private del
modulo le decisioni (variabili e procedure) che non sono di
interesse per il resto del programma
Tipi - classi - oggetti
• Il programma manipola dei tipi strutturati
• Una classe corrisponde ad uno di questi tipi
• La classe è l'abbreviazione di classe di oggetti
• Un oggetto è una entità che vive a run-time con dei dati
istanziati su cui le operazioni possono agire
• Uno stack è un oggetto i cui dati corrispondono con il
contenuto della stack stesso
Procedure - moduli - classi
• Servono a bisogni differenti e possono essere usati in
combinazione
• Procedure: sono richieste per implementare una
operazione di un modulo o di una classe
• Modulo: può essere usato per partizionare staticamente
il codice di un programma organizzato in classi
• Le differenze non sono nelle attività ma in come sono
organizzate
• Procedure: Definiscono ogni dettaglio ogni
rappresentazione è nota tramite il programma.
• Moduli: Nasconde la rappresentazione, esponendo le
operazioni pubbliche. Il vantaggio rispetto alla procedura
è che la rappresentazione è nascosta e l'accesso è
controllato.
• Classi: Come per i moduli la rappresentazione è nascosta
l'accesso è controllato. Gli oggetti vengono inizializzati
alla creazione
Classe ed eliminazione dei doppi(1)
• Rivediamo l'esempio dell'algoritmo della eliminazione dei
doppi considerando come elementi da confrontare delle
istanze di una classe Entry così definita:
Class Entry
procedure read();
procedure write();
function endmarker():boolean;
function equal(Entry):boolean;
function copy(Entry):boolean;
constructor Entry;
destructor Entry;
Classe ed eliminazione dei doppi(2)
Entry a,b;
a.read();
while not a.endmarker() do
a.write();
do
b.read;
while a.equal(b)
a.copy(b)
endwhile
Programmazione orientata agli oggetti
• Approccio che si integra con quello procedurale ma la
progettazione è radicalmente differente
• Un programma è un insieme di oggetti (astrazioni della
realtà) dotati di proprietà (dati) e metodi (procedure) che
comunicano tramite scambio di messaggi.
• E' un paradigma tanto importante da essere stato
adottato da molti dei linguaggi più recenti che ibridano
diversi paradigmi
• Javascript: OO e funzionale, Ruby fortemente OO
(aggiungere e modificare metodi a run-time) e funzionale
• Python OO e funzionale, molto usato in scripting
• Uno tra i più usati linguaggi di programmazione ad oggetti
prima dell'avvento di Java è stato C++ che poteva essere
usato per scrivere in modalità procedurale o ad oggetti