Lezione 1 - Imperativo o procedurale Corso — Programmazione Linguaggi di 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 abbiamo 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 successivo 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 conseguenza 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 Linguaggi di 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) • 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 Lezione 3 - Logico e orientato agli oggetti Corso — Programmazione Linguaggi di 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 interfacce 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