Sei sulla pagina 1di 16

FORMAZIONE TECNICA E SCIENTIFICA DI BASE

Conoscenza informatica, applicativi ed elementi base dell’informatica musicale.


1.1 – Conoscenza informatica:

All’interno del computer si possono individuare quattro componenti principali che compongono la struttura
interna e una serie di periferiche esterne:

 Microprocessore: contiene la CPU (Central Process Unit o Unità centrale, che è il nucleo del
computer) e i circuiti di controllo. Si tratta del componente che esegue le istruzioni dei vari
programmi e sovrintende al funzionamento dell’intera macchina.
 Memoria RAM (Random Access Memory): è la memoria dove vengono conservati i dati in corso
di elaborazione (i documenti aperti) e le istruzioni del programma in esecuzione; si tratta di una
memoria temporanea che si cancella completamente quando si spegne il computer.
 Memoria ROM (Read Only Memory): è una memoria permanente di sola lettura che viene scritta
una sola volta in fase di fabbricazione del computer, dopodiché non può essere più modificata
(esistono però anche le EPROM - Electric Programmable ROM - realizzate secondo una tecnologia
che consente, in particolari condizioni, la cancellazione e riscrittura del contenuto). Vi vengono
registrate le informazioni fisse, come ad esempio tabelle di conversione di codici o le istruzioni del
programma di avviamento (boot) che si attiva all'accensione della macchina. Contrariamente alla
RAM, la memoria ROM non esiste in forma di componente separato e individuale, esistono invece
numerose piccole ROM incorporate all'interno dei vari circuiti integrati (sulla scheda madre, sulle
schede d’espansione, ecc.) come nel BIOS.
 Hard Disk: o Disco Fisso è la memoria permanente del computer, in cui si conservano tutti i
documenti, i dati e i programmi. Viene usato come memoria di immagazzinamento (fa parte quindi
delle memorie dette memorie di massa).

Il funzionamento del computer si basa sul continuo rapidissimo trasferimento di segnali elettrici fra questi
componenti e fra gli stessi e le periferiche esterne. Tutti i dispositivi che mettono in comunicazione il
computer con l’esterno sono detti genericamente Periferiche oppure Dispositivi periferici di Input/Output
(o di I/O, o di Ingresso/Uscita). Alcuni dispositivi sono solo di ingresso perché inviano dati al computer ma
non ne ricevono mai (come il mouse e la tastiera), altri sono solo di uscita perché ricevono dati dal computer
senza inviarne mai (come il monitor e le casse audio), altri sono contemporaneamente di ingresso e di uscita
(come i dischi). I dispositivi di I/O più comuni sono:

o Tastiera;
o Mouse;
o Trackball;
o Tavoletta grafica;
o Monitor;
o Stampante;
o Casse;
o Masterizzazione;
o Zip;
o Scanner;
o Modem;
o WEBcam;

Alcuni dei dispositivi di I/O, per poter essere collegati alla macchina, richiedono la presenza di una scheda
d’espansione inserita all’interno del computer. Ad esempio, il monitor richiede solitamente la presenza di
una scheda video, le casse richiedono una scheda audio, ecc. Altro elemento importante è un grande circuito
stampato chiamato Scheda Madre (Motherboard), che contiene una grande quantità di componenti (fra cui
altre schede più piccole, incastrate su degli appositi supporti). La scheda madre fa da supporto e connessione
per tutti i componenti interni del computer e contiene inoltre una serie di circuiti (chipset, cache, BIOS)
adibiti al controllo delle varie parti. I modelli in commercio sono molte decine e ne escono sempre di nuovi.
Spesso nei computer di marca le schede madri svolgono anche le funzioni audio, video e rete (che nei PC
assemblati si trovano invece sempre su schede separate). Sulla scheda madre si inseriscono come
componenti separati il microprocessore, la RAM e le varie schede di espansione; vi si trovano inoltre le
prese per il collegamento dell'hard disk e dei drive per i dischi mobili (floppy e CD).

1.2 – Sistemi Operativi (SO):

Un sistema operativo (abbreviato in SO), in informatica, è un software di base, detto anche piattaforma
operativa (composto normalmente da più sottosistemi o componenti software: kernel, scheduler, file
system, gestore della memoria, interfaccia utente e spooler di stampa), che gestisce le risorse hardware
e software della macchina, fornendo servizi di base ai software applicativi. Tra i sistemi operativi per
computer si citano Microsoft Windows, MacOS, le distribuzioni GNU/Linux, sistemi Unix-like, BSD e
Chrome OS, mentre per i dispositivi mobili, quali smartphone e tablet, vi sono iOS, Android, Windows
Phone ecc.

Descrizione:

L'hardware è il primo livello di un elaboratore elettronico. Esso dialoga con il sistema operativo. Questo, a
sua volta, interagisce con le applicazioni - con le quali - interagisce l'utente finale. Schema a blocchi di
esecuzione di un'applicazione fino all'hardware passando per il sistema operativo.

Un sistema operativo è un insieme di software che fornisce all'utente una serie di comandi e servizi per
usufruire al meglio della potenza di calcolo di un qualsivoglia elaboratore elettronico, spaziando dal più
piccolo dei palmari al più potente tra i mainframe. I sistemi operativi nascondono tutti i dettagli tecnici
legati allo specifico hardware e architettura rappresentando le informazioni ad un alto livello, meglio
comprensibile dall'uomo.

Esso garantisce l'operatività di base di un calcolatore, coordinando e gestendo le risorse hardware di


