Introduzione alla programmazione Questa sezione è dedicata alle idee che stanno alla base dellinformatica. Quello che caratterizza questa parte della scienza è il suo rapporto strettissimo con le macchine che eseguono programmi, i calcolatori, che ha fatto sì che essa si sia potuta sviluppare compiutamente solo nellultimo secolo. Eppure, anche se non viene mai rimarcato se non nelle introduzioni dei corsi di informatica, nella sua essenza questa è solo una parte della matematica e può essere concepita senza rapporto con alcuna macchina elettronica. La sola cosa importante è "lazione meccanica" intesa come azione che segue regole precise, ineluttabili, senza comportamenti "casuali" o "fantasiosi", come quelle di una macchina appunto.
Da questo punto di vista, la scherzosa risposta che alla domanda "Cosa
cè dentro al calcolatore che esegue il lavoro?" replica "Un
gran numero di piccoli giapponesini obbedienti", contiene lessenza
della questione: non importa chi e come esegue le azioni, ma solo che queste
vengano fatte con il massimo scrupolo e precisione. Per questo certamente
si sceglie sempre, nelle varianti della barzelletta, un popolo dellestremo
oriente, nellimmaginario sempre associato a precisione ed obbedienza
ma anche, di conseguenza, ad una mancanza di fantasia ed inventiva. E interessante notare come vengano descritti in questa ottica i "bachi" (bug) che affliggono i programmi per computer ed i conseguenti "blocchi" (crash) che essi possono causare. Il baco è semplicemente la situazione di qualcuno che non ha capito cosa doveva fare o come doveva farlo, come quando il re-utente da un ordine al generale-programmatore perché lesercito-macchina lo esegua, ma il generale non ha capito cosa fare o come farlo. In questo caso lesercito esegue perfettamente il suo compito senza sapere che non era quello che il re voleva e senza rendersi conto che qualcosa non va. I blocchi invece sono situazioni inaspettate a cui la macchina non sa reagire, entrando in blocco, come quando lesercito, nellesecuzione di una manovra si trova in una situazione non prevista per cui non ha ricevuto addestramento. Sarebbe stato compito del generale-programmatore studiare una tattica-programma funzionante per evitare la situazione inaspettata. Dal punto di vista dellutente-re leffetto finale è sempre il disastro, la colpa è in ogni caso del programmatore-generale, ma nei due casi (anche se spesso un baco causa poi un blocco), la situazione è completamente diversa, perché nel primo caso lazione viene svolta perfettamente dalla macchina-esercito, solo che non era quella voluta dallutente-re, mentre nel secondo invece la macchina-esercito non può proseguire e si inceppa non sapendo cosa fare. Gli argomenti rilevanti di questo campo possono essere divisi in due: da una parte cè la discussione del concetto di algoritmo e della sua rappresentazione, la macchina di Turing, cioè del "cosa fare e come farlo", dallaltra quella dei linguaggi di programmazione, cioè del "come esprimerlo". Le due cose nella realtà sono andate ovviamente di pari passo, interagendo tra loro, ma in una discussione concettuale è bene tenerle distinte. Nella prima parte è centrale il concetto di algoritmo, utilizzato in matematica da sempre, dal famoso algoritmo di Euclide tanto per fare un esempio, ma precisato concettualmente solo in questo secolo dal matematico inglese Alan Turing, inventore della famosa "macchina". Qui la ferraglia non centra nulla, essendo questa macchina solo una specifica delle poche, limitatissime cose che essa sa fare. Eppure questa limitatissima versione di un calcolatore è, dal punto di vista della capacità di portare a termine un programma, esattamente potente quanto il più sofisticato elaboratore attuale, lunica differenza rilevante essendo la velocità di esecuzione. Seguono poi i modelli astratti di calcolatore inventati per darne una rappresentazione più realistica: la macchina di Von Neumann e la macchina ad accesso casuale o RAM, Random Access Machine (da non confondere con Random Access Memory, che è un concetto hardware). Questi argomenti sono in stretto contatto con larchitettura reale dei calcolatori e con il loro linguaggio macchina. Questo tipo di linguaggi costituisce quindi il primo e più elementare "stile" per un linguaggio di programmazione. Alcuni dei linguaggi di cosiddetto alto livello in realtà possono essere visti come lastrazione dalla particolare architettura della macchina "fisica" attraverso luso delle istruzioni di una macchina "logica" definita dallo stesso linguaggio. Piuttosto che programmare nellAssembler della macchina fisica si programma per esempio in C, che non a caso è stato definito con il termine autocontraddittorio di "assembler multipiattaforma", con questo intendendo che le sue istruzioni sono elementari al punto di poter pensare di costruire una "macchina C" reale che le esegua direttamente come fossero parte di un vero Assembler. Un poco più su come livello di astrazione ci sono i cosiddetti linguaggi imperativi, in cui lidea di fondo è che il programmatore ha ancora lobbligo di definire in tutti i dettagli lalgoritmo che sta costruendo, ma ha a disposizione strumenti che richiamano più un linguaggio naturale che la macchina che lo esegue. Importante è qui lidea di programmazione strutturata, con il famoso teorema di Jacopini-Böhm, che dice sostanzialmente che si può programmare senza i "goto", sostituiti dai concetti di blocco, di alternativa e ciclo. Importanti sono anche i concetti di procedura e programmazione ricorsiva, e di struttura dati con puntatore, i quali, assieme al controllo di tipo, sono i primi rudimentali elementi del concetto di tipo di dati astratto (ADT, Abstract Data Type) della programmazione ad oggetti. In questa categoria si possono far rientrare, pur con la difficoltà che tutte le classificazioni comportano, tutti i linguaggi più noti, a cominciare dal Fortran per proseguire con tutti i suoi eredi più o meno diretti, cioè Algol, Pascal, Modula e Ada da una parte, BCPL e C dallaltra. Rientra in questa categoria anche il Basic, il quale, chissà perché, viene sempre ignorato nelle storie dei linguaggi di programmazione, pur avendo un peso notevole, se non altro per chi si avvicina alla programmazione attraverso i PC. Lo stile imperativo non è lunico possibile, infatti sono noti da molto tempo linguaggi come Lisp e Prolog, rappresentanti rispettivamente dello stile funzionale e dello stile logico di programmazione. In comune hanno leliminazione dellassegnamento di valore ad una variabile, sostituita dalluso di funzioni e relazioni. La loro espressività è molto diversa da quella dei programmi imperativi, tanto da essere linguaggi di elezione nei campi dellintelligenza artificiale. Un altro tipo di evoluzione dei linguaggi è quello verso la cosiddetta programmazione ad oggetti (OOP, Object Oriented Programming), settore che attualmente attrae il maggior numero di studi ed attività, promettendo un rivoluzione nella capacità di costruire programmi per sistemi complessi. Una caratteristica dei linguaggi imperativi che li rende inadatti a questo scopo è infatti quella che con essi il programmatore ha sempre limpressione di dover partire da zero, anche quando riutilizza parti di programmi scritti da altri. Il disagio provocato da questa situazione è stato ben espresso da R.W.Hamming: "laddove Newton poteva dire di aver visto un po più avanti degli altri perché era potuto salire "sulle spalle di giganti", io sono costretto a dire che oggi noi stiamo uno sui piedi dellaltro". La capacità di utilizzare il lavoro di altri viene, nei linguaggi OO, dai concetti di incapsulamento, ereditarietà e polimorfismo. Questi permettono di dare al linguaggio che li implementa, sia la potenza dellastrazione matematica che la flessibilità del linguaggio naturale. Queste due cose sono spesso in contraddizione e non a caso esistono diversi tipi di linguaggi OO, che propendono verso uno o laltro dei due versanti. La differenza è nei due modi in cui possono essere implementati i controlli di tipo. Se il controllo è dinamico si hanno i linguaggi alla Smalltalk, flessibili ed espressivi come i linguaggi naturali, ma anche con i problemi di imprecisione di questi, se invece è statico si hanno i linguaggi alla Eiffel, perfetti per implementare il metodo "astratto" della matematica moderna, con tutta la sua potenza e precisione, ma anche, di conseguenza, meno "facili". I linguaggi OO sono derivati tutti dal capostipite Simula, da cui direttamente discendono Beta ed Eiffel. Simula ha però trovato una reinterpretazione terminologica e quasi "filosofica" in Smalltalk, che ha finito per trovarsi al centro dellattenzione, come fosse il progenitore della specie. Nella realtà però, la purezza di impostazione che caratterizza i linguaggi appena citati non ha trovato gran riscontro, forse perché le idee innovative faticano sempre ad affermarsi. Così in giro si sente parlare prevalentemente di linguaggi "misti", che altro non sono che linguaggi imperativi a cui sono stati sovrapposti i costrutti tipici della OOP: dal C sono derivati Objetive-C, C++ e Java, dal Pascal lObject Pascal (Delphi), dal Modula-2 Modula-3 e da Ada Ada 95. Qualcuno di questi concetti è filtrato addirittura nella struttura del Basic. In realtà questi inserimenti sono problematici perché i due stili di programmazione, imperativo e ad oggetti, sono in conflitto. Forse il risultato migliore in questo campo è stato raggiunto da Oberon, il successore di Modula-2 secondo lautore suo (oltre che del Pascal), Niklaus Wirth, perché qui si permane nello stile imperativo ed i concetti che allinterno del linguaggio permettono la programmazione ad oggetti (procedure legate ad un tipo + estensione dei tipi) sono viste nella loro forma più scheletrica ed elementare. Parallelamente a queste divisioni di stile, anche se sarebbe meglio dire perpendicolarmente, si sviluppano i concetti della programmazione concorrente e ad eventi. Il primo riguarda le idee di esecuzione contemporanea di più processi o sottoprocessi (task e thread), con i problemi di sincronizzazione ed attesa, con i concetti di semafori e risorse. Il secondo riguardo il concetto di interruzione del flusso di programma e salto alla procedura di gestione dellevento. Entrambi devono fare i conti con i problemi delle cosiddette sezioni critiche, cioè parti di programma non interrompibili. Tutti questi argomenti sono fondamentali per i sistemi operativi e sono strettamente connessi ai dispositivi hardware che permettono di gestirli. Come si vede, i possibili argomenti di discussione in questa materia sono moltissimi, e tanti tra essi meritano di essere conosciuti anche nel caso non si intendesse scrivere nemmeno una riga di programma nella propria vita. Il taglio che intendiamo dare alla discussione degli argomenti che col tempo riusciremo ad inserire in questa sezione vuole essere precisamente questo, e cioè un modo di vedere più orientato alle singole idee trainanti che ai vari particolari concreti di questo o quellargomento. Questo non esclude che quando sarà il caso, si espongano anche nei dettagli singole caratteristiche di un dato linguaggio, ma sempre nell'ottica di spiegare l'idea corrispondente, non particolari "usi e costumi".
|