Come abbiamo visto nel post precedente, un Computer non è altro che una CPU che esegue dei comandi e manipola dei dati sfruttando la memoria RAM e interagendo con le periferiche, e perché sappia cosa fare è necessario che sia il programmatore a dirglielo.
Per evitare di scrivere i programmi in codice binario è stato inventato un linguaggio con istruzioni più semplici da ricordare e da scrivere, chiamato Assembler.
Tuttavia con l'aumentare della potenza delle CPU e della capacità delle memorie RAM, nasceva l'esigenza di scrivere programmi sempre più complessi, ed era impensabile scriverli in ASM. L'efficenza passava in secondo piano e cresceva la necessità di un linguaggio più semplice e slegato dai dettagli dell'architettura del computer.
Così iniziarono a nascere dei linguaggi più a portata d'uomo, che rendevano possibile concentrarsi sul problema da risolvere, mettendo in secondo piano la programmazione fisica della macchina.
All'inizio nacquero i cosiddetti linguaggi "imperativi", dove il codice è un blocco unico formato da istruzioni che si susseguono l'una dopo l'altra.
Vediamo un esempio di programmazione imperativa:
1. LEGGI numero1;
2. LEGGI numero2;
3. SOMMA risultato = numero1 + numero2;
4. MOSTRA risultato;
5. SALTA A 1;
Come possiamo vedere con i linguaggi imperativi il programmatore non necessita di gestire l'hardware a basso livello, ma si limita a scrivere operazioni logiche trascurando registri, segmenti di memoria, interrupt e in generale tutto ciò che non ha a che fare con il problema da risolvere.
Tuttavia è un paradigma di programmazione ancora rudimentale, disordinato, poco leggibile.
In seguito nacquero altri paradigmi di programmazione, più ordinati ed eleganti, che permettevano di racchiudere vari blocchi di codice in procedure separate, rendendo il tutto più leggibile.
Tra i linguaggi facenti parte di questa nuova categoria, quello che ebbe un maggior successo fu il linguaggio C.
Tra i motivi del successo di questo linguaggio i principali sono le performance e la possibilità di programmare ad un livello basso di astrazione (quindi più vicino all'hardware), addirittura permettendo di scrivere istruzioni in Assembler all'interno dello stesso codice C.
Queste caratteristiche lo rendono un linguaggio potente, al punto che ancora oggi è uno dei linguaggi più utilizzati.
Per fare solo un esempio il Kernel Linux è scritto quasi tutto in C, con solo le parti più critiche scritte in ASM.
Tuttavia le sue potenzialità possono trasformarsi in strumenti pericolosi se a gestirli è un programmatore sbadato. Infatti il C permette una gestione a basso livello della memoria, rendendo più probabile la presenza di bug nel programma, che possono risultare in errori di vario genere e nel crash del programma sino al blocco totale del computer.
Il C appartiene alla categoria dei linguaggi procedurali. Alla stessa categoria fanno parte altri linguaggi come il Pascal, il Fortran, il Basic ed altri più o meno conosciuti.
Un linguaggio è procedurale quando utilizza appunto delle procedure, che non sono altro dei blocchi di codice.
Un esempio di programma procedurale in pseudo-linguaggio può essere il seguente:
INIZIO PROCEDURA somma(numero1, numero2);
risultato = numero1 + numero2;
RESTITUISCI risultato;
FINE PROCEDURA
Come possiamo vedere una procedura è un blocco di codice che riceve in input dei dati, li elabora e restituisce in output il risultato dell'elaborazione.
Alcuni linguaggi funzionali sono anche linguaggi strutturati, e cioé forniscono delle strutture di controllo per gestire il comportamento del programma in base a delle condizioni.
Vediamo un esempio:
INIZIO PROCEDURA divisione(dividendo, divisore);
SE: divisore UGUALE 0:
RESTITUISCI errore!;
ALTRIMENTI:
risultato = dividendo / divisore;
RESTITUISCI risultato;
FINE PROCEDURA
La procedura appena mostrata è molto simile a quella precedente, ma introduce una struttura di controllo.
Nel caso specifico si tratta di un'istruzione che controlla se il divisore è uguale a 0 o no.
Se è uguale a 0 restituisce errore perché è impossibile dividere un numero per zero;
se invece il numeratore è diverso da 0 esegue la divisione e restituisce il risultato.
Ricapitoliamo l'evoluzione dei linguaggi di programmazione vista sino ad ora:
- Codice macchina: le istruzioni e i dati sono rappresentati in codice binario;
ad esempio 00111 potrebbe rappresentare un'istruzione di somma di due numeri.
- Assembly: un linguaggio di basso livello, che utilizza dei codici mnemonici che rappresentano i corrispettivi codici macchina;
ad esempio un'istruzione per sommare due numeri è rappresentata dal codice mnemonico SUM.
Tuttavia siamo ancora legati alla logica dell'hardware, dobbiamo conoscere il nome dei registri, sapere come gestire la memoria e i dispositivi di input/output ecc..
- Linguaggi imperativi: chi li utilizza non necessita di conoscere i dettagli dell'hardware per poter programmare. Hanno una struttura lineare dove il codice è formato da una successione di istruzioni.
- Linguaggi procedurali: sono linguaggi di medio/alto livello, che utilizzano istruzioni generiche per eseguire compiti comuni;
ad esempio una routine che in Assembly può richiedere varie linee di codice per sommare due numeri e mostrarli a schermo, in un linguaggio procedurale (così come in un linguaggio imperativo) può essere rappresentata da una sola istruzione "print 1 + 2;".
Come abbiamo avuto modo di vedere i linguaggi di alto livello ci sollevano dal compito di dover conoscere l'hardware sottostante, permettendoci di utilizzare istruzioni più vicine ad una logica umana.
I linguaggi procedurali aggiungono ai linguaggi imperativi la possibilità di suddividere il codice in blocchi, rendendolo più leggibile e più facile da scrivere.
- Linguaggi strutturati: sono linguaggi procedurali che utilizzano anche istruzioni di controllo, che vedremo più avanti nello specifico.
Allo stato attuale procedure (o funzioni) e strutture di controllo sono elementi fondamentali dei linguaggi di programmazione moderni.
Infatti questi elementi non sono stati rimossi o sostituiti, ma anzi continuano a rappresentare le fondamenta di ogni linguaggio.
Tuttavia i linguaggi di programmazione moderni si sono preoccupati di espandere i linguaggi procedurali/strutturati, implementando nuovi paradigmi di programmazione che portano il livello di astrazione dalla macchina ad un piano ancora più elevato e quindi più vicino al modo di ragionare di un essere umano.
Questi linguaggi prendono il nome di "linguaggi di programmazione ad oggetti", che tuttavia non hanno sostituito del tutto la programmazione procedurale, tant'è vero che il C, uno dei linguaggi di programmazione più utilizzati, non contempla la presenza del paradigma ad oggetti.
Allo stesso modo tra i linguaggi di programmazione ad oggetti, non tutti sono linguaggi ad oggetti "puri", alcuni includono solo in parte degli elementi di programmazione ad oggetti, altri sono ibridi e lasciano al programmatore la scelta di utilizzare o meno un paradigma di programmazione ad oggetti.
Per correttezza devo dire che tanti programmi professionali vengono scritti in vari linguaggi senza l'ombra di programmazione ad oggetti, eppure svolgono egregiamente il loro compito.
Tuttavia bisogna dire anche che la programmazione ad oggetti migliora notevolmente la leggibilità e quindi la mantenibilità del codice.
Per concludere, possiamo dire che tutti i linguaggi di programmazione moderni mettono a disposizione delle strutture di controllo e la possibilità di divirere il codice in procedure e funzioni.
Possiamo affermare che quanto appena detto è comune a tutti i linguaggi moderni più importanti, e cioé strutture di controllo e procedure sono elementi ancora fondamentali e attuali.
Tra questi linguaggi di programmazione, alcuni implementano anche il paradigma ad oggetti, in varia misura, rendendo il processo di scrittura del codice più semplice avvicinandolo ancora di più al modo di ragionare dell'essere umano.
Nel prossimo post vedremo nei particolari cos'è un linguaggio di programmazione ad oggetti e faremo alcuni esempi pratici.
Nessun commento:
Posta un commento