processamento (processore) e memorizzazione (memoria primaria), le periferiche, le risorse/attività
software (processi) e facendo da interfaccia con l'utente, senza il quale quindi non sarebbe possibile
l'utilizzo del computer stesso e dei programmi/software specifici, come applicazioni o librerie software.

È dunque un componente essenziale del sistema di elaborazione che funge da interfaccia tra l'utente e la
macchina ed inoltre è una base alla quale si appoggiano gli altri software, che dunque dovranno essere
progettati e realizzati in modo da essere riconosciuti e supportati da quel particolare sistema operativo.
Assieme al processore, con cui spesso è strettamente legato, costituisce la cosiddetta piattaforma del
sistema di elaborazione. In generale. un sistema operativo può essere:

 monoutente, se un solo utente per volta può accedere alle risorse dell'elaboratore;
 multiutente, se più utenti possono accedere alle risorse dell'elaboratore che a sua volta può essere:
o seriale, sequenzialmente uno per volta;
o parallelo, ciascuno parallelamente agli altri;
 monotasking, se in grado di eseguire un solo compito o task (processo) alla volta.
 multitasking o multithreading, se in grado di svolgere più compiti o sotto-compiti parallelamente
attraverso una certa politica di scheduling (es. timsharing).
 portabile o meno su differenti architetture hardware di processori.

Funzioni principali:

Secondo una definizione più rigorosa, il sistema operativo è un insieme di funzioni e strutture dati
responsabile:

 del controllo e della gestione delle risorse di sistema (CPU e Memoria primaria) e delle
componenti hardware che costituiscono il computer (processi di Input/Output da e verso le
periferiche collegate al sistema)
 dell'esecuzione dei programmi (processi) che su di esso vengono eseguiti, assegnando ad essi le
necessarie risorse per l'avanzamento dei processi.

Se il sistema di elaborazione prevede la possibilità di memorizzazione aggiuntiva dei dati su memoria di


massa, come accade nei computer general purpose, esso ha anche il compito di:

 gestire l'archiviazione e l'accesso ai file. I programmi possono gestire l'archiviazione dei dati su
memoria di massa (ottenendo strutture complesse, come una base di dati), servendosi delle
procedure messe a disposizione del sistema operativo. La componente del SO che si occupa di tutto
ciò viene chiamata file system.

Infine, se è prevista interazione con l'utente, viene solitamente utilizzata allo scopo un'interfaccia software
(grafica o testuale) per accedere alle risorse hardware (dischi, memoria, I/O in generale) del sistema. D'altra
parte, un sistema operativo può essere utilizzato anche su una macchina che non preveda interazione diretta
con un essere umano (per un esempio, vedi smart card o determinati sistemi embedded) spesso dunque più
leggero e semplificato. Solitamente un sistema operativo installato su computer fornisce anche degli
applicativi di base per svolgere elaborazioni di diverso tipo.

Sebbene molte delle funzionalità sopraddette non siano spesso immediatamente visibili/percepibili
dall'utente, l'importanza del sistema operativo di un calcolatore è cruciale: oltre alla necessità di gestione
delle funzionalità di base sopraddette, al di là delle prestazioni massime offerte dall'hardware
dell'elaboratore stesso, il sistema operativo determina di fatto efficienza e buona parte delle prestazioni
effettive di funzionamento dell'intero sistema ad esempio in termini di latenze di processamento, stabilità,
interruzioni o crash di sistema.
1.3 – Strutture di controllo:

In tutti i paradigmi di programmazione imperativa, le strutture di controllo sono costrutti sintattici di


un linguaggio di programmazione la cui semantica afferisce al controllo del flusso di esecuzione di un
programma, ovvero servono a specificare se, quando, in quale ordine e quante volte devono essere eseguite
le istruzioni che compongono il codice sorgente in base alle specifiche di progetto del software da
realizzare.

Strutture di controllo fondamentali:

Sequenza:
La sequenza è la struttura di controllo fondamentale di qualsiasi linguaggio imperativo, inclusi i
linguaggi macchina. Stabilisce l'ordine in cui le istruzioni presenti nel testo del programma
devono essere eseguite a tempo di esecuzione. Di norma, non ha una espressione sintattica esplicita:
invece è data dalla semplice successione delle istruzioni; in tal caso, la macchina astratta del
linguaggio in uso le esegue in sequenza, una dopo l'altra;
Goto:
Insieme alla sequenza, il goto (vai a) è la struttura di controllo più semplice; anch'essa appare, in
qualche forma, in tutti i linguaggi macchina. Il significato generale del goto è quello di "salto"
ovvero far "passare" il controllo a una istruzione specificata, che può trovarsi in un punto qualsiasi
del programma. Il goto ammette sempre anche (o solo) una forma condizionata, il cui significato può
essere parafrasato come segue: "se è vera una condizione C, vai alla istruzione I". Nei linguaggi
macchina, la condizione C deve solitamente assumere una delle due seguenti forme: "il contenuto
della cella di memoria M è 0" oppure "il contenuto della cella di memoria M è diverso da 0";
l'istruzione I viene inoltre identificata dall'indirizzo di memoria in cui I è memorizzata. Nei
linguaggi ad alto livello come il C, la condizione C può essere espressa come una qualsiasi
espressione booleana (come il risultato della moltiplicazione di A per B è diverso da X), e
l'istruzione I può essere identificata da un nome o da un codice numerico esplicitamente associato dal
programmatore all'istruzione stessa (indipendentemente dalla sua collocazione in memoria).
A partire dagli anni Settanta, la struttura di controllo goto è stata soggetta a forti critiche in quanto
essa consente (o favorisce) lo sviluppo di programmi potenzialmente molto poco leggibili e
modificabili (il cosiddetto “spaghetti code”). Sebbene essa resti una struttura di controllo
fondamentale dei linguaggi macchina moderni, nei linguaggi di programmazione ad alto livello, a
seconda dei casi, il goto non viene fornito oppure viene fornito ma se ne sconsiglia l'uso.

