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
© Copyright 2024 ExpyDoc