Appunti di Programmazione Avanzata PDF

Document Details

Università Politecnica delle Marche

2022

Margherita Galeazzi

Tags

programmazione avanzata informatica programmazione appunti universitari

Summary

Questi appunti riguardano la programmazione avanzata, un argomento didattico per studenti universitari del corso all'Università Politecnica delle Marche (UNIVPM). Coprono argomenti come i design pattern, la programmazione funzionale, e il lambda calcolo. Gli appunti sono stati redatti nel 2022.

Full Transcript

Appunti di Programmazione Avanzata Programmazione Avanzata Università Politecnica delle Marche (UNIVPM) 97 pag. Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182...

Appunti di Programmazione Avanzata Programmazione Avanzata Università Politecnica delle Marche (UNIVPM) 97 pag. Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ A.A. 2021/ 2022 Appunti di Programma- zione Avanzata GALEAZZI MARGHERITA Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Sommario Software is everywhere................................................................................................................................. 4 Programmare nel 21esimo secolo................................................................................................................. 4 Il software architect....................................................................................................................................... 5 Software Framework, Application Framework e Inversion of control.......................................................... 5 Design Pattern................................................................................................................................................... 6 Pattern: Singleton (Creational)...................................................................................................................... 7 Definizione di un linguaggio di programmazione (PL)....................................................................................... 8 I paradigmi di programmazione.................................................................................................................... 8 La struttura generale di un interprete è la seguente:................................................................................... 8 Introduzione al 𝜆𝜆-calcolo................................................................................................................................. 10 Un passo indietro…...................................................................................................................................... 10 La programmazione imperativa............................................................................................................... 10 OOP.......................................................................................................................................................... 11 Programmazione dichiarativa: Funzionale e Logica................................................................................ 11 Dichiarativo VS Imperativo...................................................................................................................... 11 Side Effect................................................................................................................................................ 12 Trasparenza Referenziale........................................................................................................................ 12 Programmazione dichiarativa.................................................................................................................. 12 Ricorsione o iterazione............................................................................................................................ 12 Principio di Induzione.................................................................................................................................. 13 Implicazioni pratiche............................................................................................................................... 13 Lamba Calcolo.............................................................................................................................................. 14 λBasi............................................................................................................................................................. 14 Nozioni Base................................................................................................................................................ 14 Astrazione.................................................................................................................................................... 14 Regole...................................................................................................................................................... 14 Valutazione.............................................................................................................................................. 15 Riduzione..................................................................................................................................................... 15 Come si fa la sostituzione?.......................................................................................................................... 16 α-equivalenza.......................................................................................................................................... 16 Sostituzione............................................................................................................................................. 16 Beta Riduzione......................................................................................................................................... 16 Beta conversione..................................................................................................................................... 17 Currying................................................................................................................................................... 17 Normal-order Reduction......................................................................................................................... 18 Booleani… (pensiamo sempre in termini di funzioni)............................................................................. 18 Operazioni logiche (AND / OR / NOT)...................................................................................................... 18 1 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi JavaScript......................................................................................................................................................... 19 Codice nelle pagine web.............................................................................................................................. 19 console.log................................................................................................................................................... 19 Esecuzione di codice JavaScript................................................................................................................... 19 Browser’s sandbox....................................................................................................................................... 19 Statement.................................................................................................................................................... 20 Commenti.................................................................................................................................................... 20 Variabili........................................................................................................................................................ 20 Best practices........................................................................................................................................... 20 Tipi............................................................................................................................................................... 21 Number.................................................................................................................................................... 21 String........................................................................................................................................................ 22 Booleani................................................................................................................................................... 22 Null e Undefined...................................................................................................................................... 23 Istruzioni condizionali (if)............................................................................................................................ 23 L’operatore ternario.................................................................................................................................... 23 Loops: while................................................................................................................................................. 24 Loops: do while............................................................................................................................................ 24 Loops: for..................................................................................................................................................... 24 Break............................................................................................................................................................ 24 Continue...................................................................................................................................................... 24 Switch.......................................................................................................................................................... 24 Funzioni....................................................................................................................................................... 25 Dichiarazione di una funzione................................................................................................................. 25 Parametri di funzione.............................................................................................................................. 25 Ritornare un valore.................................................................................................................................. 25 Funzioni arrow............................................................................................................................................. 25 Oggetti......................................................................................................................................................... 26 Note......................................................................................................................................................... 26 Copia di un oggetto................................................................................................................................. 27 Funzioni utili............................................................................................................................................ 27 Array............................................................................................................................................................ 27 Operazioni sugli array.............................................................................................................................. 28 Note......................................................................................................................................................... 28 OOP e JavaScript funzionale............................................................................................................................ 29 ES6 Classes................................................................................................................................................... 29 Metodi..................................................................................................................................................... 29 Attributi................................................................................................................................................... 30 2 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Instantiation................................................................................................................................................ 30 Extends, super............................................................................................................................................. 30 Hoinsting...................................................................................................................................................... 31 AJAX, XML, JSON – Programmazione asincrona, FETCH e AJAX...................................................................... 32 Introduzione ad AJAX.................................................................................................................................. 32 Programmazione funzionale in JavaScript....................................................................................................... 33 Programmazione funzionale........................................................................................................................ 33 Declinazioni della programmazione funzionale in JavaScript..................................................................... 33 First-class function................................................................................................................................... 33 Callback........................................................................................................................................................ 34 High order functions e Array....................................................................................................................... 35 arr.forEach(callback)................................................................................................................................ 35 arr.find(callback)...................................................................................................................................... 35 arr.findIndex(callback)............................................................................................................................. 35 arr.filter(callback).................................................................................................................................... 35 arr.map(callback)..................................................................................................................................... 36 arr.reduce(callback)................................................................................................................................. 36 Closure e Currying....................................................................................................................................... 37 Closure..................................................................................................................................................... 37 Currying................................................................................................................................................... 39 Funzioni anonime........................................................................................................................................ 39 3 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Software is everywhere Programmare nel 21esimo secolo Il programmatore “moderno” non utilizza tantissimo l’interfaccia grafica ma utilizza molto più spesso le CLI (Command Line Interface). I dati che si trova a dover utilizzare possono provenire da più “sorgenti”, ci si può quindi trovare a dover utilizzare sia dati che sono strutturati (DB), sia dati che non sono strutturati. Una cosa fondamentale è che quando viene creato un progetto dove si ha la parte di back-end intesa proprio come interfaccia con il mondo esterno (REST API) e la parte più interna che è la parte dei dati, si ha un pro- blema, perché prima di testarlo, devo popolarlo. Tramite docker all’avvio del container verrà lanciato uno script che si chiama seed che si occupa di fare due cose: 1. Crea il database; 2. Popola il database con i dati di mock. Un altro fattore importante è la scalabilità ovvero all’aumentare degli utenti il programma deve rimanere reattivo, se il programma si trova sul cloud ci pensa il servizio stesso (tipo amazon aws), altrimenti se è il programmatore a gestire il server deve operare in maniera differente. Lo sviluppo del software è un’attività di gruppo, quindi si utilizzerano strumenti quali git per condividere il lavoro. Molto importante è anche la responsività del programma, ovvero in alcuni casi le applicazioni sono sviluppate per determinati device, mentre l’altra possibilità (che è migliore) è quella di andare ad utilizzare framework che garantiscono la responsività da parte delle applicazioni. Quindi se si vuole sviluppare un’applicazione completa (full-stack), bisogna fare sia back-end che front-end, il che significa utilizzare tanti linguaggi. Ad esempio per il front-end (insieme alle varie librerie connesse): html; javascript; css; typescript; … Ed altrettanti linguaggi per il back-end e per i database. Il problema diventa quindi quello che non è più sufficiente saper programmare in una sola maniera, ma biso- gna saper programmare con approcci diversi. 4 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Un possibile approccio è l’OOP (Object-Oriented Programming), che risulta molto adatto per certi tipi di ap- plicazioni, ma meno per altre. È importante nella programmazione favorire la riusabilità in quanto se un componente (sia lato front-end che lato back-end) può essere riutilizzato si riduce ampliamente il tempo di sviluppo. Ad esempio lato front-end, si può citare il material design di angular. Sfruttando la riusabilità non si deve riscrivere tutte le volte il modulo relativo ad un componente grafico; lo si scrive una volta e lo si shara (lo si condivide) con tantissime applicazioni. Nota che ogni componente ha sia la parte TypeScript (la parte di codice che da “istruzioni”) che la parte html (di renderizzazione che dice come i dati devono essere visualizzati). Il software architect Ha un ruolo strategico, perché si occupa di progettare l’architettura, sulla quale poi tutti andranno a svilup- pare il codice. È un compito molto complicato perché le grandi organizzazioni sono molto strutturate. Software Framework, Application Framework e Inversion of control I software framework sono quelli che danno una “common code” ovvero danno funzionalità generiche che l’utente può andare a customizzare (sono possibili override e le specializzazioni). Gli application framework, quando un utente utilizza la parte di software framework per creare poi delle applicazioni. L’inversion of control consiste a partire dalla presenza di un framework nell’adattare lo sviluppo al dato fra- mework (tradizionalmente prima si progetta e poi si sviluppa). Il programmatore è quindi guidato lungo le scelte che dovrà prendere dal framework (è comodo perché chi ha sviluppato il framework lo ha fatto in maniera intelligente e quindi si avrà uno sviluppo del codice maggiormente ottimizzato). Questo consente di avere un approccio unico, i programmatori programmano tutti nello stesso modo e se- guono le stesse filosofie di sviluppo (aspetto alquanto importante). Alcuni esempi di framework sono: General software framework:.NET, Android SDK, Spring, Cocoa, Eclipse, … Framework per applicazioni con GUI: MFC, Gnome, Qt (una delle librerie per gestire applicazioni grafiche più ampia che esiste al giorno d’oggi. È CROSS-PLATFORM), … Web Application Framework (basati sul design pattern Model-View-Controller): ASP.NET, GWT, Rails, … Concorrenza: Handoop Map/Reduce Nota che Eclipse è un framework (e non solo un ambiente di sviluppo), perché può essere customizzato da parte degli utenti per realizzare delle applicazioni che non centrano nulla con lo sviluppo di codice. Questo perché è in modo di gestire progetti, gestire elementi, che vanno a finire all’interno di finestre di visualizza- zione. 5 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Design Pattern Quando si parla di design pattern la questione diventa più complicata, in questo caso si hanno delle compo- nenti che devono parlare tra di loro (ovvero una componente può dover invocare i metodi di un'altra com- ponente), si possono a tal fine utilizzare differenti Design Patterns: Observer, Publish-Subscribe il dato nuovo viene propagato a tutti quelli che ne sono interessati ovvero il publisher è una componente che pubblica un messaggio ed il/i subscriber è/sono una/delle entità software che si mettono in ascolto di quel messaggio  si utilizza questo approccio se un dato è di interesse per più parti, Visitor, Template Method, … Di pattern ne esistono tantissimi, di fatto sono 253, noi non li vedremo tutti ma solamente alcuni che risul- tano per i nostri scopi di particolare interesse. I pattern di fatto ci consentono di realizzare i seguenti obiettivi: Astrazione; Incapsulamento; Informazioni nascoste (information hiding); Separazione; Composizione e coesione; Separazione dell’interfaccia e dell’implementazione; Singolo punto di riferimento; Divide et impera. Sono tutti termini che compaiono pure nella programmazione ad oggetti. Altri requisiti non funzionali soddisfatti sono: L’interoperabilità; L’efficienza; La robustezza; La riusabilità; La testabilità; L’affidabilità. Questi sono i pattern che più frequentemente vengono utilizzati, che sono suddivisi in Creational, Behaviou- ral e Structural: 6 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Pattern: Singleton (Creational) Questo pattern lo si utilizza perché in alcune applicazioni è di interesse l’esistenza di una singola istanza di una data classe. Se ad esempio si dovesse fare una connessione ad un database, se ne deve avere appunto una sola allora la si crea come Singleton, non si fanno connessioni multiple ad un database all’interno di una applicazione per diversi motivi: 1. Se ho un Singleton posso gestire la transnazionalità delle varie operazioni; 2. Le connessioni database sono costose (portano via numerose risorse), quindi se ne fa una sola. La struttura del Singleton: osservando la classe singleton si può vedere che si ha la possibilità di avere il che ritorna l’object identifier e si ha poi il co- struttore privato , il costruttore è privato perché questo è una sorta di meccanismo di blocco che previene la creazione da parte dell’utente di più istanze di quella classe. Il metodo (il + sta a significare che è pubblico), se l’og- getto è già stato creato ritorna ap- punto la ovvero il ri- ferimento all’oggetto, altrimenti viene creata l’istanza, in sintesi è un modo per la creazione di un Singleton (quindi restituisce sempre una , se non esiste la crea). Si mostra di seguito un esempio di codice che rispecchia la situazione vista prima: La debolezza del singleton sta nel suo utilizzo in applicazioni multithread, in queste l’utilizzo del Singleton da solo non basta e va quindi utilizzato con altri pattern quali Mediator, Servant e così via. Per specificare che una classe può avere una sola istanza, si può fare ereditare questa da Singleton. In questo modo si possono controllare gli accessi ad una singola istanza di un oggetto tramite l’incapsulamento Single- ton. Può essere modificata per consentire ad una classe di avere un dato numero (finito) di istanze. 7 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Definizione di un linguaggio di programmazione (PL) Na prima nota è che il codice che andremo a scriver in JavaScript è cross-platform, questo perché è un lin- guaggio interpretato, ovvero c’è di mezzo un interprete ovvero una componente aggiuntiva che prende il set di istruzioni scritte nel dato linguaggio e le traduce in un “linguaggio intermedio” che poi è interpretabile dal calcolatore; non è quindi già in linguaggio macchina. Un linguaggio di programmazione è definito da sintassi, semantica e paradigmi. La sintassi è ciò che concerne la forma del programma: ovvero come le espressioni, i comandi, le dichiarazioni e gli altri costrutti devono essere “arrangiati” per avere un programma ben-formato; La semantica è ciò che concerne il significato di un programma ben-formato, ovvero come ci si aspetta che un programma si comporti quando è eseguito su un computer; I paradigmi sono ciò che concerne con il modo in cui si intende utilizzare il PL nella pratica. Se fino a qualche hanno fa il cross-platform era un must-have, al giorno d’oggi questa necessità si è ridotta (è diventato più un nice-to-have che un must-have) perché grazie a docker, si possono virtualizzare le appli- cazioni in pochissimo tempo. I paradigmi di programmazione Un paradigma è uno stile di programmazione, caratterizzato da una particolare selezione di concetti chiave e astrazione. Programmazione imperativa, il programmatore decide (impera) sulla macchina ovvero da una serie di comandi che la macchina esegue (le componenti sono variabili, comandi, procedure, …, non si hanno di default meccanismi di protezione); Programmazione orientata agli oggetti (le componenti sono oggetti, metodi, classi, …); Programmazione concorrente, è di nostro interesse la programmazione concorrente ad alto livello quindi si sale nel livello di astrazione (async, await, …); Programmazione funzionale, qui si vedranno alcune cose particolari; Programmazione logica. La classificazione dei linguaggi in base ai paradigmi può indurre in errore. La struttura generale di un interprete è la seguente: In sostanza questa è la ge- stione di un interprete: FETCH, DECODE, FETCH OPERANDS (si sceglie effet- tivamente quello che s do- vrà fare), EXECUTE e si STORA il risultato. Questo se ci si pensa bene lo si fa per un interprete ma lo si fa anche a basso livello si pensi ad esempio al ciclo base della macchina di von Neumann. Quindi quanto appena detto è valido a basso li- vello, tanto quanto è valido ad un livello più alto quale può essere di fatto l’esecuzione di un bytecode. 8 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Quello che accade (nella JVM) è in sostanza questo: il ciclo do-while della macchina virtuale gira fino a che ci sono istruzioni da eseguire a differenza della macchina nella quale il ciclo do-while va avanti fintanto che la macchina è accesa. Si chiama macchina virtuale perché è come se fosse uma mac- china di von Neumann (un calcolatore) solo che è fatta virtual- mente quindi all’interno della macchina virtuale vengono ese- guite FETCH e EXECUTE. Un’altra cosa fondamentale sono le memorie, quelle di princi- pale interesse per noi sono heap e stack, lo stack è quella che è più limitata. Vedremo poi che nella programmazione funzionale quasi tutto quello che è iterativo diventa ricorsivo, ma questo va in contrasto con quanto imparato fino ad oggi, ovvero che la ricorsione non è ottimale. Bisogna però ricordarsi che la ricorsione tail, ovvero quanto la chiamata a sé stessa avviene come ultima operazione, si trasforma in iterazione. Quindi i compilatori nell’ottimizzazione si accorgono che la ricorsione tail può essere trasformata in iterazione e procedono in questa trasformazione. Quindi il fatto che la programmazione funzionale significhi programmare in modo ricorsivo, non significa ne- cessariamente che le prestazioni saranno scadenti, perché se la ricorsione è fatta bene le cose funzioneranno in maniera ottimale. La ricorsione fatta in maniera standard (senza ottimizzazioni), finisce nel record di attivazione quindi si fanno una serie di PUSH, fin tanto che si arriva al caso base. Se non si arriva al caso base prima o poi si avrà uno STACK OVERFLOW  lo stack tende a riempirsi, si riempie, la macchina si accorge che lo stack è pieno (lo stack ha una memoria finita) e si ha un’eccezione che se non gestita fa crashare tutto. Ogni codice scritto con un linguaggio moderno ha un linter, ovvero un modulo che ci consente di verificare se il codice che abbiamo scritto soddisfa le regole stilistiche di un linguaggio. Il linter quindi da una valutazione al codice scritto che sarà più positiva, più si rispettano le regole di scrittura del codice. 9 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Introduzione al 𝜆𝜆-calcolo Il lambda calcolo è in sostanza, un sistema formale in logica matematica per esprimere il calcolo basato sull’astrazione e l’applicazione di funzioni utilizzando l’associazione e la sostituzione di variabili. Risale agli anni ’30 ed è una maniera formale per esprimere un sistema di calcolo. È noto che quando facciamo una funzione questa passa per la valutazione di una espressione, può essere qualcosa di concreto che resti- tuisce effettivamente un valore, oppure può essere qualcosa di fortemente astratto. Di fatto la lambda è una funzione spesso anonima che ha un ingresso ed un’uscita. Noi siamo abituati a pen- sare che quando scriviamo una funzione generalmente si passano dei valori e ci vengono ritornati dei valori. Questo è un approccio che funzione e che ci viene inculcato dal C, dal C++, dal Java, dal Phyton, però non funziona con altri linguaggi. Ad esempio nel caso del JavaScript si vedrà che è in realtà possibile ritornare funzioni ed è possibile anche prendere in ingresso delle funzioni. Questo è un fattore di notevole interesse perché ci da la possibilità di rendere il calcolo funzionale, quindi significa che non si è obbligati a apssare necessariamente un valore, ma si può anche passare una funzione. Un passo indietro… La programmazione imperativa Il paradigma imperativo trae la sua origine dai linguaggi macchina e assemblativi e ricomprende sia la pro- grammazione procedurale che la programmazione ad oggetti. Nel paradigma imperativo i programmi sono sequenze di istruzioni che prescrivono dettagliatamente quali operazioni effettuare e in quale ordine. L’istruzione fondamentale è l’istruzione di assegnamento, la quale stabilisce come modificare il contenuto di una locazione di memoria individuata da un identificatore di variabile (alias). Il controllo del flusso avviene mediante istruzioni di selezione (salti condizionati) e di ripetizione. Nota che esi- stono anche i salti incondizionati (i goto) ma è noto che non sono una buona pratica perché rendono impredi- cibile il comportamento del programma. È supportata la ricorsione. Sono presenti tipi di dati Scalari: per valori numerici, logici, enumerati, caratteri e indirizzi; Strutturati: omogenei, quali array, stringhe e insiemi; eterogenei, quali record e file; Definiti dal programmatore. Le variabili sono allocabili staticamente o dinamicamente. Non si possono creare in runtime delle funzioni (il codice è compilato e quindi abbastanza rigido). Nella programmazione procedurale le istruzioni di un programma sono raggruppate in blocchi, detti proce- dure e si fa ciò per evitare ridondanze di codice. Una cosa importante è però che una procedura è definita una sola volta (nella programmazione ad oggetti questo vincolo è più lasco perché è possibile effettuare l’overload). La definizione di una procedura è basata sui dati di valore ignoto (=parametri formali) che vengono inizializ- zati al momento dell’invocazione della procedura attraverso i cosiddetti parametri effettivi, i quali possono essere passati per valore o per indirizzo. È opzionale l’invocazione di una procedura, ovvero una procedura definita non è invocata per forza. Inoltre una funzione potrebbe non ritornare un valore, questo perché passando un dato per riferimento si opera direttamente sul dato originario. 10 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi OOP La programmazione a oggetti è un’evoluzione per aumentare l’efficienzw del processo di produzione e ma- nutenzione del software. Si innalza il livello di astrazione ottenuto separando i dati dalle procedure pur collocandoli vicino a quest’ul- time. Nella programmazione ad oggetti le istruzioni vengono raggruppate assieme ai loro dati, dando luogo alle cosiddette classi. Una classe è una collezione di attributi (dati) e metodi (procedure), collettivamente chiamati membri, la cui visibilità può essere pubblica, protetta o privata. Ispirandosi al concetto di tipo di dato astratto (insieme di valori e le operazioni su di esso), ciascuna classe esibisce un’interfaccia costituita dalla dichiarazione dei soli membri pubblici e incapsula la dichiarazione di tutti gli attributi e la definizione di tutti i metodi. Grazie al meccanismo dell’ereditarietà è possibile definire gerarchie di classi: Promuove il riutilizzo del codice attraverso l’estensione di classi già esistenti, Abilita il polimorfismo, mediante classi astratte che lasciano alle classi derivate il compito di comple- tare la definizione dei suoi metodi. Un programma a oggetti è una collezione di oggetti che interagiscono tra loro invocando reciprocamente i relativi metodi per consultare o modificare i rispettivi dati e sono dotati di meccanismi per gestire eccezioni quando si verificano determinate situazioni Un oggetto è un’istanza di una classe avente valori specifici ed eventualmente immodificabili per gli attributi della classe, quindi è assimilabile all’allocazione dinamica con relativa inizializzazione di una variabile avente come tipo quella classe. Gli oggetti di una classe hanno gli stessi metodi, ma possono differire per i valori degli attributi. Programmazione dichiarativa: Funzionale e Logica Nella programmazione dichiarativa i programmi sono collezioni di espressioni matematico-logiche che de- scrivono cosa deve essere calcolato senza specificare come. Inoltre non si ha la struttura iterativa, in realtà piuttosto che iterare, si fa uso della ricorsione. Solitamente non si utilizza un approccio completamente funzionale ma un approccio che è un ibrido tra il funzionale e quello ad oggetti. Vedremo che sono presenti controlli di flusso. E le esecuzioni del codice funzionale sono assimilabili a dedu- zioni in un sistema formale (si deduce perché di volta in volta si sostituisce e si riduce ed infine si arriva alla soluzione). Dichiarativo VS Imperativo Le differenze tra i due stili di programmazione sono abbastanza importanti perché in primo luogo non si ha più la parte di business logic che viene inserita nella programmazione di tipo imperativo, la funzione è già tutta auto-contenuta (nel senso che il funzionamento è già intrinseco alla funzione scritta). In secondo luogo è possibile verificare in modo più efficiente ed efficace la correttezza formale di quello che viene fatto, perché i side-effect sono molto difficili da scovare. Un altro aspetto interessante è che il valore di una variabile non cambia più una volta che è stato attribuito perché si sta utilizzando il meccanismo di sostituzione (si sostituisce, si rimpiazza, ed infine si riduce), non si ha quindi il problema dei side-effect, perché anziché utilizzare l’assegnamento si utilizza il meccanismo di sostituzione. Nella programmazione dichiarativa non ci sono effetti collaterali in quanto la valutazione di un’espressione non può modificare variabili al di fuori dell’ambiente locale, quindi è tutto contenuto (“muore e nasce” con la funzione). Nella programmazione imperativa invece, la presenza di variabili globali o di parametri passati per riferi- mento, può generare problemi (non è detto che lanciando la stessa funzione due volte si ottenga lo stesso risultato). 11 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Side Effect Si chiama effetto collaterale (side effect) perché nel momento in cui si va a modificare qualcosa dall’esterno (qualcosa che è esterno alla funzione), questo potenzialmente può creare problemi. Molto spesso si fa ciò in programmazione perché o si: Passa qualcosa per riferimento; Utilizzano le variabili globali. Se sono presenti i due casi riportati sopra le funzioni non sono più funzioni in senso matematico, perché la funzione in senso matematico deve essere auto-contenuta, non dipende da altro, ha tutto quello che gli serve per essere calcolata. Per evitare effetti collaterali (side effect) è buona norma: Non avere parametri passati per indirizzo nelle intestazioni delle funzioni; Non introdurre assegnamenti a variabili esterne nel corpo delle funzioni. Molto spesso per ridurre i problemi dovuti alla presenza di variabili globali, queste dovrebbero essere messe (incapsulate) dentro una classe Singleton e in questa classe andrebbero messi anche tutti i meccanismi di protezione. Se si vogliono distribuire le “copie aggiornate”, si ritorna ai pattern di cui abbiamo parlato in precedenza (Publisher-Subscriber)  si propagano le modifiche in modo sicuro. La programmazione funzionale non risente di questi side effect. Trasparenza Referenziale Un’espressione in un programma si dice che sia referenzialmente trasparente se può essere sostituita con il suo valore e il comportamento risultante è lo stesso del risultante prima del cambiamento. Ciò significa che il comportamento del programma non viene modificato indipendentemente dal fatto che l’input utilizzato sia un riferimento o un valore effettivo a cui punta il riferimento. Sarebbe il principio del “cambiando l’ordine degli addendi il risultato non cambia”. Facciamo un esempio: f(x)+f(x) = 2*f(x) Significa che è indifferente se sommo la funzione f(x)a sé stessa o se la moltiplico per due, in sintesi otterrò lo stesso risultato. f(x)+g(x) = g(x)+f(x) Significa che è indifferente se sommo la funzione g(x)alla funzione f(x) o se sommo la funzione f(x) alla funzione g(x), in sintesi otterrò lo stesso risultato. Programmazione dichiarativa La conseguenza della trasparenza referenziale è che viene restituito lo stesso risultato ogni volta che un’espressione viene valutata sugli stessi argomenti. Il risultato di una procedura che usa variabili globali o passa parametri per indirizzo potrebbe invece dipen- dere dal momento in cui è invocata. Dal punto di vista prestazionale, il valore di un’espressione complessa su determinati argomenti può essere memorizzato in modo da non doverlo ricalcolare (sorta di caching  a livello software). Analogamente, due espressioni tra cui non ci sono dipendenze di dati possono essere valutate in qualsiasi ordine e persino in parallelo. Ricorsione o iterazione La ricorsione è l’unico strumento linguistico per rappresentare l’iterazione (non c’è il while e neanche il for). Il tipo di dato ricorsivo lista è praticamente l’unico tipo di dato strutturato disponibile. La gestione dinamica della memoria per le liste è completamente automatizzata. 12 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi I linguaggi dichiarativi sono dunque interpretati, ma per molti motivi di efficienza vengono solitamente ac- compagnati anche da compilatori da utilizzare quando il codice diventa stabile. Nota che se ci sono valori randomici in una funzione per fare in modo che due run della stessa diano il me- desimo risultato è bene utilizzare il seed. Principio di Induzione Molto della programmazione funzionale si basa sul principio di induzione, che consente la creazione di oggetti a partire da dei cosiddetti casi base (da cui si derivano i successivi). Il principio di induzione è il quinto dei postulati introdotti alla fine del 1800 da Giuseppe Peano per dare una definizione assiomatica di ℕ come il più piccolo insieme che contiene 0 ed è chiuso rispetto all’operazione unaria di successore: 1. Esiste un numero 0 ∈ ℕ; 2. Esiste una funzione totale 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 ∶ ℕ → ℕ; 3. Per ogni 𝑛𝑛 ∈ ℕ , 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛) ≠ 0; 4. Per ogni 𝑛𝑛, 𝑛𝑛′ ∈ ℕ, se 𝑛𝑛 ≠ 𝑛𝑛′ allora 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛) ≠ 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛′ ); 5. Se 𝑀𝑀 è un sottoinsieme di ℕ tale che: a. 0 ∈ 𝑀𝑀; b. Per ogni 𝑛𝑛 ∈ ℕ , 𝑛𝑛 ∈ 𝑀𝑀 implica 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛) ∈ 𝑀𝑀; allora 𝑀𝑀 = ℕ. Gli elementi di ℕ sono quindi 0, 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(0), 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(0) , … dove 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(0) viene denotato col simbolo 1, 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(0)) viene denotato col simbolo 2, e così via. In altri termini, il numero di unità che compongono un numero naturale è pari al numero di volte che bisogna applicare la funzione successore allo 0 per ottenere il numero considerato. Nota che come successore si può anche avere il predecessore (track), questo è utile per muoversi. Implicazioni pratiche Oltre che ℕ stesso, il principio di induzione consente di definire in modo finito le quattro operazioni aritme- tiche su ℕ avendo a disposizione la funzione 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠: ℕ → ℕ e introducendo la funzione totale 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝: ℕ≠0 → ℕ tale che 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑛𝑛) = 𝑛𝑛 per ogni 𝑛𝑛 ∈ ℕ e 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝(𝑛𝑛) = 𝑛𝑛 per ogni 𝑛𝑛 ∈ ℕ≠0 : 𝑚𝑚 𝑠𝑠𝑝𝑝 𝑛𝑛 = 0 Addizione: 𝑚𝑚 ⨁ 𝑛𝑛 = 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠(𝑚𝑚) ⨁ 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝(𝑛𝑛) 𝑠𝑠𝑝𝑝 𝑛𝑛 ≠ 0 sulla cui base si definisce 𝑛𝑛 ≤ 𝑚𝑚 sse esiste 𝑛𝑛 ′ ∈ ℕ tale che 𝑛𝑛 ⨁ 𝑛𝑛′ = 𝑚𝑚 e conseguentemente 𝑛𝑛 < 𝑚𝑚 sse 𝑛𝑛 ≤ 𝑚𝑚 con 𝑛𝑛 ≠ 𝑚𝑚, nonché 𝑚𝑚 ≥ 𝑛𝑛 sse 𝑛𝑛 ≤ 𝑚𝑚 ed 𝑚𝑚 > 𝑛𝑛 sse 𝑛𝑛 < 𝑚𝑚; 𝑚𝑚 𝑠𝑠𝑝𝑝 𝑛𝑛 = 0 Sottrazione: 𝑚𝑚 ⊖ 𝑛𝑛 = dove 𝑛𝑛 ≤ 𝑚𝑚; 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝(𝑚𝑚) ⊖ 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝(𝑛𝑛) 𝑠𝑠𝑝𝑝 𝑛𝑛 > 0 0 𝑠𝑠𝑝𝑝 𝑛𝑛 = 0 Moltiplicazione: 𝑚𝑚 ⊗ 𝑛𝑛 = 𝑚𝑚 ⨁ 𝑚𝑚 ⊗ 𝑝𝑝𝑝𝑝𝑝𝑝𝑝𝑝(𝑛𝑛) 𝑠𝑠𝑝𝑝 𝑛𝑛 ≠ 0 ; 0 𝑠𝑠𝑝𝑝 𝑚𝑚 < 𝑛𝑛 Divisione: 𝑚𝑚 ⊘ 𝑛𝑛 = 𝑠𝑠𝑠𝑠𝑠𝑠𝑠𝑠 (𝑚𝑚 dove 𝑛𝑛 ≠ 0. ⊖ 𝑛𝑛) ⊘ 𝑛𝑛 𝑠𝑠𝑝𝑝 𝑚𝑚 ≥ 𝑛𝑛 Facciamo un esempio: 𝟓𝟓 ⊕ 𝟐𝟐 = 𝟔𝟔 ⊕ 𝟏𝟏 = 𝟕𝟕 ⊕ 𝟎𝟎 = 𝟕𝟕 𝟓𝟓 ⊖ 𝟐𝟐 = 𝟒𝟒 ⊖ 𝟏𝟏 = 𝟑𝟑 ⊖ 𝟎𝟎 = 𝟑𝟑 𝟓𝟓 ⊗ 𝟐𝟐 = 𝟓𝟓 ⊕ (𝟓𝟓 ⊗ 𝟏𝟏) = 𝟓𝟓 ⊕ 𝟓𝟓 ⊕ (𝟓𝟓 ⊗ 𝟎𝟎) = 𝟓𝟓 ⊕ (𝟓𝟓 ⊕ 𝟎𝟎) = 𝟓𝟓 ⊕ 𝟓𝟓 = ⋯ = 𝟏𝟏𝟎𝟎 13 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi 𝟓𝟓 ⊘ 𝟐𝟐 = 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔((𝟓𝟓 ⊖ 𝟐𝟐) ⊘ 𝟐𝟐) = ⋯ = 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔(𝟑𝟑 ⊘ 𝟐𝟐) = 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔 (𝟑𝟑 ⊖ 𝟐𝟐) ⊘ 𝟐𝟐 = ⋯ = 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔(𝟏𝟏 ⊘ 𝟐𝟐) = 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔(𝟎𝟎) = 𝒔𝒔𝒔𝒔𝒔𝒔𝒔𝒔(𝟏𝟏) = 𝟐𝟐 Lamba Calcolo In matematica una funzione è un caso particolare di relazione. Una funzione 𝑓𝑓 ∶ 𝐴𝐴 → 𝐵𝐵 è un sottoinsieme di 𝐴𝐴 × 𝐵𝐵 che a ogni elemento di 𝐴𝐴 associa al più un elemento di 𝐵𝐵 è un insieme di coppie ordinate, formate da un elemento di 𝐴𝐴 e un elemento di 𝐵𝐵, che gode di una sorta di proprietà di univocità dà 𝐴𝐴 verso 𝐵𝐵. Questa è una visione estensionale del concetto di funzione, in quanto si focalizza sull’insieme di coppie ordi- nate senza considerare il procedimento computazionale tramite il quale si ottiene il risultato 𝑏𝑏 = 𝑓𝑓(𝑎𝑎) par- tendo dall’argomento 𝑎𝑎. λBasi Church era interessato a creare un calcolo che catturasse gli aspetti computazionali delle funzioni per calcolo si intende un sistema formale dotato di una sintassi per generare termini in un certo formato accompagnata da un insieme di regole di riscrittura per trasformare i termini in altri termini. Il λ-calcolo descrive le funzioni nella loro piena generalità – incluse funzioni ricorsive e funzioni di ordine superiore (cioè aventi funzioni come argomenti) – attraverso una sintassi estremamente semplice che distin- gue tra definizione di funzione, detta λ-astrazione, e applicazione di funzione. Questo supera l’ambiguità che talvolta viene a crearsi in merito alla notazione 𝑓𝑓(𝑥𝑥), usata a volte per deno- tare la definizione di f e altre volte per denotare il valore che 𝑓𝑓 assume in 𝑥𝑥. Nozioni Base Gli aspetti fondamentali nel lambda calcolo sono: l le variabili; i termini lambda; l'applicazione; l'astrazione. Quella sopra mostrata è la “grammatica” che ci serve per poter generare delle frasi che siano poi coerenti con il linguaggio che utilizzeremo. Astrazione L’astrazione e è solo una definizione di funzione, è indicata dal simbolo λ e dopo questo simbolo si elencano le variabili di input di una funzione; seguita da un punto; poi l'output della funzione in cui si descrive cosa si vuole ottenere (quindi è il corpo di una funzione allo stesso tempo). (𝜆𝜆𝑥𝑥. 𝐸𝐸) ∈ 𝛬𝛬 per ogni 𝑥𝑥 ∈ 𝑉𝑉𝑎𝑎𝑝𝑝 ed 𝐸𝐸 ∈ 𝛬𝛬, detta λ-astrazione, dove λ è detto legatore per 𝑥𝑥 in 𝐸𝐸. (𝐸𝐸1 𝐸𝐸2 ) ∈ 𝛬𝛬 per ogni 𝐸𝐸1 , 𝐸𝐸2 ∈ 𝛬𝛬, detta applicazione di 𝐸𝐸1 a 𝐸𝐸2. Regole Assumiamo che l’applicazione abbia precedenza sulla λ-astrazione 1. la λ-astrazione sia associativa da destra λx.E1 E2 sta per (λx(E1 E2)) 14 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi λx.λy.E sta per (λx.(λy.E)) 2. l’applicazione è associativa a sinistra – E1 E2 E3 sta per ((E1 E2) E3). λx.x funzione identità (passo x e mi ritorna x) λx.x+1 successore (passo x e mi ritorna x+1) λx.x-1 predecessore (passo x e mi ritorna x-1) (λx.x+1)2 è l’applicazione della funzione successore al valore 2 (passo 2 e mi ritorna 3) (λx.x)y è l’applicazione della funzione identità ad y 3. (λz.z) ≡ (λy.y) ≡ (λt.t) ≡ (λu.u) 4. I nomi degli argomenti nelle definizioni di funzione non hanno alcun significato. 5. Ne λ-calcolo tutti i nomi sono locali alle definizioni. 6. Nella funzione λx.x diciamo che x è “vincolato” poiché la sua occorrenza nel corpo della definizione è preceduta da λx. 7. Un nome che non è preceduto da λ è chiamata “variabile libera”. 8. Nell'espressione (λx.xy) la variabile x è vincolata ed y è libera. (λx.x)(λy.yx) Valutazione La funzione lambda, che si scrive: (λx.x)2 2 Si traduce in Javascript come: (x => x)(2) oppure (function(x) {return x;})(2) Una funzione anonima o funzione lambda è una funzione definita, e possibilmente chia- mata, senza essere legata ad un identificatore; queste sono utili per passare come argomento una funzione di ordine superiore. Riduzione Facciamo un esempio: (lambda x: x+1) (2) = lambda 2: 2+1 = 2+1 = 3 Questa è una lambda fatta in Python. Lambda identifica di fatto una funzione, quindi un punto dove si fa calcolo, x rappresenta il parametro, i : in Python rappresentano il blocco di codice (in altri linguaggi si utiliz- zano le graffe) ovvero il corpo della nostra funzione e il (2) diventa il modo per invocare la funzione descritta prima. Quindi di fatto il 2 diventa il parametro che viene passato alla funzione, c’è quindi un meccanismo di valuta- zione che corrisponde a sostituire, che significa prendere il valore passato tra parentesi (nell’esempio (2)) e andarlo a sostituire in tutte le occorrenze (nell’esempio sopra mettere 2 al posto di x, come si vede nella seconda riga). Non è finita qui perché la macchina non ha ancora prodotto un risultato, per produrre il risultato, deve effet- tuare un’ulteriore operazione quindi deve fare l’operazione aritmetica (nell’esempio la somma di 2 e 1). Infine 3 è un termine minimo, questo perché è stato ridotto ai minimi termini cioè non ha bisogno di ulteriori processamenti perché non sono coinvolti ulteriori operatori o ulteriori espressioni. 15 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Il meccanismo nell’esempio sopra viene anche chiamato di riduzione perché prima sostituisco, poi si procede nella valutazione, ovvero si va a vedere se è possibile ridurla (ovvero effettuare ulteriori operazioni), se la risposta è si si procede nell’esecuzione delle operazioni necessarie a ridurla e di conseguenza si arriva al termine minimo. Detto in due parole questo è quello che avviene nell’approccio funzionale, quindi si ha la funzione che di- ciamo avere un comportamento astratto, diventa poi un valore reale che possiamo utilizzare nel momento in cui si va a fare sostituzione e riduzione. Rispetto alla maniera “tradizionale” una prima cosa da notare e che le funzioni sono anonime, ovvero non hanno un nome, non si ha l’istruzione di return, può essere invocata al volo e quindi un approccio diverso. Per fare un esempio si consideri il voler fare una funzione che calcoli la somma di una lista di elementi: l = [1,2,3,4,5] sum_value = 0 for item in l: sum_value+=item Il codice appena visto è un algoritmo puramente sequenziale, non è parallelizzato in alcun modo, quindi non sfrutta capacità di calcolo aggiuntive. Si potrebbe in realtà utilizzare un meccanismo di riduzione, ovvero si potrebbe utilizzare una logica diversa. Si pensi di avere una macchina in grado di fare calcolo parallelo, sfrut- tando questa capacità si potrebbe ridurre il tempo necessario per svolgere un calcolo. L’approccio di riduzione in realtà è un approccio che è molto comune perché se si dispone di unità di calcolo aggiuntive (tipo dei thread o quant’altro) è possibile fare una divisione, dare ad ogni unità una piccola parte ed applicare il meccanismo di riduzione, procedendo nella riduzione di volta in volta fino ad arrivare all’ele- mento minimale. Come si fa la sostituzione? Questo è un argomento leggermente più complesso e prima di trattarlo bisogna introdurre l’ α-equivalenza. α-equivalenza Termini che differiscono esclusivamente per le variabili legate vanno considerati identici. Tale proprietà è codificata nel λ-calcolo con l’α-equivalenza. λx.x e λz.z sono lo stesso termine, ovvero: λx.x ≡α λz.z Sostituzione La notazione M[L/x] denota che ogni occorrenza libera della variabile x in M è sostituita dal termine L M=x L M = PQ P[L/x]Q[L/x] M=λy.P λy.(P[L/x]) Beta Riduzione Diremo che un termine P si β-riduce in un passo al termine Q (scriveremo P ->β Q) se Q può essere ottenuto da P sostituendo un sotto-termine di P della forma (λx.M)N con M[N/x] NORMALIZZAZIONE: Se un termine non possiede β-riduzioni, esso è in forma normale  λx.x è in forma normale; 16 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Se un termine può essere ridotto in forma normale, allora esso possiede una forma normale  (λx.x)y possiede una forma normale: y. (λx.A)B ->β A[B/x] Regola: data un'applicazione (λx.A)B si elide la componente λx e (nel sotto-termine rimanente, A) si so- stituisce ogni occorrenza libera x con il termine B. Facciamo un esempio: Beta conversione Diremo che il termine M è β-convertibile in N se e solo se M può essere trasformato in N (o viceversa) ese- guendo zero o più β-riduzioni (o β-espansioni) ed indicheremo tale proprietà con M =β N Currying Per definizione una funzione (λx.M) ha come argomento una sola variabile. Tramite il Currying ciò non è una limitazione computazionale. Il Currying è una tecnica che permette di trasformare una funzione che prende n argomenti in una catena di n funzioni che prendono un solo argomento ciascuna. Il Currying è ammesso in quanto le funzioni, nel λ- calcolo, sono entità di prima classe. Funzioni che ammettono funzioni come input/output sono funzioni di ordine superiore (higher-order func- tions). Facciamo un esempio: Un approccio basato sul lambda calcolo, piuttosto che un approccio basato su una funzione e variabili globali è completamente diverso perché l’esecuzione di una funzione lambda, mi ritorna un valore (o una funzione) ma non modifica lo stato. Per modificare lo stato si dovrebbe fare una cosa del genere: 17 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Normal-order Reduction È la strategia di riduzione "standard" per il lambda calcolo. Si riduce, ad ogni step, il termine “esterno” più a sinistra: Più a sinistra (leftmost): da NM proviamo a ridurre N prima di M; Più esterno (outermost): proviamo a ridurre prima (λx.N)M, piuttosto che N o M leftmost – outermost Facciamo un esempio: Leftmost Innermost (LI): tra indici successivi più interni, si sceglie quello più a sinistra per ridurlo; Leftmost Outermost (LO): tra indici successivi più esterni, si sceglie quello più a sinistra per ridurlo; Booleani… (pensiamo sempre in termini di funzioni) Definiamo due funzioni che associamo a TRUE e FALSE: T ≡ λxy.x e F ≡ λxy.y. La prima funzione accetta due argomenti e restituisce il primo, mentre la seconda funzione restituisce il secondo di due argo- menti. Operazioni logiche (AND / OR / NOT) Definiamo funzioni ∧ ≡ 𝜆𝜆𝑥𝑥𝜆𝜆. 𝑥𝑥𝜆𝜆(𝜆𝜆𝑠𝑠𝜆𝜆. 𝜆𝜆) ≡ 𝜆𝜆𝑥𝑥𝜆𝜆. 𝑥𝑥𝜆𝜆𝑥𝑥 ∨ ≡ 𝜆𝜆𝑥𝑥𝜆𝜆. 𝑥𝑥(𝜆𝜆𝑠𝑠𝜆𝜆. 𝑠𝑠)𝜆𝜆 ≡ 𝜆𝜆𝑥𝑥𝜆𝜆. 𝑥𝑥𝑥𝑥𝜆𝜆 ¬ ≡ 𝜆𝜆𝑥𝑥. 𝑥𝑥(𝜆𝜆𝑠𝑠𝜆𝜆. 𝜆𝜆)(𝜆𝜆𝑎𝑎𝑏𝑏. 𝑎𝑎) ≡ 𝜆𝜆𝑥𝑥. 𝑥𝑥𝑥𝑥𝑥𝑥 Facciamo un esempio: NOT TRUE 𝑥𝑥 ≡ 𝜆𝜆𝑥𝑥𝜆𝜆. 𝑥𝑥 ¬≡ 𝜆𝜆𝑥𝑥. 𝑥𝑥(𝜆𝜆𝑠𝑠𝜆𝜆. 𝜆𝜆)(𝜆𝜆𝑎𝑎𝑏𝑏. 𝑎𝑎) ≡ 𝜆𝜆𝑥𝑥. 𝑥𝑥𝑥𝑥𝑥𝑥 ¬𝑥𝑥 ≡ 𝜆𝜆𝑥𝑥. 𝑥𝑥(𝜆𝜆𝑠𝑠𝜆𝜆. 𝜆𝜆)(𝜆𝜆𝑎𝑎𝑏𝑏. 𝑎𝑎)(𝜆𝜆𝑠𝑠𝑝𝑝. 𝑝𝑝) 18 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi JavaScript È a tutti gli effetti un linguaggio di programmazione, è nativo rispetto al browser (in sostanza è il linguaggio che il browser utilizza per eseguire codice). È stato creato nel 1995 da Brendan Eich (il co-founder di Mozzilla). All’inizio era abbastanza basilare e consentiva una manipolazione minima del DOM. JavaScript è debolmente tipizzato (le variabili possono cambiare il loro tipo). Codice nelle pagine web HTML consente di inserire file JavaScript (o direttamente codice JavaScript), nelle pagine web, mediante il tag. Il tag si trova nell’ del file HTML. Se il codice si trova direttamente nel file HTML si dice inline altrimenti si dice esterno. console.log Si possono stampare messaggi di log in JavaScript mediante console.log(). Facciamo un esempio: console.log(‘Hello, world!’); È l’equivalente in JavaScript del comando Java: System.out.println, System.out.print, System.out.printf, … Sia nelle applicazioni di front-end che nelle applicazioni di back-end quando si esegue il console.log si hanno diversi livelli: Log; Warn; Error. Esecuzione di codice JavaScript JavaScript è compilato ed eseguito “on the fly”, utilizzando o il browser engine oppure node.js. Inoltre in JavaScript non c’è il metodo main  lo script viene eseguito dalla cima al fondo. Browser’s sandbox Il browser non ha accesso a tutto ma ha accesso solo ad alcune risorse. Quindi le “abilità” di JavaScript nel browser sono limitate per il bene e la sicurezza dell’utente. Lo scopo è quello di prevenire l’accesso di un pagina web malevola alle informazioni private dell’utente. Alcuni esempi sono: Il codice JavaScript non può leggere/scrivere file sull’hard disk, copiare o eseguire programmi, e non può accedere alle funzioni del Sistema Operativo; 19 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Ci sono criteri di protezione chiamati Same Origin Policy, che in sostanza devono limitare gli accessi di una pagina web a contenuti esterni; Si devono limitare le origini delle richieste (CORS), viene spesso fatto lato server. Statement Gli statement sono costrutti sintattici e comandi che eseguono azioni. Ad esempio: alert(“Hello”); alert(“World!”); Il ; può essere usato per terminare uno statement, ma non è necessario se c’è un a capo tra gli statement (JavaScript aggiungerà da solo i ; imliciti). Il seguente codice ad esempio è comunque corretto: alert(“Hello”) alert(“World!”) Commenti Le strutture dei commenti sono uguali a quelle viste per altri linguaggi e possono essere: Commenti one-line (iniziano con //); Commenti multi-line (hanno la struttura ). Facciamo un esempio: //Questo è un commento Variabili Questa invece è una differenza importante. La dichiarazione di una variabile in JavaScript avviene tramite tre modificatori: var, let e const. Più si riesce a ridurre lo scope delle variabili meno è probabile che ci siano side-effect. Le variabili (var) e le costanti (const) dichiarate all’inizio sono globali, non ha quindi senso dichiarare un let se non all’interno di un blocco di una funzione. Va fatto un appunto su const, ovvero: Best practices Sarebbe bene: Usare const quando possibile; Se necessiti di una variabile riassegnabile, utilizza let; Non usare var: o Su internet si trovano un sacco di sesmpi con var pechè let e const sono più nuovi; o Comunque const e let sono ben supportati, non c’è quindi una ragione per non utilizzarli. 20 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Molte delle applicazioni scritte oggi hanno come linee guida quelle di AirBnb o quelle di Google. Tipi JavaScript non ha tipi, ma i valori si. Questi sono i tipi che troveremo anche in TypeScript. Ci sono sei tipi primitivi: Boolean: true e false; Number: sono considerati tutti double, non esistono gli integer; String: possono essere racchiuse in ‘single-quotes’, in “double-quotes” o nei `backticks` Symbol; Null: un valore che significa l’assenza di valore; Undefined: è il valore di una variabile a cui non è stato assegnato alcun valore. Ci sono anche i tipi oggetto, inclusi gli Array, Date, String, … Li tratteremo poi. Number Come detto prima sono tutti considerati double, non esistono quindi gli interi. Gli operatori e le precedenze sono come quelli del Java o del C++. Ci sono alcuni valori speciali, quali: Nan (not-a-number); +Infinity; -Infinity. C’è una classe Math (Math.floor, Math.ceil, …). BigInt Il tipo numero di JavaScript non può rappresentare valori interi maggiori di (2^53 - 1) o inferiore a -(2^53 - 1) per i negativi. Il tipo BigInt è stato recentemente aggiunto al linguaggio per rappresentare numeri interi di lunghezza arbi- traria. Un valore BigInt è creato aggiungendo n alla fine di un numero intero. Facciamo un esempio: La n alla fine indica che è un BigInt. Operazioni di base Le seguenti operazioni matematiche sono supportate: Addizione +; Sottrazione -; Moltiplicazione *; Divisione /; Remainder %; Potenza **; Incremento ++; Decremento --; Aggiungi a +=; Moltiplica a *=. Il più + esiste in due forme: la forma binaria che abbiamo usato sopra e la forma unaria, che può convertire un valore in un numero (ad es. +"3"). 21 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Operazioni bitwise Sono supportate le operazioni bitwise tra i numeri, quali: AND &; OR |; XOR ^; NOT ~; LEFT SHIFT ; ZERO-FILL RIGHT SHIFT >>>. String Può essere definito con virgolette singole, doppie o backtick. Utilizzando la notazione ${} in una stringa con backtick si può incorporare il valore della variabile (passata dentro ${}) nella stringa stessa. Una stringa è Immutabile. Non esiste il tipo carattere (char): le lettere sono stringhe di lunghezza uno. Si può usare + per la concatenazione. Si può controllare la dimensione tramite la proprietà (nota che a diffe- renza di altri linguaggi è una proprietà e non una funzione) della lunghezza (non una funzione), inoltre si può accedere a caratteri specifici tramite l'indicizzazione (come un array). Booleani Esistono due valori letterali per booleano: true e false che generalmente si comportano come ci si aspette- rebbe. Si possono utilizzare i soliti operatori booleani: && (and), || (or), ! (not). I valori non booleani possono essere utilizzati nelle istruzioni di controllo, essi vengono convertiti nel loro valore "vero" o "falso": nullo, non definito, 0, NaN, ‘ ’,” “  restituiscono false tutto il resto  restituisce vero. Uguaglianze In JavaScript == e != non hanno un funzionamento corretto, in quanto eseguono una conversione di tipo implicita prima di fare la comparazione. Anziché correggere == e !=, lo standard ECMAScript ha aggiunto === e !==. Quindi bisogna sempre usare === e !== al posto di == e !=. 22 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Null e Undefined La differenza è che: null è un valore che rappresenta l’assenza di valore (è simile al null di Java o al nullptr in C++); undefined è il valore dato (dalla macchina) ad una variabile che non ha valore (si può settare il valore di una variabile affinchè questa sia undefined). Istruzioni condizionali (if) L'istruzione if (...) valuta l'espressione tra parentesi e converte il risultato in un valore booleano. Se il boo- leano è vero, il branch (dato da un blocco o da una singola istruzione) viene eseguito. Inoltre l'istruzione if può includere un blocco else opzionale, che verrà eseguito se il booleano è falso. Facciamo un esempio: if (x % 2 == 0) { console.log('x is even!'); } else { console.log('x is odd!); } L'istruzione if può anche includere più rami, usando uno o più blocchi else if. Facciamo un esempio: if (x % 2 == 0) { console.log('x is even!'); } else if (x % 5 == 0) { console.log('x is divisible by 5!'); } else { console.log('x is odd!); } L’operatore ternario A volte, abbiamo bisogno di assegnare una variabile a seconda di una condizione. Il cosiddetto operatore “condizionale” o “punto interrogativo” permette di farlo in modo breve e semplice (operatore ternario). La sintassi è: let result = condition? value1 : value2; La condizione viene valutata: se è veritiera, viene restituito value1, altrimenti value2. Per esempio: let accessAllowed = (age > 18) ? true : false; L’operatore ternario può anche essere messo in chain, e questo ritornerà un valore che dipende da una o più condizioni. Facciamo un esempio: let age = prompt('age?', 18); let message = (age < 3) ? 'Hi, baby!' : (age < 18) ? 'Hello!' : (age < 100) ? 'Greetings!' : 'What an unusual age!'; alert( message ); 23 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Loops: while Il loop while ha la seguente sintassi: while (condition) { // code // so-called "loop body" } Fino a che la condizione è vera il codice contenuto nel corpo viene eseguito. N.B. Se il corpo è costituito da una sola riga le parentesi graffe possono essere omesse. Loops: do while Il controllo sulla condizione può essere fatto alla fine utilizzando la sintassi del ciclo do while: do { // loop body } while (condition); Il loop, prima esegue il corpo, poi va a verificare la condizione, e, fino a che questa è vera esegue il corpo ancora e ancora, … Questa sintassi va utilizzata solamente quando si vuole che il codice sia eseguito almeno una volta a prescin- dere dalla condizione. Loops: for Il loop for è più complesso ma anche il loop più utilizzato (utile come contatore). È fatto così: for (begin; condition; step) { //... loop body... } Ogni parte del for, può essere omessa. Break Normalmente, un ciclo esce quando la sua condizione diventa falsa, ma si può forzare l'uscita in qualsiasi momento utilizzando la speciale direttiva break. Continue La direttiva continue è una "versione più leggera" di break. Non interrompe l'intero ciclo. Interrompe, invece, l'iterazione corrente e forza il ciclo ad avviarne una nuova (se la condizione lo consente). Switch Un'istruzione switch può sostituire un if multiplo. Confrontare un valore con più varianti. switch (x) { case 'value1': // if (x === 'value1') … [break] case 'value2': // if (x === 'value2') … [break] default: … [break] } Va utilizzato il break perché le alternative devono essere mutuamente esclusive. 24 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Funzioni Dichiarazione di una funzione JavaScript non è tipizzato, ciò significa che si è abbastanza liberi, si possono persino creare funzioni che ritor- nano altre funzioni. La sintassi è la seguente: function nome_della_funzione() { corpo della funzione; } C’è una sintassi che permette di creare una funzione espressione, ovvero permette di creare una nuova fun- zione nel mezzo di una espressione. Facciamo un esempio: let sayHi = function() { alert( "Hello" ); }; sayHi() // function is called here! Qui possiamo vedere una variabile sayHi che ottiene un valore, ovvero il valore della nuova funzione, creata come function() { alert("Hello"); }. Parametri di funzione Non c’è nulla di particolare, sono gli argomenti passati alla funzione. I parametri delle funzioni non sono dichiarati con var, let o const. Ritornare un valore Per restituire un valore da una funzione, analogamente ad altri linguaggi, si può utilizzare la parola chiave return. Non si è obbligati a ritornare un valore, ma in una programmazione funzionale non avrebbe senso perché si cerca di ridurre al minimo le funzioni che vanno a cambiare valori esterni. Facciamo un esempio: function sum(a, b) { return a + b; } let add5To7 = sum(5, 7); console.log(add5To7) N.B. A differenza dei linguaggi come Java, in JavaScript non viene specificato il valore di ritorno, questo per- ché JavaScript lo asserisce automaticamente. Funzioni arrow È un modo conciso e molto semplice per definire una funzione. Per fare ciò si usa la seguente sintassi: let func = (arg1, arg2,..., argN) => expression; Le arrow function possono essere anche multiline Facciamo un esempio: let sum = (a, b) => { // multiline function let result = a + b; return result; // explicit "return" }; alert( sum(1, 2) ); // 3 25 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Non si può fare sum(1)(2), perché la funzione vuole 2 parametri, se si fosse utilizzato il carrying si sarebbe potuto operare in maniera differente. Oggetti In JavaScript gli oggetto sono rappresentati come coppie di chiave-valore. Gli oggetti vengono utilizzati per memorizzare raccolte di chiavi di vari dati e entità più complesse (simili a mappe/dizionari). In JavaScript, gli oggetti penetrano in quasi ogni aspetto del linguaggio. Un oggetto può essere creato con parentesi quadre {…} con un elenco di proprietà facoltativo. Una proprietà è una coppia "chiave: valore", dove key è una stringa ("nome proprietà") e il valore può essere qualsiasi cosa. let user = { //un oggetto name: "John", //nella chiave "name" è storato il valore "John" age: 30 //nella chiave "age" è storato il valore 30 }; L’unica differenza con il JSON (JavaScript Object Notation) è che in quest’ultimo le chiavi vanno messe tra virgolette. Esistono due funzioni che permettono di passare da JSON a oggetto e viceversa. Essendo un oggetto i valori delle sue proprietà sono accessibili con la dot notation. Mediante questa nota- zione si possono anche aggiungere nuove proprietà. Per cancellare una proprietà si utilizza invece la parola chiave delete. Facciamo un esempio: let user = { name: "John", age: 30 }; // prende il valore della proprietà dell’oggetto alert(user.name); // John alert(user.age); // 30 user.isAdmin = true; //Aggiunge la proprietà con chiave “isAdmin” e valore true delete user.age; //Cancella la proprietà con chiave “age” Esiste anche la possibilità di utilizzare la notazione con le parentesi quadre (come nei dizioanri). let user = {}; // set user["likes birds"] = true; // get alert(user["likes birds"]); // true // delete delete user["likes birds"]; Le parentesi quadre forniscono anche un modo per ottenere il nome della proprietà come risultato di qual- siasi espressione – al contrario di una stringa letterale – come da una variabile come segue: let key = "likes birds"; // same as user["likes birds"] = true; user[key] = true; Note Non ci sono limitazioni sui nomi delle proprietà. Possono essere sia stringhe che simboli. Altri tipi vengono automaticamente convertiti in stringhe. 26 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi L'operatore "in" può essere utilizzato per verificare se una proprietà è presente in un oggetto (es. "key" in object) Per iterare su tutte le chiavi di un oggetto, esiste una forma speciale di ciclo: for..in. Facciamo un esempio: for (let key in user) { // keys alert(key); // name, age, isAdmin // valori per le chiavi alert(user[key]); // John, 30, true } Un oggetto è una collezione iterabile non propriamente nel termine classico, ma si può considerare come una collezione di chiavi-valori. Copia di un oggetto Una delle differenze fondamentali degli oggetti rispetto ai tipi primitivi è che gli oggetti vengono archiviati e copiati "per riferimento", mentre i valori primitivi vengono sempre copiati “come un intero valore”. let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // changed by the "admin" reference alert(user.name); // 'Pete' Funzioni utili Come citavamo prima in Javascript esistono due funzioni alquanto utili: 1. JSON.stringify(object);  consente di prendere un oggetto e serializzarlo in JSON; 2. JSON.parse(‘stringa_json’)  consente di prendere una stringa JSON e trasformarla in un oggetto JavaScript. Il secondo risulta molto comodo perché quando si riceve una risposta da un API, questa è una stringa in formato JSON, che quindi deve essere convertita in un oggetto. Nota che si possono creare oggetti annidati. Array Sono di tipo dinamico non hanno quindi una fixed size. Ci sono due modi per creare un array vuoto e questi sono: let arr = new Array(); let arr = []; Si può prendere un elemento tramite il suo indice, mettendolo di finaco al nome dell’array tra parentesi quadre. 27 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Facciamo un esempio: let fruits = ["Apple", "Orange", "Plum"]; alert(fruits); // Apple alert(fruits); // Plum Operazioni sugli array Metodo Descrizione arr.push(element) Aggiunge element in fondo arr.unshift(element) Aggiunge element in cima arr.pop() Rimuove il primo elemento dal fondo arr.shift() Rimuove il primo elemento dalla cima arr.indexOf(element) Ritorna l’indice numerico di element o -1 se ele- ment non è presente Note Un array è un tipo speciale di oggetto. Le parentesi quadre utilizzate per accedere ad una proprietà, proven- gono effettivamente dalla sintassi del tipo oggetto. La proprietà lenght può essere utilizzata per ottenere la lunghezza dell'array. Come gli oggetti, gli array vengono copiati per riferimento. Esiste una forma speciale di loop per gli array, for..of. Facciamo un esempio: let fruits = ["Apple", "Orange", "Plum"]; // itera sugli elementi dell’array for (let fruit of fruits) { alert( fruit ); } 28 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi OOP e JavaScript funzionale Quando si lavora con API si fa la modellazione con una classe del tipo che viene ritornato, contenente tutta una serie di attributi, e si andranno a modellare tutti gli attributi che ci vengono ritornati. Questo è importante perché quando si fa il parse di un JSON risultante da una chiamata ad una API poten- zialmente viene ritornato un array di oggetti e per essere sicuri che la risposta dell’API sia corretta, si va a verificare che se viene eseguito il cast dell’oggetto ottenuto dall’API (che è di tipo generico) ad un tipo speci- fico (quello modellato con la classe) ed il cast ha successo allora i dati ritornati dall’API sono conformi al modello dei dati. Se il cast fallisce, significa che l’API ha ritornato una risposta che non è conforme al modello dei dati creato in precedenza. ES6 Classes Possiamo definire classi in JavaScript usando una sintassi che è simile a Java o C++: Questi sono spesso chiamati "Classi ES6" o "Classi ES2015" perché sono state introdotte nel EcmaScript 6 standard, nella versione del 2015. Ricordiamo che EcmaScript è lo standard; JavaScript è un’attuazione dello Standard EcmaScript. Metodi Il costruttore è opzionale ed i parametri e i metodi (del costruttore) sono definiti come per le funzioni globali. Non si utilizza la parola chiave function per definire i metodi. Si può sempre fare riferimento ad altri metodi o attributi interni alla classe, utilizzando il prefisso. I metodi sono pubblici di default, ma si possono definire attributi o metodi pri- vati utilizzando il simbolo #. Nota che questa è una feature nuova, at- tualmente non funziona in tutti i brow- ser. Si possono trovare maggiori informazioni, al seguente link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields In altri linguaggi, come il Python, non sono presenti meccanismi di protezione forte ed infatti per i metodi privati e per i metodi protetti si utilizza l’underscore, che anteposto al nome del metodo identifica che è privato. 29 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Attributi Gli attributi possono essere definiti esplicitamente in JavaScript, anche se sono opzionali. Gli attributi possono anche essere privati (si usa la stessa notazione vista prima per i metodi privati). Gli attributi pubblici sono definiti set- tando nel costruttore, o in qualunque altra funzione. Come negli altri linguaggi in JavaScript si possono avere getter e setter per specifiche proprietà. Per definirli si utilizzano le parole chiave get e set. Inoltre vi è la possibilità di dichiarare gli attributi di tipo statico, per fare ciò bisogna utilizzare la parola chiave static. Instantiation La parola chiave new serve per creare un nuovo oggetto di una data classe. Extends, super È la stessa cosa che in Java. Extends ci consente di avere la parte di ereditarietà. 30 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi La parola chiave super è usata per riferirsi alla super-classe (la classe “genitore”). Può essere usata all’interno del costrut- tore o in un altro metodo della classe. Hoinsting Un'importante differenza tra le dichiarazioni di funzione e le dichiarazioni di classe è che mentre le funzioni possono essere chiamate nel codice che appare prima di essere definite, le classi devono essere definite prima di poter essere costruite. Un codice come il seguente genererà un ReferenceError: const p = new Rectangle(); // ReferenceError class Rectangle {} Ciò si verifica perché mentre la classe viene hoisted, i suoi valori non vengono inizializzati. 31 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi AJAX, XML, JSON – Programmazione asincrona, FETCH e AJAX Un’applicazione web è un contenuto che viene erogato da un server che risponde a delle richieste secondo un protocollo che in generale è http o https. Poi la pagina web risultante può essere una pagina web statica o dinamica (è interattiva, non si ricarica ogni volta che sono inseriti dei dati e soprattutto carica i dati nuovi senza ricaricarsi e questi dati li prende inter- rogando le API). Oltre alla parte del web server (utilizziamo l’estensione di Visual Studio Code: Live Server), ci servirà un mock API server (utilizzeremo mockoon), che ci consente di simulare la chiamata ad un API (viene chiamata l’API localmente). Introduzione ad AJAX In sostanza accade che quando si fanno delle richieste, che avvengono in modo asincrono, una volta che si è ottenuta la risposta si reagisce alla risposta. 32 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Programmazione funzionale in JavaScript Programmazione funzionale Richiamiamo i principi fondamentali: non ci sono statement; non c’è stato (evita i side effect); è tutto trattato come una funzione. Solitamente non si sceglie mai una programmazione completamente funzionale ma si opta per un giusto compromesso tra la programmazione orientata alle funzioni e la programmazione funzionale pura. Declinazioni della programmazione funzionale in JavaScript Ci sono alcuni concetti della programmazione funzionale che risultano particolarmente di interesse e sono: First-class functions  ovvero le funzioni sono trattate come oggetti; Currying  si semplificano le funzioni da multiparametri a funzioni che prendono il singolo parame- tro; Closures  funzioni all’interno di altre funzioni; Anonymous functions / lambdas / function literals È bene imparare anche il paradigma di programmazione funzionale perché: First-class function Sono, come accade in altri linguaggi, delle funzioni che vengono trattate come delle variabili. Risultano di interesse perché essendo delle variabili, posso: passarle ad altre funzioni come parametri; salvarle in una variabile; evitare la definizione della funzione usando il nome/identificatore, queste vengono anche chiamate: o anonymous function; o lambda function; o function literal value. Le funzioni possono essere dichiarate in tre differenti modi: è l’old style rappresenta un approccio più moderno altro non è che l’arrow notation A prescindere da come siano dichiarate, le funzioni si invocano tutte nel medesimo modo. Le funzioni (che sono considerate oggetti) a differenza degli oggetti classici, hanno in JavaScript la possibilità di avere un metodo che si chiama call, che in pratica va ad invocare la funzione. (Fa la stessa cosa dell’in- vocazione “tradizionale”). 33 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi Facciamo un esempio: Se si provasse ad utilizzare il metodo call su un oggetto tradizionale, si avrebbe un errore perché il compu- ter la vorrebbe eseguire come una funzione, ma non lo è in quanto non ha del codice eseguibile, ha solo delle proprietà. Ma se l’oggetto ha al suo interno anche delle funzioni (ovvero una delle sue proprietà ha come valore una funzione), queste possono essere richiamate con call (quindi oggetto.metodo_dellog- getti.call() va bene). Callback Abbiamo visto nelle lezioni precedenti la possibilità di inserire delle callback, sia: andando ad evidenziare che quando uno clicca su un elemento HTML accade qualcosa; nel codice quando si andavano a gestire le risposte derivanti dall’interrogazione della nostra API (la callback che viene richiamata quando c’è un cambiamento di stato). Utilizzeremo le callback anche in altri contesti soprattutto nella parte di log. Poi quando si parlerà della ge- stione sincrona e asincrona (quindi con le promise e le async…await), il concetto sarà ancora più importante e si andranno quindi a rimpiazzare le callback, con altri elementi (che effettivamente sono le async, le await e le promise). Facciamo un esempio: Nel codice riportato affianco si hanno 3 funzioni che sono ask(), showOk() e showCan- cel(). La funzione ask(), può essere ovviamente riuti- lizzata, e la cosa interessante è che passandogli question, yes e no, posso dargli un compor- tamento diverso perché gli si possono passare le showOk e lo showCancel, quindi come argo- mento della funzione ask posso passare delle fun- zioni. Guardando dentro il primo blocco di codice si può vedere la logica, in sintesi posso andare ad utilizzare ask cambiando le implementazioni di yes e di no. Questo ci permette di avere una versatilità molto elevata. Le callback risultano di particolare interesse, perché: permettono di ridurre il codice ripetuto; il codice risulta facilmente leggibile; consentono di avere un elevata modularità  ad esempio nel caso in cui si devono gestire i dati ritornati da un API, con un’unica callback si possono gestire diverse situazioni (molto codice è incap- sulato nella callback). 34 Document shared on https://www.docsity.com/it/appunti-di-programmazione-avanzata/8789182/ Programmazione Avanzata A.A. 2021/2022 Margherita Galeazzi High order functions e Array Si vedrà come il codice scritto nella maniera standard viene semplificato utilizzando la programmazione fun- zionale. Si vedrà che non c’è una dipendenza con l’esterno quindi potenzialmente non ci sono side effect. arr.forEach(callback) Per poter andare a iterare sugli array, si può optare per la maniera classica: oppure lo si può fare tramite un for each. Il for each può essere utilizzato in vari modi ma quello che al momento e per noi d’interesse è il segu

Use Quizgecko on...
Browser
Browser