Strutture di controllo della programmazione strutturata:

Nel 1966, con un celebre teorema, Corrado Böhm e Giuseppe Jacopini introdussero i fondamenti teorici
del paradigma della programmazione strutturata dimostrando che qualsiasi programma scritto usando la
struttura di controllo goto poteva essere riscritto usando solo strutture di controllo di tipo sequenza,
iterazione e alternativa. Unitamente alle critiche di cui si è detto sopra, questo risultato contribuì a
decretare la fine della programmazione basata sul goto. Tutti i linguaggi moderni offrono un insieme di
strutture di controllo dei tre tipi introdotti da Böhm e Jacopini, sebbene molti mantengano anche il goto (pur
sconsigliandone l'uso indiscriminato).

Alternativa:
Le strutture di controllo "alternative" consentono di specificare che una data istruzione o un dato
blocco di istruzioni devono essere eseguiti "(solo) se" vale una certa condizione. Sono perciò anche
dette strutture condizionali.

Alternativa if-then e if-then-else:


L'alternativa if-then (se-allora) è la più semplice forma di alternativa. Il suo significato può essere
parafrasato con la frase "se vale la condizione C, esegui l'istruzione (blocco) I". La maggior parte dei
linguaggi di programmazione ammette anche (come variante) la forma più articolata if-then-else (se-
allora-altrimenti), che si può parafrasare come: "se vale la condizione C esegui l'istruzione (blocco)
I1; altrimenti esegui l'istruzione (blocco) I2".

L'alternativa case:
L'alternativa case può essere assimilata a una catena di if-then-else con certe restrizioni. In questo
caso, la scelta di uno fra N istruzioni o blocchi alternativi viene fatta sulla base del valore di una
determinata variabile o espressione, normalmente di tipo intero. Essa può essere parafrasata come
segue: "valuta il valore N; nel caso in cui il suo valore sia V1, esegui I1; nel caso in cui sia V2, esegui
I2 (ecc.)". Il seguente frammento di codice pseudo-C illustra il concetto:

case sceltaMenu of
1: apriFile();
2: chiudiFile();
3: salvaFile();
4: esci();
end;

Il fatto che il valore N debba (spesso) essere di tipo intero è legato a considerazioni di efficienza
nell'implementazione del meccanismo. Il case si presta infatti a essere trasformato, a livello di
linguaggio macchina, in un goto con indirizzamento indiretto, che potrebbe essere basato su una
tabella in memoria di cui N seleziona una particolare entry, in cui è specificato l'indirizzo delle
istruzioni da eseguire per quel valore di N.

Iterazione:
Le strutture di controllo "iterative" consentono di specificare che una data istruzione o un dato
blocco di istruzioni devono essere eseguiti ripetutamente. Esse vengono anche dette cicli. Ogni
struttura di controllo di questo tipo deve consentire di specificare sotto quali condizioni l'iterazione
(ripetizione) di tali istruzioni debba terminare, ovvero la condizione di terminazione del ciclo oppure,
equivalentemente, la condizione di permanenza nel ciclo. Di seguito si esaminano le strutture di
controllo più note di questa categoria.

Ciclo for:
Il ciclo for è indicato quando il modo più naturale per esprimere la condizione di permanenza in un
ciclo consiste nello specificare quante volte debbano essere ripetuti l'istruzione o il blocco controllati
dal ciclo. Le forme tradizionali di ciclo for possono essere parafrasate come "ripeti (il codice
controllato) per i che va da un certo valore iniziale a un certo valore finale, con un certo passo". i è in
generale una variabile di tipo intero, detta contatore. Il seguente frammento di codice BASIC
illustra il concetto:

FOR I=1 TO 9 STEP 2


PRINT I
NEXT I

Questo frammento ripete l'istruzione di stampa a video della variabile contatore I. Essa parte dal
valore iniziale 1 per arrivare al valore finale 10. L'indicazione del "passo" (STEP) specifica come
varia il valore di I da una iterazione alla successiva. Il frammento dunque stamperà la sequenza 1, 3,
5, 7, 9.

Ciclo while:
Il ciclo while (mentre, o fintantoché) è indicato quando la condizione di permanenza in un ciclo è
una generica condizione booleana, indipendente dal numero di iterazioni eseguite. Le forme
tradizionali di ciclo while possono essere parafrasate come "ripeti (il codice controllato) fintantoché
resta vera la condizione C". Un esempio tipico è la lettura di dati da un file di cui non si conosca a
priori la dimensione; esso potrebbe assumere la forma "leggi il prossimo dato finché non incontri la
fine del file". Il seguente frammento di codice pseudo-C mostra un esempio di while:

passwordUtente = leggiPassword();
while(passwordUtente <> passwordCorretta) {
segnalaErrorePassword();
passwordUtente = leggiPassword();
}

Nel while del C (e di molti altri linguaggi) la condizione di permanenza nel ciclo viene controllata
prima di eseguire la prima iterazione del ciclo stesso; se essa risulta immediatamente falsa, le
istruzioni nel ciclo non vengono eseguite. Nell'esempio riportato sopra, se la password letta è
corretta, il blocco di codice che segnala l'errore di immissione e chiede di inserire nuovamente la
password non viene (ovviamente) eseguito.

