vuoi
o PayPal
tutte le volte che vuoi
Il programma deve implementare la soluzione del problema e i concetti del paradigma.
1. Programmazione Imperativa:
Il paradigma imperativo è uno dei più antichi e diffusi stili di programmazione. In questo approccio, un
programma è concepito come una sequenza di istruzioni che modificano lo stato del sistema. Possono essere la
lettura input, computazione o scrittura output. Il programma esegue una serie di passi sequenziali per
raggiungere il risultato desiderato.
Meccanismo di astrazione: procedura (istruzione “complessa” - inventata da me) si riutilizza.
I costrutti fondamentali della programmazione imperativa includono:
Assegnamenti: il cambiamento dei valori delle variabili.
● Istruzioni condizionali: che permettono di eseguire determinate istruzioni solo in base a certe condizioni.
● Cicli: per ripetere una serie di istruzioni fino a quando una condizione non viene soddisfatta.
●
I linguaggi imperativi più noti includono FORTRAN, Cobol, C e Pascal. In questi linguaggi, l'attenzione è rivolta alla
manipolazione diretta della memoria attraverso istruzioni che descrivono "come" eseguire una computazione,
piuttosto che "cosa" computare. Ad esempio, per calcolare la somma di numeri in un array, il programmatore
deve specificare esplicitamente il ciclo che itererà attraverso l'array e accumulerà la somma. 3
2. Programmazione Orientata agli Oggetti (OOP):
La programmazione orientata agli oggetti (OOP) rappresenta un'evoluzione del paradigma imperativo,
introducendo un modello che organizza il software come una collezione di oggetti che interagiscono tra loro. Un
oggetto è una rappresentazione di un'entità del mondo reale o concettuale, che possiede uno stato
(rappresentato da attributi) e un insieme di comportamenti (metodi).
I concetti fondamentali dell'OOP includono:
Classificazione: la definizione di classi che rappresentano categorie di oggetti.
● Ereditarietà: meccanismo che permette di definire nuove classi basate su altre già esistenti, riutilizzando
●
e modificando il comportamento.
Incapsulamento: la protezione dello stato interno degli oggetti, consentendo l'accesso solo tramite
●
metodi definiti.
L'OOP facilita la modellazione di sistemi complessi poiché permette di organizzare il codice attorno alle entità del
problema, favorendo il riuso del codice e la modularità. Tra i linguaggi che supportano pienamente questo
paradigma troviamo Smalltalk, C++, Java, C# e Ruby. Un esempio classico di OOP è la rappresentazione di un
"veicolo" come oggetto, con attributi come la velocità e metodi come "accelera" o "frena". Le interazioni tra gli
oggetti avvengono tramite lo scambio di messaggi (chiamate ai metodi).
3. Programmazione Funzionale:
Il paradigma funzionale si basa sul concetto di funzione matematica e adotta un approccio dichiarativo.
Piuttosto che eseguire una sequenza di istruzioni, un programma funzionale è concepito come una collezione di
funzioni che trasformano valori di input in valori di output, senza modificare lo stato del sistema o i dati esterni.
Ogni funzione ha il dominio e il codominio.
I concetti chiave della programmazione funzionale includono:
Composizione di funzioni: combinazione di funzioni più semplici per costruire funzioni più complesse.
● Ricorsione: sostituzione di cicli con funzioni che chiamano se stesse.
● Immutabilità: le variabili non possono essere modificate una volta assegnate, migliorando la prevedibilità
●
e la sicurezza del codice.
I linguaggi funzionali più rappresentativi sono Lisp, Scheme, ML e Haskell. In questi linguaggi, si evitano variabili
mutabili e assegnamenti. Ad esempio, in Haskell, per sommare i numeri in una lista si usa la ricorsione. INfatti
può modellare le liste infinite, cioè liste pigre (generano gli elementi fino a quando mi servono).
La programmazione funzionale è particolarmente apprezzata per la sua eleganza e chiarezza, poiché consente
di esprimere le soluzioni ai problemi in modo compatto e privo di effetti collaterali. Questo la rende molto utile
nel calcolo parallelo e nella programmazione concorrente. 4
4. Programmazione Logica
La programmazione logica rappresenta un paradigma dove un programma è visto come una collezione di
dichiarazioni logiche (chiamate clausole e sono delle funzioni logiche booleane), che specificano cosa deve essere
computato piuttosto che come. La soluzione di un problema viene trovata applicando queste dichiarazioni per
derivare conclusioni logiche. Sono problemi risolvibili mediante “tentativi”.
Meccanismi di Astrazione:
Dichiarazioni logiche: I fatti e le regole descrivono le relazioni tra le entità del problema.
● Backtracking: Se una strada non porta alla soluzione, il sistema torna indietro e esplora alternative
●
(ricerca esaustiva).
Nondeterminismo: Non c'è una singola soluzione al problema; il sistema può trovare più soluzioni valide.
●
Esempi di linguaggi logici:
Prolog
●
In questo paradigma, l'accento è posto sul definire la relazione tra dati e le regole per trovare soluzioni, con il
motore logico che si occupa di esplorare lo spazio delle soluzioni possibili.
Linguaggi di programmazione e architettura degli elaboratori
I linguaggi di programmazione (LP) e l'architettura degli elaboratori hanno una relazione strettamente
interconnessa. Da un lato, i metodi di design del software influenzano lo sviluppo e la progettazione dei linguaggi
di programmazione, imponendo requisiti affinché questi ultimi supportino meglio lo sviluppo software. Dall'altro,
l'architettura degli elaboratori, in particolare il modello di Von Neumann, pone requisiti sui linguaggi di
programmazione affinché possano essere implementati in modo efficiente sulle macchine attuali. Questa
interazione ha plasmato l'evoluzione sia dei linguaggi di programmazione che delle architetture computazionali,
portando a una continua ricerca di equilibrio tra astrazione e prestazioni.
Linguaggi di Programmazione e Metodi di Design
I linguaggi di programmazione non si sviluppano in un vuoto, ma sono influenzati dai metodi di design del
software che vengono utilizzati nella pratica. I metodi di design, che comprendono concetti come la progettazione
modulare, la riusabilità del codice e la manutenibilità, stabiliscono implicitamente i requisiti sui linguaggi di
programmazione affinché possano meglio supportare il processo di sviluppo del software.
Un linguaggio di programmazione ben progettato deve quindi fornire costrutti e astrazioni che facilitino il design
del software, offrendo strumenti per la gestione della complessità, la modularità e il riuso del codice. In questo
modo, l'interfaccia tra i metodi di design e i linguaggi di programmazione è bidirezionale: i metodi di design
5
influenzano la progettazione dei linguaggi e, viceversa, i linguaggi di programmazione determinano il modo in cui
il design del software può essere implementato.
Architettura degli Elaboratori e Linguaggi di Programmazione
Un secondo fattore che influisce sui linguaggi di programmazione è l'architettura degli elaboratori. In particolare,
il modello di Von Neumann ha avuto un ruolo chiave nello sviluppo dei linguaggi di programmazione imperativi,
influenzandone la struttura e il funzionamento.
Architettura di Von Neumann
L'architettura di Von Neumann, descritta per la prima volta negli anni '40, è il modello di base su cui si fondano la
maggior parte dei moderni elaboratori. In questo modello, la CPU esegue istruzioni in modo sequenziale,
prelevandole dalla memoria. Il processo tipico di esecuzione di un'istruzione prevede tre fasi:
1. Prelievo dei dati dalla memoria: La CPU legge i dati necessari per l'operazione dalla memoria.
2. Manipolazione dei dati: L'Unità Aritmetico-Logica (ALU) esegue l'operazione richiesta sui dati.
3. Scrittura dei risultati nella memoria: I risultati dell'operazione vengono copiati nuovamente nella
memoria.
Questa architettura ha un'influenza diretta sul modo in cui i linguaggi di programmazione sono progettati. I
linguaggi imperativi, in particolare, sono una diretta astrazione dell'architettura di Von Neumann.
Linguaggi di Programmazione Imperativi e Architettura di Von Neumann
I linguaggi di programmazione imperativi, come FORTRAN, C, e Pascal, sono strettamente legati al modello di Von
Neumann. In un linguaggio imperativo, un programma è visto come una sequenza di istruzioni che vengono
eseguite in modo sequenziale, modificando lo stato del programma tramite operazioni sulle variabili.
1. Esecuzione sequenziale delle istruzioni: Il modello computazionale di un linguaggio imperativo si basa
sull'esecuzione sequenziale delle istruzioni, esattamente come la CPU esegue istruzioni in sequenza nella
macchina di Von Neumann.
2. Variabili e memoria: Una variabile in un linguaggio imperativo è un'astrazione di una cella di memoria.
Ogni variabile ha un nome e un valore, proprio come ogni cella di memoria ha un indirizzo e un contenuto.
3. Stato della computazione: Lo stato di un programma imperativo è rappresentato dai valori delle sue
variabili, proprio come lo stato di un elaboratore Von Neumann è determinato dal contenuto della sua memoria.
In questo contesto, i linguaggi imperativi forniscono un modello computazionale naturale per le macchine di Von
Neumann, poiché riflettono il modo in cui la CPU manipola dati e istruzioni. Tuttavia, con l'evoluzione dei
linguaggi di programmazione, si è cercato di superare alcune delle limitazioni di questo modello, portando a
linguaggi con livelli di astrazione più elevati. 6
Abbandono del Modello di Von Neumann
Nonostante la sua importanza storica, il modello di Von Neumann non è perfetto. Esistono problemi di efficienza
e flessibilità che hanno spinto verso modelli computazionali alternativi. L'architettura di Von Neumann impone
una forte dipendenza dalla sequenzialità delle operazioni e dalla manipolazione esplicita dello stato del
programma, cosa che può essere un limite per certi tipi di problemi o per architetture parallele e distribuite.
Per superare questi limiti, sono stati introdotti nuovi paradigmi di programmazione che abbandonano il modello
computazionale basato su Von Neumann. Alcuni di questi nuovi paradigmi si fondano su basi matematiche, come
la teoria delle funzioni ricorsive e la logica matematica, e propongono approcci diversi al calcolo:
1. Programmazione funzionale: Questo paradigma, presente in linguaggi come Haskell e Lisp, si basa sulla
logica delle funzioni matematiche, evitando la manipolazione esplicita dello stato. Le funzioni pure non hanno
effetti collaterali e non modificano lo stato della memoria, in contrasto con il modello di Von Neumann, che si
basa sull'alterazione de