programma (informatica)
IndiceDescrizione generale
In termini rigorosi con programma si indica un'unità sintattica, formata da costrutti di dichiarazione e da istruzioni, comprendente tutti gli elementi necessari per risolvere un problema, eseguire una funzione, svolgere un compito. Composta in base a regole di linguaggi artificiali, essa è gestibile da appropriati sistemi di programmazione, in simbiosi con gli elaboratori, deputati a utilizzare il programma stesso. Il programma quale insieme sequenziale di statements – frasi scritte in un linguaggio comprensibile sia al programmatore che alla macchina – è un documento. Quando però viene eseguito, il programma diventa un evento, un processo che trasforma dati in risultati. Nel suo complesso il programma può essere visto come una macchina logica: “documento” se la macchina è spenta, “evento” se la macchina è attiva, con l'elaboratore che fornisce l'energia e muove, con estrema tempestività e precisione, la macchina logica stessa. Questa interpretazione illustra il carattere potenzialmente universale di un programma, che ne permette l'utilizzazione su un'ampia gamma di elaboratori dissimili. Naturalmente le caratteristiche conferite dalla progettazione del programma e in primo luogo la scelta del linguaggio di programmazione saranno state studiate per renderlo indipendente dall'elaboratore ospitante, una proprietà chiamata portabilità.
Struttura e funzionamento
Le strutture di dati (insiemi organizzati di informazioni) e gli algoritmi (procedure definite per la soluzione di un problema) sono i materiali impiegati nella costruzione dei programmi. A livello fondamentale l'elaboratore può lavorare con un solo tipo di dati, i bit; può operare secondo un unico insieme di algoritmi (nell'ordine delle centinaia), quelli definiti dalle istruzioni dell'unità centrale-CPU, anch'essi in bit. Si possono ipotizzare almeno tanti algoritmi quanti sono i problemi riconducibili all'elaborazione (un'infinità di problemi: quelli computazionali). Dato che l'elaboratore opera su bit, il programma dovrà essere espresso nel codice prescelto per il set di istruzioni. Inoltre esso dovrà essere completamente specificato in quanto l'elaboratore, non disponendo di alcuna capacità di iniziativa, richiede che anche operazioni evidenti alla mente umana non vengano tralasciate. Così concepito il programma diventa, in scrittura, una serie di istruzioni e, in esecuzione, una sequenza ordinata di passi di programma. Un programma è in genere sequenziale, con le eccezioni (frequenti) specificate da particolari istruzioni di salto. L'elaboratore – che tiene il conto dei passi di programmi eseguiti, grazie a un orologio basato sul tempo di esecuzione dell'istruzione generica – passa automaticamente da un'operazione alla successiva. Un passo tipico è il seguente. Riconosciuto il carattere di programma (dal nome del programma stesso o dall'indirizzo nella memoria riservato al programma), l'elaboratore predispone i propri circuiti e preleva dalla memoria una prima stringa (una serie di cifre) di bit che, proprio perché considerata dalla macchina un'istruzione, viene immessa nel registro di decodifica. La decodifica dell'istruzione – per esempio caricare in un registro dell'unità aritmetica un dato il cui indirizzo di memoria è contenuto in una parte della stringa stessa – consiste nella configurazione automatica dei circuiti dell'elaboratore in modo da realizzare un collegamento elettrico fra il registro aritmetico e la locazione di memoria individuata dall'indirizzo del dato. Si trasferisce così una seconda stringa di bit, indistinguibile come tale dalla prima, ma vista come dato in virtù dell'origine del comando e della destinazione del trasferimento; completata l'operazione l'elaboratore passa automaticamente all'istruzione successiva. È scontato che l'elaboratore trovi i dati necessari agli indirizzi indicati via via dal programma. La struttura base del programma "Per il diagramma elementare a blocchi di un programma che determina la Somma di N numeri vedi pg. 21 del 18° volume." "Per il diagramma di un programma che determina la somma di N numeri vedi il lemma del 16° volume." comprenderà un inizio (indirizzo/nome) con la specifica dei dati; seguiranno le istruzioni vere e proprie, che potranno essere di lettura e messa a dimora in memoria di dati e costanti, di trattamento delle informazioni (calcoli, elaborazione di immagini, ecc.), di organizzazione dei risultati per la loro presentazione su video, stampa, ecc.; un'ultima sezione del programma ne specifica il completamento, che libera l'elaboratore per altra funzione, ossia per il programma successivo. Tutte le attività considerate vengono svolte in rappresentazione binaria. A ciascuna istruzione è associata una stringa di zeri e uno e quindi programmare significherebbe (e ha significato ai primordi) la scrittura di lunghi elenchi di stringhe: per esempio 10101001 può voler dire sommare (e 10101010 sottrarre). È il cosiddetto programma in linguaggio macchina (programma oggetto), pressoché inaccettabile per l'uomo per noia, ripetitività e astrusità. Il problema è stato aggirato grazie alla potenza e flessibilità del concetto di programma. A ogni istruzione è stata assegnata una sigla (ADD per addizione; MOV per move, “sposta”, ecc.); è stato scritto, una volta per tutte, un programma per tradurre ADD in codice binario (10101001) e così via.
Evoluzione dei programmi
Si è venuto così precisando il concetto di linguaggio quale insieme di regole per scrivere un programma più comprensibile, chiamato programma sorgente; esso presuppone l'esistenza di un programma traduttore (chiamato nel caso specifico assembler) che traduce il programma sorgente nel programma oggetto. Si è effettuato in questo modo un primo passo verso l'indipendenza del programma dall'elaboratore. Da un lato si avrà un generico programma nel linguaggio assembler che ha molti elementi di generalità (ADD significherà addizione per tutti, indipendentemente dalla stringa di bit che lo rappresenta nel mondo dell'elaboratore in considerazione); dall'altro lato vi sarà un programma traduttore, che sarà specifico per l'elaboratore considerato, ma che viene scritto una volta per tutte. Per l'assembler vi è corrispondenza uno a uno delle sigle con le stringhe del set di istruzioni base dell'elaboratore. Nella ricerca dell'indipendenza dalla macchina e della semplificazione del lavoro di specifica delle istruzioni si è andati oltre l'assembler, verso linguaggi via via più distanti dal linguaggio macchina e sempre più vicini a un linguaggio umano, in realtà un linguaggio più leggibile ma sempre estremamente arido. Il programma traduttore è divenuto così più complesso e si è rinunciato alla corrispondenza biunivoca istruzione in linguaggio sorgente-istruzione in linguaggio macchina. I programmi traduttori si sono inoltre specializzati in compilatori (compilers), che effettuano la traduzione in linguaggio macchina una volta per tutte, e in interpreti, che convertono le istruzioni una per una e le eseguono contestualmente permettendo quindi, a spese dell'efficienza di conversione, un dialogo continuo con l'utente. Le regole per queste scritture di alto livello sono diventate molto articolate e si sono organizzate in sintassi, che viene riconosciuta dal traduttore prima di effettuare la traduzione vera e propria. La semplificazione del lavoro di programmazione è stata perseguita anche tramite “unificazione” di operazioni ripetitive. Per esempio la lettura dei dati, in rapporto alla periferica che lo fornisce (tastiera, disco, modem, ecc.), o la visualizzazione sullo schermo possono essere scritte una volta per tutte. È questa l'idea alla base del sistema operativo "Per lo schema del sistema operativo vedi pg. 21 del 18° volume." "Per un sistema operativo vedi figura al lemma dell'Aggiornamento 1995." che all'origine era semplicemente l'insieme dei programmi che permettono all'utente di impiegare programmi applicativi (propri o preconfezionati) senza preoccuparsi di compiti quali i trasferimenti di memoria, il salvataggio dei programmi e dei dati e la gestione delle risorse fisiche del sistema di elaborazione (unità centrale, gerarchie di memoria, periferiche, comunicazioni). Per la funzione di raccordo fra il sistema fisico, con specificità fortemente dipendenti dal progetto dell'elaboratore e dal livello di sviluppo tecnologico all'epoca della realizzazione, e i programmi applicativi scritti in linguaggi molto generali, il sistema operativo ha assunto compiti sempre maggiori. Grazie alla progressiva indipendenza programma-elaboratore si sono sviluppati programmi per applicazioni (la videoscrittura è l'esempio forse più noto) che costituiscono una categoria di prodotti che sono scritti per macchine virtuali da organizzazioni specializzate (software houses). Essi si appoggiano ai sistemi operativi per trasformare l'elaboratore in macchina da scrivere, tavolo da disegno, tabellone elettronico, simulatore di operazioni (per esempio di volo). Scrivere un programma è un'attività ad alta intensità di lavoro. Se gli elaboratori, le periferiche e le altre apparecchiature di elaborazione dell'informazione (il cosiddetto hardware) hanno fatto registrare riduzioni di costo notevolissime, il costo del corredo di programmi (il software), la cui espansione non ha avuto sosta mentre sono aumentati i requisiti di qualità e la complessità dei problemi da risolvere, è stato invece sistematicamente crescente. Il programma è diventato uno strumento che si inserisce in un ambiente più ampio, un insieme compatibile di programmi, con il quale deve poter convivere. Il termine software, inizialmente sinonimo di programma, si è ampliato e comprende non solo i programmi stessi ma anche i dati a essi associati, la relativa documentazione e le procedure per sviluppare, per esercire e per la “manutenzione” dei programmi. Software è tutto quanto è immateriale, invisibile nell'informatica e si contrappone ad hardware (materiale, fisico). L'ingegneria del software è l'applicazione della conoscenza scientifica per il progetto e la costruzione di programmi di elaborazione e la relativa documentazione. Il principio dell'indistinguibilità per un elaboratore dell'informazione (dato) dall'istruzione (comando per la configurazione circuitale richiesta) enunciato da Von Neumann all'inizio del secondo dopoguerra è alla base della programmazione tradizionale. Come già visto, quando un elaboratore numerico con programma memorizzato funziona, la memoria contiene due tipi di informazione codificati in bit: i dati in elaborazione cui accede l'unità aritmetica e logica e le istruzioni che ne controllano il funzionamento cui accede l'unità di controllo. Lo schema della programmazione tradizionale, che svolge un'operazione alla volta, superata la fase del linguaggio macchina, si appoggia al linguaggio evoluto in cui è scritto il programma sorgente, e al programma traduttore che effettua la conversione del primo in linguaggio macchina.
Scelta del linguaggio e metodo di programmazione
La scelta del tipo di linguaggio è importante per le prestazioni del programma (efficienza, portabilità, leggibilità) come è importante il metodo di programmazione, "Per la programmazione strutturata vedi lo schema a pg. 21 del 18° volume." "Per gli elementi di uno schema di flusso vedi figura al lemma dell'Aggiornamento 1995." ma il vincolo di un'operazione alla volta restringe in modo essenziale l'ambito di applicazione della programmazione tradizionale (sequenziale). Problemi computazionali difficili – quali la simulazione dell'interazione fra sistemi atomici o fra oggetti celesti, la fluidodinamica di base per le previsioni meteorologiche, la ricerca veloce all'interno delle enormi basi di dati dell'intelligenza artificiale – richiederebbero tempi lunghissimi di esecuzione nel contesto della programmazione sequenziale. Questa esigenza ha fatto progettare elaboratori paralleli con migliaia di singole unità di elaborazione e ha portato allo studio di programmi paralleli capaci di gestire simultaneamente molti filoni di attività. Si sono in un primo tempo costruite versioni parallele di un programma sequenziale secondo una strategia simile a quella adottata per la portabilità dei programmi, ossia sviluppando un compilatore capace di rendere esplicito il parallelismo nascosto in un programma sequenziale. Per esempio, il prodotto di due matrici quadrate consiste nel creare una nuova matrice il cui elemento generico è la somma dei prodotti degli elementi della riga della prima matrice con quelli corrispondenti per ordine della colonna della seconda matrice (il primo elemento della riga con il primo della colonna, ecc.). Tutti gli elementi della nuova matrice possono in teoria essere elaborati in parallelo da un elaboratore con un numero di unità di elaborazione pari al numero degli elementi della matrice. Un approccio più innovativo è lo sviluppo di algoritmi paralleli; è questo il caso degli schemi a trasferimento di messaggi, utile quando i programmi si possono scomporre facilmente in reti logiche di pezzi distinti e abbastanza indipendenti. Un modo di programmazione anche più avanzato è quello della macchina connessionistica, dove ogni elemento di una struttura di dati può essere modificato simultaneamente da una serie di processi identici che prendono ordine da un capoprocesso. Un certo tipo di parallelismo è centrale e presente in tutta la programmazione. Nuovi linguaggi che sfruttino la simmetria dei programmi nel tempo (sequenziale) con quella nello spazio (parallela) potrebbero allargare le frontiere dell'elaborazione a problemi troppo complessi. Una categoria di programmi che si collega alla branca della scienza dell'informazione chiamata intelligenza artificiale è quella dei “sistemi esperti”. Si tratta di programmi che si rivolgono a ristretti settori della conoscenza (nei campi della medicina, chimica, matematica, ingegneria) e vengono associati a un insieme di fatti e conoscenze di base. Il programma che contiene un processo automatico di ragionamento per risolvere i problemi del settore considerato utilizza la base di conoscenza per affrontare la situazione specifica. I sistemi esperti ricorrono a linguaggi la cui metodologia si distacca da quella convenzionale; alcuni sono costruiti con il linguaggio LISP che è un linguaggio funzionale procedurale che, in qualche modo, si ricollega al concetto di funzione e a quello di oggetto; altri si basano sul PROLOG, un linguaggio di programmazione logica non procedurale che, in base a una serie di dichiarazioni associate all'impiego di formule logiche, fornisce la soluzione del problema.
F. R. Crawford, Introduction to Data Processing, Englewood Cliffs, 1968; L. J. Cohen, Operating System Analisys and Design, New York, 1970; A. Ralston, Introduction to Programming and Computer Science, New York, 1971; M. Italiani, G. Serazzi, Il sistema CND per l'insegnamento della programmazione, Pavia, 1972; K. London, Introduzione agli elaboratori elettronici, Milano, 1973; A. Chianese, Dal progetto alla codifica dei programmi secondo i principi della programmazione strutturale, Napoli, 1991.