Ciclo loop-until:
Il ciclo loop-until (ripeti finché) differisce dal while per due aspetti. Innanzitutto, esso garantisce che
venga eseguita sempre almeno una iterazione del ciclo (la condizione che controlla il ciclo viene
verificata dopo aver concluso la prima iterazione). In secondo luogo, viene espressa la condizione di
terminazione del ciclo anziché quella di permanenza nel ciclo (quest'ultima differenza non ha
comunque un impatto concettuale molto importante, essendo le due condizioni esprimibili
semplicemente come negazione l'una dell'altra). Il seguente frammento pseudo - Pascal illustra il
concetto:

loop
passwordUtente:= leggiPassword();
until (passwordUtente = passwordCorretta

Varianti di while e loop-until:


Le due differenze citate sopra fra while e loop-until sono in effetti indipendenti l'una dall'altra, per
cui sono facilmente immaginabili (benché meno diffuse per motivi storici) anche altre due
combinazioni: cicli che garantiscono una iterazione ma in cui si specifica la condizione di
permanenza nel ciclo, e cicli che ammettono 0 iterazioni ma in cui si specifica la condizione di
terminazione del ciclo. Un esempio del primo caso è la struttura do-while (fai fintantoché) del C e
dei suoi derivati, esemplificata in questo frammento di pseudo-codice:

Do
passwordUtente = leggiPassword();
while(passwordUtente<>passwordCorretta);

Iterazione basata su collezioni:


Alcuni linguaggi (per esempio Smalltalk, Perl, C#, Java) forniscono varianti del ciclo for in cui il
"contatore" è una variabile generica (non necessariamente un numero intero) che assume una
sequenza di valori del tipo corrispondente, per esempio tutti i valori contenuti in un array o in una
collezione. Il seguente frammento di codice C# illustra il concetto:

foreach (string s in unaCollezioneDiStringhe) {


stampa(s); }

Terminazione anticipata di cicli e iterazioni:


Molti linguaggi forniscono una istruzione specifica per terminare "prematuramente" un ciclo, ovvero
causarne la terminazione in un modo alternativo rispetto a quello principale del ciclo (per esempio
basato sul valore del contatore nel for o sulla verità/falsità di una condizione nei cicli while o repeat-
until). Il seguente frammento illustra il concetto in un ciclo Java:

// cerco un valore N in un array


boolean trovato = false;
for(int i=0; i<100; i++)
if(v[i]==N) {
trovato = true;
break;
}
}

In questo frammento Java, il programma deve stabilire se un dato numero N è contenuto in un array.
Non appena N viene trovato, diventa inutile proseguire l'attraversamento dell'array stesso: il break
termina quindi il ciclo for.
L'uso indiscriminato di meccanismi come il break (fornito da linguaggi come C, C++ e Java) è
spesso criticato e sconsigliato perché si viene a perdere una utile proprietà di leggibilità dei cicli
della programmazione strutturata: infatti, di fronte a un ciclo while con una condizione C, il lettore
tende ad assumere che, al termine del ciclo, C sia falsa. L'uso di break o costrutti simili,
introducendo "un altro" possibile motivo di terminazione del ciclo (oltretutto potenzialmente
"nascosto" all'interno del blocco controllato dal while) fa sì che questa assunzione non del tutto
sicura. Tuttavia, vi sono anche casi in cui lo stato del programma alla fine del ciclo, con o senza
break, è lo stesso, per cui il suddetto problema non si pone. Questo è il caso dell'esempio Java
riportato sopra (il contatore i è locale al for, e quindi viene deallocato al termine del ciclo).
Un meccanismo simile (in generale considerato meno pericoloso) è quello che consente di terminare
anticipatamente una determinata iterazione di un ciclo, passando immediatamente alla successiva. Si
veda il seguente programma:

for(int i=1; i<=100; i++)


if(numeroPrimo(i)) {
stampa(i);
continue;
}
// calcola e stampa i fattori di i
}

Questo programma stampa i fattori di tutti i numeri interi da 1 a 100. Se i è un numero primo, è
sufficiente stampare il numero stesso; altrimenti sono necessarie altre operazioni per scomporlo.
L'istruzione continue indica che l'iterazione corrente è conclusa e fa sì che il ciclo passi
immediatamente alla successiva.

Annidamento:
Tutte le strutture di controllo possono essere annidate tra loro ovvero inserite una dentro l'altra
all'interno del codice del programma, rispettando la sintassi del linguaggio usato.
Strutture di controllo non locali:

Nella maggior parte dei linguaggi di programmazione di alto livello, l'esecuzione di un programma
evolve attraverso diversi contesti, in ciascuno dei quali sono possibili alcune azioni e non altre. Per esempio,
l'esecuzione di una subroutine avviene in un contesto solitamente descrivibile in termini di record di
attivazione, che comprende dati locali a cui solo quella attivazione di subroutine può accedere. Inoltre,
vengono poste regole ben precise che stabiliscono come e quando l'esecuzione del programma transita da un
contesto all'altro (nel caso delle subroutine, queste regole sono generalmente coerenti con il modello dello
stack dei record di attivazione. Per strutture di controllo non locali si intendono quelle strutture di
controllo che causano un salto del flusso di controllo che prescinde da (può costituire un'eccezione a) tali
regole. Il goto è il caso estremo, e in effetti i linguaggi fortemente basati sul goto senza restrizione (come i
linguaggi macchina) sono spesso caratterizzati da nozioni di contesto molto deboli o del tutto assenti.

Nei linguaggi strutturati esistono talvolta strutture di controllo non locali che aggirano le normali
restrizioni sull'evoluzione del contesto di un programma in esecuzione, senza essere però del tutto incoerenti
con esse; queste si possono definire strutture di controllo non locali strutturate. Gli esempi più noti di
strutture di controllo non locali (sia non strutturate che strutturate) sono riportati nel seguito e afferiscono al
problema generale della gestione delle eccezioni.

Condizioni in PL/1

Il linguaggio PL/1 ha un meccanismo di gestione delle eccezioni piuttosto semplice. Sono previste
un certo numero di condizioni (leggi: eccezioni, situazioni anomale; per esempio, tentativi di
dividere un numero per 0 o di accedere a un array con un valore di indice illegale) che vengono
"sollevate" (RAISE) automaticamente dal linguaggio. Il programmatore può indicare cosa fare
quando viene sollevata una condizione attraverso una clausola della forma "ON <condizione>
<azione>". Nella maggior parte dei casi, l'<azione> da eseguire quando si verifica la <condizione>
viene specificata nella forma di un goto. Il meccanismo di PL/1 si può considerare come una
versione primitiva (e poco o per niente "strutturata") della gestione delle eccezioni in C++ e
linguaggi derivati.

Eccezioni in C++ e linguaggi derivati:

C++, D, Java, e C# gestiscono le eccezioni con una struttura di controllo non locale strutturata
apposita, solitamente detta struttura try-catch, la cui sintassi è illustrata di seguito:

try {
...
... // codice che può causare eccezioni di vari tipi
...
}
catch (UnTipoDiEccezione e) {
... // gestisce il problema
}
catch (UnAltroTipoDiEccezione e) {
... // gestisce il problema
}
finally {
... // codice che va eseguito comunque
}

In questo schema, il blocco di codice controllato da try contiene una o più istruzioni che possono
causare un'eccezione. Se tale evenienza si verifica, il controllo salta fuori dal contesto-blocco
associato a try passando a un blocco controllato da una catch (come l'esempio suggerisce, si possono
avere più catch associate a diversi tipi di eccezioni). Il blocco catch è il gestore dell'eccezione,
ovvero contiene quelle operazioni che costituiscono, in senso ampio, la "contromisura" prevista dal
programmatore nel caso si verifichi quella particolare eccezione. Il blocco controllato da finally
(presente in D, Java, e C#) contiene istruzioni che devono essere eseguite comunque, che si verifichi
o no una eccezione (normalmente si collocano nel blocco controllato da finally operazioni di rilascio
risorse come chiusura di file o di connessioni di rete; per situazioni di questo genere C# ha anche un
altro costrutto ad hoc, la clausola using).

Se in un blocco try viene sollevata un'eccezione per la quale non è stata prevista alcuna catch (oppure se si
verifica un'eccezione in un punto del codice non controllato da una try), la subroutine o il metodo corrente
terminano e l'eccezione viene propagata alla subroutine o metodo chiamante, che la intercetterà se
l'istruzione di chiamata alla subroutine fallita è inclusa in un blocco try con associata una catch appropriata
per quel tipo di eccezione; viceversa, il chiamante stesso terminerà e l'eccezione verrà ulteriormente
propagata verso "l'alto" (al chiamante del chiamante). Su questo modello di gestione si possono fare due
osservazioni:

 esso può essere definito strutturato nel senso che pur saltando da un contesto all'altro secondo regole
diverse da quelle che "normalmente" regolano il cambiamento di contesto di un programma
strutturato, non ne viola i princìpi fondamentali: il controllo non può passare a un punto qualsiasi del
programma (come nel goto), bensì rispetta il modello dello stack dei record di attivazione (passaggio
dal contesto del chiamato a quello del chiamante);
 è giustificato dal fatto che, nella pratica della programmazione, non tutte le eccezioni possono essere
efficacemente gestite "localmente"; spesso, prendere contromisure per un certo problema richiede
informazioni aggiuntive che sono disponibili solo in un contesto più ampio. Per esempio, se un
fallimento nell'apertura di un file dev'essere segnalato all'utente con un messaggio in una finestra
pop-up, non è ragionevole attendersi che questo possa essere fatto da una routine generica di accesso
a file (la cui progettazione, per motivi di riusabilità probabilmente non "assume" che l'applicazione
corrente sia dotata di GUI piuttosto che avere un'interfaccia testuale).

Un modello analogo a quello appena descritto si trova anche nei linguaggi Python, Ruby, Objective
C e altri.

Strutture di controllo concorrenti:

Nel contesto della programmazione concorrente e parallela, sono state introdotte strutture di controllo
specifiche che specificano o implicano l'esecuzione concettualmente contemporanea di determinati insiemi
di operazioni o istruzioni. Il linguaggio più rappresentativo in questo senso è probabilmente il linguaggio di
programmazione parallela Occam. Questo linguaggio fornisce almeno due strutture di controllo innovative,
rispettivamente per l'esecuzione parallela di istruzioni e una forma speciale di alternativa che implica la
valutazione parallela delle condizioni che la governano.

La struttura PAR di Occam:


La struttura di controllo PAR specifica che un certo insieme di istruzioni devono essere eseguite in
parallelo. Nella sua forma più semplice, la struttura PAR ha la seguente sintassi:

PAR
x:= x+1
y:= y+1

In questo frammento di codice, l'incremento delle due variabili avviene contemporaneamente. Il PAR
ammette anche una forma più complessa che presenta alcune analogie con un ciclo for, e viene
coerentemente indicata con le parole chiave PAR-FOR. Il seguente frammento di codice acquisisce
un dato intero da quattro canali in parallelo.

PAR i=0 FOR 4


INT n
c[i] ? n[i]

L'analogia con il ciclo for riguarda l'uso del "contatore" i. Come un ciclo for tradizionale, il
frammento di codice riportato esegue le operazioni indicate cinque volte, "per i che va da 0 a 4";
tuttavia, le cinque operazioni di input non sono svolte sequenzialmente, bensì in parallelo.

La struttura ALT di Occam:

Il costrutto ALT consente di definire un insieme di "comandi con guardia". Un comando con guardia
è costituito da una condizione detta guardia e una istruzione, con qualche analogia con una struttura
if. Tuttavia, il significato del comando con guardia non è che l'istruzione sarà eseguita se la
condizione è vera; piuttosto, l'istruzione potrà essere eseguita quando la condizione diventerà vera. Il
costrutto ALT raggruppa un certo numero di comandi con guardia; il primo comando per cui la
guardia diventa vera viene eseguito. Se ci sono più comandi la cui guardia è vera, uno di essi viene
selezionato (arbitrariamente) dalla macchina virtuale del linguaggio.

ALT
in.a? v
out! v
in.b? v
out! v

Il costrutto ALT qui riportato comprende due comandi con guardia. Le guardie sono in questo caso
istruzioni (sospensive) che attendono in input un valore da uno di due canali (in.a e in.b). Non
appena il dato diventa disponibile su uno qualsiasi dei due canali, esso verrà acquisito, la guardia
corrispondente si considererà "vera", e l'istruzione (di output) associata sarà eseguita, ponendo fine
all'esecuzione del blocco ALT.

1.4 – Strutture dati:

In informatica, una struttura dati è un'entità usata per organizzare un insieme di dati all'interno della
memoria del computer, ed eventualmente per memorizzarli in una memoria di massa. La scelta delle
strutture dati da utilizzare è strettamente legata a quella degli algoritmi; per questo, spesso essi vengono
considerati insieme. Infatti, la scelta della struttura dati influisce inevitabilmente sull'efficienza degli
algoritmi che la manipolano.

La struttura dati è un metodo di organizzazione dati, quindi prescinde da ciò che è effettivamente contenuto.
Ciascun linguaggio di programmazione offre strumenti, più o meno sofisticati, per definire strutture dati,
ovvero aggregare dati di tipo omogeneo o eterogeneo. Questi strumenti sono tipicamente componibili. Più
formalmente, i linguaggi forniscono un insieme predefinito di tipi di dato elementari, e le strutture dati sono
strumenti per costruire tipi di dati aggregati più complessi.

L'operazione di costruzione di una variabile di un tipo di dati complesso è detta "istanziazione", e può
avvenire sia durante la compilazione del programma (compile time) sia durante la sua esecuzione
(runtime). Le strutture di dati si differenziano prima di tutto in base alle operazioni che si possono effettuare
su di esse e alle prestazioni offerte. Questo permette di studiare un'astrazione dall'implementazione.

Costruttori di strutture dati:

Array o vettore:

Un array è una struttura dati omogenea, che contiene un numero finito di elementi tutti dello stesso
tipo, ad esempio un vettore di 10 interi. Questi elementi sono individuati attraverso un indice
numerico, che tipicamente va da 0 al numero massimo di elementi meno uno. La dimensione del
vettore deve essere dichiarata al momento della sua creazione. Vettori di dimensione diversa
costituiscono tipi di dati diversi. L'accesso ad un elemento di un array ha un costo computazionale
costante, mentre l'aggiunta o la rimozione di elementi in posizione casuale possono essere piuttosto
onerose, tipicamente appannaggio degli array dinamici.

Record o struttura:

Un record è una struttura dati che può essere eterogenea o omogenea. Nel primo caso contiene una
combinazione di elementi che possono essere di diverso tipo, ad esempio un intero, un numero in
virgola mobile e un carattere testuale. Gli elementi che lo compongono sono detti anche campi, e
sono identificati da un nome.

Classe:

Una classe è un costrutto tipico dei linguaggi orientati agli oggetti, e consiste in un record a cui
sono associate anche delle operazioni o metodi.

Composizione di costruttori:

Questi costruttori possono essere liberamente combinati per dare luogo a strutture più complesse.

Per esempio, è possibile rappresentare una matrice bidimensionale di taglia come un vettore di
dimensione avente come elementi dei vettori di lunghezza . Ancora, si può definire un array di
cinquecento elementi, ognuno dei quali è un record composto da quattro stringhe e due vettori,
ciascuno contenente quattro vettori di tre caratteri.

Le strutture dati ottenute mediante la composizione di questi costruttori sono dette anche "statiche",
in quanto la loro occupazione di memoria è definita al momento della compilazione, o al più al
momento dell'istanziazione. Ad esempio: il programma decide che ha bisogno di un array di 100
elementi per processare i suoi dati, e lo alloca, ovvero impegna la memoria necessaria. Da questo
momento, l'array avrà dimensione fissa cioè sarà sempre composto di 100 elementi, e terrà
impegnata la memoria necessaria fino alla terminazione del programma o a quando non sarà
distrutto. Cambiare la lunghezza dell'array è possibile, ma molto costoso in termini di risorse di
calcolo ed è quindi un'operazione evitata per quanto possibile (vedi array dinamici).

Il limite delle strutture dati statiche è che mal si adattano a problemi in cui la numerosità dei dati da
trattare non è nota a priori e/o varia sensibilmente durante l'esecuzione del programma.
Strutture dati dinamiche:

Le strutture dati dinamiche sono basate sull'uso di dati di tipo puntatore, e sull'allocazione dinamica della
memoria. Gli elementi possono essere allocati (e deallocati) man mano che servono, collegati tra loro in
modi diversi, e questi collegamenti possono a loro volta mutare durante l'esecuzione del programma. Lo
spazio di memoria necessario per allocare i puntatori, e le operazioni necessarie alla loro manutenzione
costituiscono il costo aggiuntivo delle strutture dati dinamiche.

In assenza dei puntatori, è anche possibile costruire strutture dati dinamiche utilizzando gli array,
rinunciando però alla flessibilità nell'uso della memoria: viene allocato un array di dimensioni sufficienti a
contenere tutti gli elementi che si pensa di dover gestire, e al posto dei puntatori si usano indici nell'array.

Le strutture dati dinamiche possono adattarsi a rappresentare qualsiasi situazione. Qui vengono illustrati i
tipi più comuni.

Lista:

Una lista è un insieme di "nodi" collegati linearmente. I nodi sono dei record che contengono un
"carico utile" di dati, ed un puntatore all'elemento successivo della lista. L'ordine con cui sono
collegati i nodi definisce un ordinamento tra di loro. Un nodo funge da testa della lista, e da questo è
possibile accedere a tutti i nodi della lista. Conoscendo un nodo interno alla lista, è possibile accedere
ai nodi successivi, ma non a quelli precedenti.

Il costo di accesso ad un nodo della lista cresce con la dimensione della lista. Conoscendo il nodo
precedente ad un nodo N, è possibile rimuovere N dalla lista, o inserire un elemento prima di lui, in
un tempo costante.

Lista bidirezionale o doppiamente concatenata:

In questo caso i nodi contengono un puntatore sia al nodo precedente che al successivo. Usando la
sintassi del linguaggio C, dato un nodo N il suo successore è N->succ, e il suo precedente è N->prec.
Deve sempre essere vero che N->succ->prec == N. Ogni nodo permette di accedere a tutti gli
elementi della lista. Gli elementi "strutturali" di questa struttura dati, ovvero i due puntatori contenuti
in ogni nodo, sono ridondanti.
Albero:

Un albero è una rappresentazione dell'albero in teoria dei grafi.

Ogni nodo contiene due (o più) puntatori ad altri nodi che sono detti suoi "figli". Continuando nella
metafora, dato un nodo, è possibile accedere a tutti i suoi discendenti. Un albero deve essere privo di
cicli, ovvero un nodo non può essere discendente di sé stesso, ovvero non deve essere possibile
partire da un nodo, seguire i puntatori ai figli e ritornare al nodo di partenza. Inoltre, ciascun nodo
deve essere figlio di un solo padre.

In alcune implementazioni, ogni nodo contiene anche un collegamento al suo "padre", chiaramente
distinto da quelli ai suoi figli.

Tra i figli di un nodo esiste normalmente una relazione d'ordine, definita dall'ordine dei puntatori nel
padre.

In molte implementazioni, ogni nodo ha un numero fissato di figli, ad esempio due o tre. Si parla in
questo caso di alberi binari o ternari. In altri casi, il numero di figli di un nodo è arbitrario; questo
può essere gestito memorizzando i figli di un nodo in una lista di uno dei tipi descritti sopra.

Ciascun nodo, oltre ai puntatori ai nodi figli, ha normalmente un "carico utile", ovvero un dato
associato al nodo, utile per il problema applicativo da risolvere.

Gli alberi si prestano molto bene a rappresentare le formule matematiche.

Quando i dati sono dotati di una relazione d'ordine totale, essa stessa può essere rappresentata in
maniera conveniente nella struttura di alberi binari. Ad esempio, si può adottare la convenzione
secondo la quale un nodo N è nel sotto-albero sinistro M se e solo se N < M, altrimenti N è nel
sottoalbero destro di M. Un albero dotato di questa proprietà è chiamato albero binario di ricerca (o
binary search tree, BST). In questo modo, la ricerca di un elemento in un albero ordinato equilibrato
richiede un tempo proporzionale all'altezza dell'albero, che nel caso migliore è a sua volta
proporzionale al logaritmo del numero di elementi, mentre l'inserimento di un elemento in un albero
ordinato richiede inoltre che la proprietà di ordinamento precedentemente descritta sia rispettata. A
seconda dell'ordine degli inserimenti l'albero potrebbe sbilanciarsi ed avere cioè foglie a profondità
molto diverse le une dalle altre, causando inefficienze nella ricerca. Talvolta è quindi necessario che
la struttura dati sia dotata e sia oggetto di un'operazione di equilibratura forzata che minimizzi
l'altezza dell'albero.
Grafo:

Il grafo è una generalizzazione dell'albero. Ogni nodo ha un numero arbitrario di nodi "vicini", e può
contenere cicli. In generale, può essere associato un carico utile sia ai nodi che ai collegamenti tra di
loro.

Tabella hash:

Una tabella hash è una struttura dati utile per cercare velocemente un elemento all'interno di un
insieme sulla base di una chiave, ovvero di un sottoinsieme dalle caratteristiche dell'elemento. Di
ciascun elemento da memorizzare viene calcolato un hash della chiave. Viene poi costruito un array,
che ha come indice il valore dell'hash, come elementi puntatori a liste di nodi che corrispondono a
quel valore dell'hash. Se la funzione di hash è ben scelta, statisticamente le liste avranno lunghezze
equilibrate.

Per cercare un elemento, si calcola il suo valore di hash, si seleziona l'elemento dell'array
corrispondente e si percorre la lista fino a quando non lo si trova.

Contenitori:

Le strutture dati sopra esposte possono essere utilizzate per realizzare alcuni tipi di contenitori di utilizzo
frequente, che possono forzare una particolare modalità di accesso ai dati.

Pila o stack:

Una pila è una struttura dati di tipo LIFO (Last In First Out). Viene tipicamente realizzata con array
o liste.

Coda:

Una coda è una struttura dati di tipo FIFO (First In First Out). Viene tipicamente realizzata con
array o liste.

Array associativo:

È una struttura dati presente in molti linguaggi di scripting. Consiste in un array, i cui elementi sono
però identificati da una chiave di tipo arbitrario invece che da un indice numerico. Per accedere ad un
elemento, si mette tipicamente la sua chiave tra parentesi quadre, al posto dell'indice. Se non esiste
un elemento con quella chiave, si ottiene un errore oppure un valore convenzionale.

Template di strutture dati:

L'implementazione manuale di strutture dati dinamiche è un compito ripetitivo, a rischio di errori. Per questa
ragione, sono stati usati vari metodi per separare la definizione delle strutture dati dal loro utilizzo in
algoritmi.

Alcuni linguaggi mettono a disposizione lo strumento dei template, che permette di scrivere funzioni o
classi parametriche rispetto al tipo degli argomenti o di alcuni membri della classe che può essere utilizzata
normalmente.
Un template viene istanziato specificando il tipo degli argomenti di funzione o dei membri di classe non
specificati nella definizione, costruendo una funzione o una classe.

In questo modo, è possibile scrivere una classe lista generica rispetto al contenuto, con i metodi necessari a
manipolare una lista, e poi usarla sia per gestire liste di interi che liste di oggetti complessi.

STL e Generics:

Un importante sforzo per costruire una libreria di strutture dati generiche rispetto al tipo dei dati
immagazzinati è la Standard Template Library (vedi STL su sito SGI), basata sul concetto di
fornire uno strumento per comporre in modo ortogonale dati, contenitori (strutture dati) ed
algoritmi. In STL, un importante strumento per collegare in modo generico strutture dati ed
algoritmi sono gli iteatori, una generalizzazione dei puntatori.

STL è una libreria di classi in linguaggio C++.

Il linguaggio Java presenta, invece, due modalità per gestire le strutture dati fondamentali presenti
nel linguaggio. Fino alla versione 1.4 la gestione dei contenitori è stata realizzata con l'ereditarietà
invece che con i template. I contenitori contengono quindi oggetti di tipo "Object", un tipo da cui
discendono tutte le classi definite in Java; di conseguenza, in Java non è altrettanto facile assicurarsi
che tutti gli oggetti inseriti in un contenitore appartengano ad un dato tipo. Dalla versione 1.5 sono
stati introdotti i generics, che si comportano in modo molto simile ai template C++ e risolvono molti
dei problemi relativi all'upper casting dei "vecchi" contenitori.

1.5 – Applicativi:

I software applicativi sono programmi informatici (software) utilizzati per l’automatizzazione di ufficio
(Office Automation) o per varie utilità. Si distinguono dai software di base (sistemi operativi) in quanto gli
applicativi posso essere utilizzati soltanto se nel computer è già presente un S.O. (Windows, Mac, Linux
ecc.) e sono progettati per funzionare soltanto su determinate piattaforme. Ad esempio, un applicativo
sviluppato per Windows non può essere installato su un computer con sistema operativo Linux. Gli
applicativi si distinguono anche dai videogiochi perché privi della finalità ludica.

Possiamo distinguere gli applicativi nelle seguenti categorie:

Office Automation:

Sono gli applicativi più diffusi. Consentono all’utente di scrivere testi (Word o Writer), lavorare su
fogli di lavoro (Excel), creare basi dati, scaricare la posta elettronica, navigare in internet (browser)
ecc.

Applicativi aziendali:

Questa categoria di applicativi è molto simile all’Office Automation. Tuttavia, mentre i programmi
di O.A. sono per lo più generici e sono utilizzati anche da utenze domestiche, gli applicativi aziendali
sono dedicati a risolvere specifiche esigenze professionali. Un esempio di applicativo aziendale è il
software per la gestione del magazzino e il software di fatturazione.

Software di sviluppo:

I software di sviluppo sono utilizzati dai programmatori informatici per la creazione di nuovi
software, pagine web, videogames, applicazioni varie ecc. Sono applicativi settoriali molto noti in
ambito professionale ma per lo più sconosciuti agli utenti finali. Per essere utilizzati richiedono una
adeguata preparazione da parte dell’utente. Alcuni esempi di software applicativi sono i programmi
di fotoritocco (PhotoShop, Fireworks), quelli per creare pagine web (Dreamweave, Frontpage) o
applicativi (Visual Basic, Visual C).

Utility:

Sono applicativi progettati per ottimizzare la gestione delle risorse di un computer o aumentare il
livello di sicurezza (antivirus). Sono utilizzati da utenti professionali e da utenti esperti per
migliorare il funzionamento del pc. Molte utilities sono preinstallate nei sistemi operativi, altre
possono essere installate successivamente.