1.4 Proprietà degli algoritmi: costrutti fondamentali

annuncio pubblicitario
Prof. S. Masetta
[email protected]
328 6563830
Agg. 16/11/2015
www.sezioneb.com
P
Prrooggrraam
mm
maa ddii IInnffoorrm
maattiiccaa
C
Cllaassssee 33°° B
B
IITTIIS
S E
E.. TToorrrriicceellllii –– S
S.. A
Aggaattaa M
Miilliitteelllloo ((M
ME
E))
SOMMARIO
1
1.1
MODELLI DELL’INFORMATICA _________________________________________ 4
Introduzione al concetto di algoritmo ________________________________________________________ 4
1.2
Esempi _________________________________________________________________________________ 5
1.2.1
Esempio: fare la somma di due numeri _____________________________________________________ 5
1.2.2
Esempio: fare la somma di due numeri con incrementi successivi. _______________________________ 6
1.2.3
Esempio: numeri pari e numeri dispari _____________________________________________________ 6
1.2.4
Esempio: Divisione intera tra due numeri positivi ____________________________________________ 7
1.2.5
Esempio: Dato un numero N generato a caso trovarlo e contare il numero dei tentativi ___________ 7
1.2.6
Esempio: calcolo delle tasse _____________________________________________________________ 7
1.2.7
Esempio: Dire se in un vettore sono di più i valori maggiori di MAX o quelli minori _________________ 8
1.2.8
Esempio: Elevazione a potenza __________________________________________________________ 8
1.2.9
Esempio: Macchinetta del caffè __________________________________________________________ 9
1.2.10
Esempio: media dei numeri pari caricati in un vettore ________________________________________ 10
1.2.11
Esempio: Flow Char elevazione a potenza _________________________________________________ 10
1.2.12
Esempio: matrice 2 * n ________________________________________________________________ 11
1.2.13
Esempio: Cicli fissi ___________________________________________________________________ 11
1.3
Soluzione dei problemi: processi algoritmici _________________________________________________ 13
1.4
Proprietà degli algoritmi: costrutti fondamentali, complessità __________________________________ 13
1.5
Costrutti di base ________________________________________________________________________ 14
1.6
Algoritmi notevoli: ordinamento, ricerca, fusione _____________________________________________
1.6.1
Ottimizzazione dei Programmi: _________________________________________________________
1.6.2
Algoritmi e complessità _______________________________________________________________
1.6.3
Elenco degli algoritmi di ordinamento (wikipedia) ________________________________________
1.6.3.1 Ricerca __________________________________________________________________________
1.6.3.1.1 Introduzione ___________________________________________________________________
1.6.3.1.2 Hash _________________________________________________________________________
1.6.3.1.2.1 Introduzione ______________________________________________________________
1.6.3.1.2.2 Gestione degli overflow ____________________________________________________
1.6.3.2 Algoritmi di ordinamento ____________________________________________________________
1.6.3.3 Algoritmi di ricerca _________________________________________________________________
2
14
14
15
16
17
17
17
17
18
18
20
STRUTTURE DATI ___________________________________________________ 21
2.1
Tipi di dati _____________________________________________________________________________ 21
2.2
I vettori (ARRAY) ______________________________________________________________________ 23
2.3
Le matrici _____________________________________________________________________________ 24
3
PROGRAMMAZIONE E LINGUAGGI ____________________________________ 24
pag 1
3.1
Introduzione alla programmazione _________________________________________________________
3.1.1
Le funzioni _________________________________________________________________________
3.1.2
Tipi di linguaggi _____________________________________________________________________
3.1.3
Linguaggi imperativi __________________________________________________________________
3.1.4
Stile funzionale ______________________________________________________________________
3.1.5
Programmazione ad oggetti ____________________________________________________________
3.1.6
Programmazione concorrente ed a eventi __________________________________________________
24
25
27
27
28
28
29
3.2
dati e procedure, linguaggi e tecniche di programmazione secondo i diversi paradigmi: _____________
3.2.1
Metodo Top Down ___________________________________________________________________
3.2.2
Metodo Bottom Up ___________________________________________________________________
3.2.3
Programmazione strutturata ____________________________________________________________
29
29
29
30
3.3
Metodologia di costruzione dei programmi. Modularità _______________________________________ 31
3.4
Ingegneria del software, tecniche di documentazione e di manutenzione dei programmi. ____________ 31
4
LABORATORIO _____________________________________________________ 32
4.1
cicli ___________________________________________________________________________________ 32
4.2
caricamento di vettore con numeri casuali ___________________________________________________ 33
4.3
Media, massimo e minimo dei valori di una matrice __________________________________________ 33
4.4
Uso delle funzioni _______________________________________________________________________ 33
4.5
Calcolo del fattoriale _____________________________________________________________________ 34
4.6
Algoritmo di Euclide (MCD) ______________________________________________________________ 34
4.7
programma che dice se x è il fattoriale di qualche numero ______________________________________ 35
4.8
Uso degli operatori logici _________________________________________________________________ 35
4.9
Uso della shell per rinominare files _________________________________________________________ 35
4.10
programma di conversione da decimale a binario _____________________________________________ 36
4.11
operazioni sui files _______________________________________________________________________ 36
4.12
funzione INPUTBOX ____________________________________________________________________ 36
4.13
Calcolo di polinomio _____________________________________________________________________ 36
4.14
Shift di un vettore _______________________________________________________________________ 36
4.15
Ordinamento di un vettore ________________________________________________________________ 36
4.16
Generazione di tabella della verità _________________________________________________________ 36
4.17
Calcolatrice ____________________________________________________________________________ 37
4.18
Numeri di fibonacci _____________________________________________________________________ 37
4.19
Codifica di un testo per sostituzione ________________________________________________________ 37
4.20
Operazioni sulle stringhe _________________________________________________________________ 38
4.21
Campo Minato _________________________________________________________________________ 38
pag 2
5
VARIE _____________________________________________________________ 39
5.1
Appendici ______________________________________________________________________________ 39
5.1.1
Programmazione strutturata secondo il teorema di Jacopini-Böhm __________________________ 39
5.2
Uso delle TABELLE _____________________________________________________________________ 43
5.3
Bibbliografia ___________________________________________________________________________ 45
pag 3
1
MODELLI DELL’INFORMATICA
1.1
Introduzione al concetto di algoritmo
Immaginiamo che dobbiamo formalizzare il pensiero dell’uomo al fine di poter descrivere in un modo preciso
( cioè formale, con una forma ben definita) come risolvere un problema.
Tale pensiero schematizzato, con passi ben precisi, prende il nome di ALGORITMO.
La prima definizione di Algoritmo è stata data da Alan Turing (un algoritmo ha un inizio e una fine, risolve
tutti i problemi dello stesso tipo, non è unico per risolvere un problema, ogni passo determina univocamente
il successivo, è formato da un numero finito di passi)
Argomenti trattati:


definizione formale di algoritmo
ESEMPIO: calcolare la divisione di un numero per 5. Questo si può fare in due modi
Primo algoritmo)
RIS = X / 5
Secondo algoritmo)
Y = X / 10
(che corrisponde a spostare a sinistra la virgola di una posizione)
RIS = Y * 2
Questi due procedimenti sono la soluzione formale al problema del calcolo di un qualunque
numero per 5.

ESEMPIO: Algoritmo per il calcolo del fattoriale

Introduzione al concetto di ottimizzazione degli algoritmi

“
“
di bontà “
“
NOTA: vedasi i concetti di programmazione strutturata
pag 4
1.2
1.2.1
o
o
o
o
o
o
o
o
o
Esempi
Esempio: fare la somma di due numeri
Inizio
A è un numero
B è un numero
Leggi A da tastiera
Leggi B da tastiera
S=A+B
Scrivi S su video
Scrivi S su stampante
Fine
pag 5
1.2.2
Esempio: fare la somma di due numeri con incrementi successivi.
1.2.3
Esempio: numeri pari e numeri dispari
Se si vuole sviluppare un algoritmo per capire se un dato numero N è pari o dispari possiamo procedere ad
eliminare da N stesso tante volte 2 fino a quando N diventa 0 oppure 1. Se resta 1 è dispari altrimenti è pari:
Vediamo
1) acquisire dall’utente il valore di N
2) se N = 0 allora era pari -> Fine programma
pag 6
3) se N = 1 allora era dispari -> Fine programma
4) N = N -2 (cioè ad N togli il valore 2)
5) torna al punto 2
Questo algoritmo ha un difetto. Funziona solo con i numeri positivi. Infatti se N = -3 va in un ciclo infinito in
quanto continua a decrementare N non raggiungendo mai né 0 né 1.
Infatti se N è negativo deve sommare 2 e non sottrarre.
Allora il nuovo programma diventa
1)
2)
3)
4)
5)
6)
acquisire dall’utente il valore di N
se N = 0 allora era pari -> Fine programma
se N = 1 allora era dispari -> Fine programma
se N > 0 allora N = N - 2 (cioè ad N togli il valore 2)
se N < 0 allora N = N +2 (cioè ad N aggiungi il valore 2)
torna al punto 2
1.2.4
Esempio: Divisione
intera tra due numeri positivi
1.2.5
Esempio: Dato un numero N generato a caso trovarlo e contare il numero dei tentativi
1.2.6 Esempio: calcolo delle tasse
Laboratorio: colcolo delle tasse in base alle aliquote seguenti
Imposta sul reddito delle persone fisiche IRPEF
L'Irpef è un'imposta diretta, personale, progressiva per scaglioni che si applica al reddito complessivo netto.
E' regolata dal testo unico dell'imposta sui redditi (DPR n. 917/1986) ed è l'imposta con il numero maggiore
di contribuenti e al primo posto per quanto riguarda il gettito fiscale dello Stato (circa un terzo).
Le nuove aliquote IRPEF - Finanziaria 2007
Nuove Aliquote IRPEF
Scaglione di reddito
23%
fino a 15.000 Euro
27%
da 15.000 a 28.000 Euro
38%
da 28.000 a 55.000 Euro
41%
da 55.000 a 75.000 Euro
43%
Per redditi superiori a 75.000 euro
Esercitazione: Si deve sviluppare un diagramma di flusso che dato X, totale fatturato, ci calcola quanto
dobbiamo pagare di tasse in base alla tabella di cui sopra.
Per esempio, se abbiamo fatturato 16.000 € allora si paga il 23% sui primi 15.000 € e il 27% sui 1000
rimanenti per un totale di 3450 + 270 € = 3720 €
pag 7
Nel caso di fatturato 30000 euro si paga il 23% sui primi 15000 + il 27% dei successivi 13.000 + il 38% degli
ultimi 2000 €
1.2.7
Esempio:
Dire se in un vettore sono di più i valori maggiori di MAX o quelli minori
1.2.8
Esempio: Elevazione a potenza
Se si vuole sviluppare un algoritmo per implementare X Y (Cioè X elevato Y) possiamo pensare di farlo
prendendo una variabile R (Risultato) alla quale moltiplichiamo per Y volte il valore di X .
Infatti X Y è la stessa cosa di fare per Y volte
1)
2)
3)
4)
5)
6)
7)
X* X * X* ... *X
acquisire dall’utente il valore di X
acquisire dall’utente il valore di Y
R=X
Y = Y -1
se Y = 0 allora hai finito
R=R *X
torna a 4
pag 8
1.2.9
Esempio: Macchinetta del caffè
Si deve creare l’algoritmo per programmare una macchinetta che fa caffè e the. Questa ha la caratteristica di
avere due pulsanti (A e B ) per scegliere Caffè oppure The, ed una fessura C per prendere i soldi e dare il
resto. Un caffè costa 1 Euro mentre un the costa 1,5 Euro.
Si vuole che inseriti i soldi (qualunque cifra) la macchinetta sappia come
A
comportarsi (cioè capire se fare il caffè o il the, se i soldi immessi bastano
oppure se deve tornare il resto).
B
C
Start
Inserire ancora
1 – C Euro
Leggi C
C<1
Leggi X
Controlla
X
X=A
X=B
C=1
Controlla
C
Fai caffè
C>1
Sottoprogramma The
Dare resto
C - 1 Euro
LEGENDA:


leggi C = acquisisci i soldi
leggi X = acquisisci la scelta (A o B)
NOTARE:




il programma non ha una fine.
non abbiamo gestito altri tipi di input
abbiamo introdotto il concetto di sottoprogramma
possiamo creare delle variabili per gestire i prezzi
pag 9
1.2.10 Esempio: media dei numeri pari caricati in un vettore
1.2.11 Esempio: Flow Char elevazione a potenza
pag 10
1.2.12 Esempio: matrice 2 * n
Avendo una matrice
MAT
12
15
22
55
22
34
18
14
5
44
M
F
F
M
F
M
M
M
F
F
dove la prima riga indica una età mentre il corrispondente di sotto (nella seconda riga) indica il sesso di una
persona (ESEMPIO: MAT(0,2) indica una persona di 22 anni MAT(1,2) indica che è di sesso femminile),
sviluppare un algoritmo che calcoli la media delle età di tutti i maschi.
Fatto questo calcolare anche la percentuale delle femmine presenti nella lista.
1.2.13 Esempio: Cicli fissi
caricamento vettore
no
start
Input N
Input V(I)
I= 0
I = (n-1)
I= I+1
si
------------------------------------------------------------------------------------------------------ordinamento vettore
v(I) >= v(J)
J=I+1
no
I=0
J=N
no
Si
si
I=I+1
I = (n-1)
Sostituisci
V(I) , V(J)
si
pag 11
J=J+1
end
pag 12
1.3
Soluzione dei problemi: processi algoritmici
.
Costrutti di base
•if then else
•repeat until
•while do
•selezione
•case
Rappresentazione:
•Flow Chart
•codifiche
•struttogramma
e prog. strutturata
Definizione
Documentazione
•ciclo di vita
•progettaz.
•ISO 9000
Algoritmi
Sviluppo:
•top down
•bottom up
•progr. Strutt.
•Stile imperativo
•Stile funzionale
Tipi di programmazione:
•funzionale
•imperativa
•oggetti
•ad eventi
1.4
Approccio:
•top down
•bottom up
•progr. Strutt.
Bontà :
•complessità
•carico comput.
•Velocità
•uso delle risorse
•garbage collection
•threads
•ottimizzazione
Ottimizzazione:
•velocità
•leggibilità
•uso risorse
•uso memoria
Proprietà degli algoritmi: costrutti fondamentali, complessità
Introdurre i concetti che ci permettono di valutare la bontà degli algoritmi.

algoritmo, definizione
Def: Diamo una definizione formale di Algoritmo
Un qualsiasi procedimento eseguibile in modo meccanico è detto algoritmo se :
1.
2.
3.
4.
5.

è formato da un numero finito di passi
ogni passo determina univocamente il successivo
rappresenta la soluzione a tutti i problemi dello stesso tipo
è interpretabile dall’ esecutore
ha un inizio e una fine
flow chart e codifiche
pag 13

struttogramma e programmazione strutturata
Fare vedere come sia difficile mettere su carta un algoritmo che prevede salti, e come sia ancora più
difficile gestire un programma che abbia salti (fa fatica pure un programmatore esperto a districarsi
nella logica di un programma così fatto) e soprattutto come non si possa ottimizzare il codice che
prevede i salti (meglio i cicli for) oltre alla inutilità dell’utilizzo delle cache.




complessità
carico computazionale
ottimizzazione
algoritmi che implementano e sfruttano al massimo i threads
1.5
Costrutti di base
I costrutti di base principali sono:





1.6
if --- then --- else
repeat --- until
while --- do
do --- case
for --- next
Algoritmi notevoli: ordinamento, ricerca, fusione
1.6.1 Ottimizzazione dei Programmi:
Ottimizzazione dei programmi e dell’ordinamento come mezzo per effettuare delle ricerche più performanti

Ottimizzare la leggibilità

Ottimizzare la Velocità:
Si può fare in molti modi che dipendono spesso anche dal tipo di SO e di linguaggio che si sta usando
(ES: un algoritmo che usa una FORK, e quindi processi concorrenti su una piattaforma che non supporta
il multitasking).
Molte regole valgono sempre. Per esempio

conviene usare i cicli FOR NEXT invece di usare dei GOTO. Questo in quanto quando inizia un
ciclo viene memorizzato dal SO l’indirizzo di memoria in cui questo si trova e dove quindi deve
ritornare. Con la GOTO invece devono essere lette tutti i numeri di riga fin quando non si trova
quella giusta. I cicli for next invece vengono trattati con lo stack allocato ogni volta che uno di
questi inizia. Lo spazio sullo stack viene cancellato quando il ciclo finisce. Per questo motivo il
NEXT chiude sempre e solo l’ultimo FOR aperto !
Inoltre le GOTO vanificano l’uso delle cache memory.

Le subroutine vengono trovate anch’esse per scansione, per cui quelle utilizzate più spesso è
meglio metterle in cima al programma. In genere vengono scritte in questo ordine:
Inizializzazioni
Subroutine di servizio
Sottoprogramma 1
Sottoprogramma 1
…
pag 14
Sottoprogramma 1
Programma Principale (MAIN)


Riutilizzare le stesse variabili ove possibile, nel caso che non servano più per altri scopi (ES:
una variabile temp)

Il valore delle variabili viene trovato in apposite tabelle scandite in maniera sequenziale per cui
quelle dichiarate prima sono anche trovate prima -> dichiarare prima quelle più usate.
Ottimizzare lo spazio in memoria:


Vedi [ei1 81]
garbage collection. Viene utilizzata per deallocare dalla memoria le variabili che sappiamo già che
non vengono utilizzate nel resto del codice.
Ricorsione di coda come metodo di risparmio dello stack. Tenere presente che una procedura è
detta ricorsiva quando richiama se stessa !
ESEMPIO: fattoriale di N
con la ricorsione
Procedura P(N)
IF N = 1 THEN RETURN N
ELSE RETURN N* P(N-1)
con un ciclo for
10 Input N
20 RIS= N
30 if N=1 then print RIS else 50
40 end
50 RIS = RIS * N
60 N = N –1
70 goto 30
1.6.2 Algoritmi e complessità
qui
NOTA: la bontà di un algoritmo dipende da tanti fattori che non possono essere determinati a priori. Se ad
esempio stiamo studiando un algoritmo che deve funzionare su una memoria in un cellulare o su un
dispositivo elettronico molto piccolo allora è importante che occupi poca memoria. invece se consideriamo
un algoritmo che funzioni in rete e soddisfa le esigenze di tanti utenti allora deve essere performante (magari
a discapito della memoria che occupa). In genere quindi deve essere il progettista a capire come valutare gli
algoritmi per le sue necessità.
Negli algoritmi che considereremo tenere presente …




… che consideriamo per semplicità sempre liste di numeri interi da ordinare in senso crescente
… quali sono i criteri di valutazione. Cioè
a) numero di righe del codice
b) quanta memoria occupano
c) complessità (intesa come l’esponente di N nel calcolo relativo)
d) applicazione dell’algoritmo (su pochi elementi , su molti su liste preordinate)
… le curve delle prestazioni dei vari algoritmi (bubble e shell si incontrano verso i 55 – 60
elementi da ordinare)
… che si può dimostrare che: N + (N-1) + (N-2) + . . . = N (N + 1) / 2
pag 15
1.6.3
Elenco degli algoritmi di ordinamento (wikipedia)
Vi sono varie classi di algoritmi di ordinamento, i più noti ed utilizzati sono gli algoritmi di ordinamento per
confronto (comparison sort algorithms), ma esistono altre classi caratterizzate da un tempo di esecuzione nel
caso peggiore inferiore a O(nlogn).
Nella tabella seguente sono elencati alcuni algoritmi di ordinamento, riportandone la complessità al caso
Migliore, Medio e Peggiore, la memoria aggiuntiva richiesta, e la stabilità.
Nome
Migliore
Medio
Peggiore
Memoria
Stabile
In place
Avl sort
—
—
—
—
—
—
Bubble sort
O(n)
O(n )
O(n )
O(1)
Sì
Sì
2
2
B sort
—
—
—
—
—
—
B-Tree sort
—
—
—
—
—
—
B-Tree sort
—
—
—
—
—
—
Bitonic sort
—
—
—
—
—
—
Btm sort
—
—
—
—
—
—
Bucket sort
O(n+m)
O(n+m)
O(n+m)
O(m)
—
No
Comb sort
—
—
—
—
—
—
Counting sort
O(n+k)
O(n+k)
O(n+k)
O(n+k)
Sì
Sì
—
—
Gnome sort
Heap sort
O(nlog n) O(nlog n)
—
—
—
—
O(nlog n)
O(1)
No
Sì
Insertion sort
O(n)
O(n + d)
O(n )
O(1)
Sì
Sì
Integer sort
O(n + k)
O(n + k)
O(n + k)
O(1)
Sì
Sì
Intro sort
—
—
—
—
—
—
—
—
Jump sort
Merge sort
O(nlog n) O(nlog n)
2
—
—
—
—
O(nlog n)
O(n)
Sì
No
Ms sort
—
—
—
—
—
—
Oet sort
—
—
—
—
—
—
O(n2)
O(log n)
No
—
—
—
O(n2)
O(log n)
No
Sì
Partition sort
Plasel sort
O(nlog n) O(nlog n)
—
—
—
—
Quicksort
O(nlog n) O(nlog n)
Radix sort
O(n·k/s)
O(n·k/s)
O(n·k/s)
O(n)
Sì
—
Ripple sort
—
—
—
—
—
—
Selection sort
O(n )
O(n )
O(n )
O(1)
No
Sì
Several unique sort
—
—
—
—
—
—
Shaker sort
O(n)
—
O(n2)
O(1)
Sì
Sì
Shear sort
—
—
—
—
—
—
Simple sort
—
—
—
—
—
—
Shell sort
—
—
O(n1.5)
O(1)
No
Sì
Smooth sort
—
—
—
—
—
—
Stupid sort
O(1)
O(n × n!)
illimitato
O(1)
No
Sì
O(nlog n)
O(n)
No
—
O(nlog(3)/log(1.5))
—
—
Sì
Sunrise sort
Trippel sort
2
2
O(nlog n) O(nlog n)
—
—
2
1
QuickSort nel caso peggiore impiega O(n2), ma utilizzando Quickselect per la selezione del pivot, il tempo
diventa O(nlogn)
pag 16
1.6.3.1
Ricerca
1.6.3.1.1 Introduzione
Ci sono vari algoritmi di ricerca. Questi li possiamo fondamentalmente organizzare (vedi [russo 64]) nei modi
seguenti (si assume di ricercare un elemento su n):

ricerca lineare
Si intende la ricerca più semplice. Si scandisce dall’inizio alla fine  tempi che sono proporzionali a
n (statisticamente si fanno n/2 accessi)

ricerca binaria
E’ migliore del precedente, però prevede che la lista sia ordinata. Complessità nell’ordine di log2 di
n.

per calcolo
E’ ancora più efficiente. Si tratta di avere una rappresentazione numerica della chiave (con
qualunque algoritmo). fatto questo interpretando il valore che la chiave assume si posiziona in un
indirizzo ben preciso. In pratica si fa un accesso per associazione.
Tra i metodi di ricerca per calcolo descriviamo il metodo Hash e quello B-Tree.
1.6.3.1.2
1.6.3.1.2.1
Hash
Introduzione
Questo sistema per organizzare gli indici fornisce una funzione H(K) la quale applicata alla chiave K mi
permette di rintracciare l’area di memoria in cui si trova le informazioni che cerco sulla chiave.
H(K) = indirizzo
Tenere presente che :











la struttura è organizzata in blocchi di informazioni dette bucket (in cui mettiamo le registrazioni
logiche)
ogni bucket contiene al massimo C chiavi
un bucket può coincidere con una pagina dati, una pagina virtuale, un insieme di pagine.
Questo non interessa in quanto è un dettaglio implementativo
ho m bucket (0, 1, ... , m-1)
la funzione di hashing restituisce il buchet (quindi un valore compreso tra 0 e m-1)
la funzione di hash serve sia per inserire che per reperire le chiavi
ogni bucket fisicamente ha una dimensione fissa. Quindi sapendo quanto è grande (in byte) e
quanto occupa una chiave è facile determinare C
se si riempie un bucket si va in overflow e deve essere prevista una politica per contenere le
chiavi in più
due chiavi che finiscono nello stesso bucket sono dette sinonime.
per N intendo tutte le chiavi possibili che devo trattare (gestire)
introduco il concetto di fattore di caricamento
Si intende con questo termine il valore:
d = N / (C*m)
cioè il rapporto tra le chiavi che devo gestire e quelle che entrano nella struttura che
predispongo.
pag 17
Se questo valore è basso avrò tendenzialmente pochi overflow. Se si avvicina a 1 o lo supera
vuol dire che la struttura è sottodimensionata.
Detto quanto sopra appare chiaro il funzionamento di una struttura ad hash. Prendo la chiave, la traduco in
numerica, vi applico la funzione di hash e scopro in quale bucket andare a cercare. questo riduce gli accessi
in maniera tanto migliore quanto maggiore è il numero dei bucket.
Resta solo da definire la funzione di hash cercando di risolvere il problema della equidistribuzione. Infatti se
le chiavi le distribuisco bene allora avrò anche pochi overflow.
La funzione che è stato dimostrato rispondere a questi requisiti è:
H(K) = K * costante mod m
Infatti questa fornisce sempre valori compresi tra 0 e m-1, e si dimostra che tanto più grande è la costante
più i valori dati sono equiprobabili.
Cioè la probabilità che data una chiave trovo un preciso bucket è 1/m.
1.6.3.1.2.2 Gestione degli overflow
E’ ovvio che in fase di progettazione si debba fare in modo che ci siano pochi overflow.
In ogni caso devono essere previste le gestioni. [da1 pg 130]
Abbiamo principalmente due tecniche. Supponiamo per semplicità di avere C=2 (gestiamo due chiavi per
bucket) e due bucket in tutto : i e j.
a) Soluzione a liste circolari
In questo caso se H(K) di tre chiavi mi da sempre j allora le prime due le metto nel bucket j e la terza in i.
scandendo le liste finché non trovo spazio.
Implica più scansioni anche per la ricerca. Va bene se prevedo pochissimi overflow.
b) Soluzione a catene distinte
Prevede di allocare delle liste in più in caso di overflow. Le liste sono ordinate in maniera che se ci
finisco evito di controllarle tutte.
NOTA: Proposte più avanzate introducono l’Hashing dinamico, dove cioè il numero dei bucket non è
determinato a priori. In questo caso si devono implementare delle politiche di riorganizzazione.
1.6.3.2 Algoritmi di ordinamento
Sono importanti in quanto certi algoritmi di ricerca si basano su liste già ordinate.
Negli ordinamenti il problema è quello di minimizzare il numero di operazioni medie necessarie.
Ci sono anche in [corso pg 96 ] . In particolare si potrebbe fare qualche esempio su uso di struttogramma e
sul calcolo della complessità
Fare delle considerazioni sul modo di calcolare le prestazioni:



tempo di swap
parametri di calcolo
ordine di complessità
pag 18
Tipi di algoritmi:
1. A cicli fissi [ei1 88]
Questo metodo consiste nel confrontare il primo elemento con tutti gli altri ed eventualmente
mettere al primo posto quello più piccolo. Dopo si procede partendo dal secondo elemento e
confrontarlo con tutti i rimanenti, e così via.
Il vantaggio è che l’algoritmo si scrive in 5 linee (immaginiamo l’utilità su un cellulare !!). Lo
svantaggio è che se la lista è già ordinata si faranno sempre lo stesso numero di scansioni
(appunto i cicli sono fissi e sono N-1, il primo per il primo numero , il secondo per il secondo
numero, …)
Programma:
FOR I =1 TO N-1
FOR J = I+1 TO N
IF A(I) >A(J) THEN SWAP (I,J)
NEXT
NEXT
Struttogramma
FOR
I
=
1
FOR J =
I+1
IF A(I) >A(J)
no
si
SWAP (I,J)
N
N
–
1
2. Metodo Bubble Sort [ei1 89]
Molti dicono che l’unico vantaggio del metodo bubble sort sia nel nome accattivante e che per il resto
non sia buono.
TEST = TRUE
I= 1
REPEAT
IF A(I) > A(I+1) THEN SWAP(I,J) : TEST = FALSE
I=I+1
IF I = N THEN I =1 ; TEST = TRUE
UNTIL TEST = TRUE
La complessità è dell’ordine N* (N+1)/2 ~ N²
pag 19
1.6.3.3
Algoritmi di ricerca
Si può guardare in [ei1 86].
Il problema è quello di ottimizzare il tempo medio di confronto tra i valori ( Vedasi [ei1 87]) che in genere è
T=Tm*N/2 dove Tm è il tempo medio di un confronto.

Ricerca Lineare:
Se A è una costante di confronto con il metodo utilizzato allora il tempo medio è Tm=A* N.

Ricerca Binaria:
Se B è una costante di confronto con il metodo utilizzato allora il tempo medio è Tm=B* Log2(N)
NOTA: per un numero N di elementi minore di 50 .. 100 allora conviene il metodo lineare altrimenti quello
binario.
NOTA: Vi vedano le tecniche di ricerca nella sezione relativa.
pag 20
2
STRUTTURE DATI
Le strutture dati servono come supporto per programmi complessi
2.1
Tipi di dati
Tipo di dati
Spazio su disco
Intervallo
Byte
1 byte
Da 0 a 255
Boolean
2 byte
True o False
Le variabili che includono informazioni di tipo
vero/falso, sì/no e on/off possono essere dichiarate
come tipo Boolean. Il valore predefinito di Boolean è
False. Nell'esempio seguente, blnRunning è una
variabile Boolean in cui è memorizzata
un'impostazione di tipo sì/no.
Dim blnRunning As Boolean
' Verifica che il nastro stia
avanzando.
If Recorder.Direction = 1 Then
blnRunning = True
End if
pag 21
Integer
2 byte
Da -32.768 a 32.767
Long
(intero lungo)
4 byte
Da -2.147.483.648 a 2.147.483.6477
Single
(virgola mobile a
precisione semplice)
4 byte
Da -3,402823E38 a -1,401298E-45 per valori negativi;
da 1,401298E-45 a 3,402823E38 per valori positivi
Double
(virgola mobile a
precisione doppia)
8 byte
Da -1,79769313486232E308 a
-4,94065645841247E-324 per valori negativi; da
4,94065645841247E-324 a 1,79769313486232E308
per valori positivi.
Currency
(intero diviso)
8 byte
Da -922.337.203.685.477,5808 a
922.337.203.685.477,5807
Decimal
14 byte
+/-79.228.162.514.264.337.593.543.950.335 senza
virgola;
+/-7,9228162514264337593543950335 con 28
decimali; il numero minore diverso da zero è
+/-0,0000000000000000000000000001
Date
8 byte
Dall'1 gennaio 100 al 31 dicembre 9999
Object
4 byte
Qualsiasi riferimento Object
String
(lunghezza variabile)
10 byte +
lunghezza
stringa
Da 0 a circa 2 miliardi
Le variabili in cui vengono sempre memorizzate
stringhe e mai valori numerici possono essere
dichiarate come tipo String:
Private S As String
È quindi possibile assegnarvi stringhe e gestirle tramite
funzioni stringa:
S = "Database"
Variant
(con numeri)
16 byte
Variant
(con caratteri)
22 byte +
lunghezza
stringa
Qualsiasi valore numerico fino all'intervallo di un
Double
pag 22
2.2
I vettori (ARRAY)
Un elemento di un array e’ uno dei valori contenuti nell’array e ad esso si accede attraverso un indice
che individua la sua posizione all’interno dell’array.
pag 23
Nome del vettore
a[0]
-45
a[1]
6
a[2]
0
a[3]
72
a[4]
1543
a[5]
-89
a[6]
0
a[7]
62
a[8]
-3
a[9]
1
a[10]
6453
a[11]
78
Indice dell’elemento nel
vettore
2.3
3
Le matrici
PROGRAMMAZIONE E LINGUAGGI
3.1
Introduzione alla programmazione
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",

dall’altra 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
pag 24
matematico inglese Alan Turing, inventore della famosa "macchina". Qui la ferraglia non c’entra 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, l’unica 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.
3.1.1
Le funzioni
Procedure e funzioni
Un programma puo' certamente essere realizzato scrivendo una sequenza di istruzioni che termina
con il programma stesso.
La soluzione non e' malvagia, ma si presta ad innumerevoli svantaggi. Il primo fra tutti e'
l'impossibilita' di gestire progetti di grosse dimensioni: finche' si tratta di correggere o di modificare
codice di un migliaio di righe non ci sono problemi, ma quando il codice raggiunge dimensioni
dell'ordine di 100.000 righe ed oltre...
Tutti i linguaggi ad alto livello consentono di programmare in modo piu' elegante ed efficace
attraverso le astrazioni mediante procedure e funzioni.
Queste astrazioni consentono di creare unita' ben distinte all'interno di un programma raggruppando
una sequenza di istruzioni, dandole un nome e stabilendo le modalita' di comunicazione con il resto
del programma. Queste istruzioni, generalmente, vengono raggruppate tenendo conto del compito
da svolgere, anche se qualsiasi criterio e' permesso.
Il programmatore, per usare questa sequenza di istruzioni, avra' bisogno solamente di indicarne il
nome, al resto pensera' il compilatore o il traduttore del linguaggio.
Si ha un'astrazione della nozione di istruzione (la procedura) e di operatore (la funzione). Infatti, la
procedura non e' altro che una istruzione complessa che puo' essere utilizzata ovunque possa una
istruzione semplice: il compito principale di una procedura e' il modificare il contenuto di locazioni
di memoria. La funzione, invece, puo' essere utilizzata in qualunque valutazione di espressione: ha
il compito di fornire un valore, anche se non e' escluso che, nel fornirlo, assuma pure i
comportamenti della procedura.
Come le istruzioni e gli operatori, anche le procedure e le funzioni possono accettare degli
argomenti (parametri). Nel paragrafo successivo verra' mostrato come.
Infine, e' stato detto che la funzione e' un'astrazione del concetto di operatore. Ma un operatore, la
maggior parte delle volte, e' sovraccarico, cioe' e' definito su piu' tipi di dato diversi, piu' o meno
simili. Purtroppo, non tutti i linguaggi di programmazione ad alto livello, permettono il sovraccarico
delle funzioni (al posto di sovraccarico si parla overloading delle funzioni), cioe', per alcuni
linguaggi, una funzione non puo' essere definita piu' volte all'interno di un programma per diversi
tipi di argomenti.
Passaggio parametri
Come visto nella sezione precedente, per la sequenza di istruzioni che realizza una procedura o una
funzione, viene stabilita la modalita' di comunicazione con il resto del programma. I parametri
servono alla comunicazione esplicita, ed interna, di dati e risultati fra procedura o funzione
(chiamato) ed il resto del programma (chiamante, puo' essere anche un'altra funzione e procedura).
Dal lato del codice "chiamato", si parla di parametri formali. Dal lato "chiamante", si parla di
parametri attuali.
pag 25
I parametri formali sono degli identificatori speciali dichiarati localmente al chiamato. L'essere
speciale deriva dall'associazione che viene fatta tra essi ed i parametri attuali, nel meccanismo di
passaggio dei parametri. I parametri attuali possono rappresentare dei valori o degli identificatori
esterni al chiamato.
Vediamo un esempio.
void A(int x, int y)
{
...
}
void B()
{
...
int h=4;
...
A(h,5);
...
}
Nel codice mostrato, la funzione B chiama la A. In questo processo di chiamata, "h" e "5" sono i
parametri attuali, mentre invece "x" ed "y" sono i parametri formali.
Il meccanismo di passaggio non sempre e' lo stesso: esistono diverse tecniche.
Passaggio per valore:
E' il modo piu' semplice per passare parametri: i parametri attuali vengono valutati (ne viene
calcolato ed estratto il valore) ed i valori sono associati ai parametri formali.
Nel codice precedente, "x" assumera' il valore di "h" ed "y" assumera' il valore "5". Saranno
comunque tutte entita' diverse, cioe' i valori dei parametri attuali non verranno alterati in alcun
modo.
Passaggio per riferimento (o per indirizzo):
Viene passato l'indirizzo della memoria contenente i valori dei parametri attuali. Quindi,
quest'ultmi, condivideranno l'indirizzo con i parametri formali. Ogni modifica fatta ai formali, si
riflettera' sugli attuali.
In alcuni linguaggi, se il parametro passato per riferimento non e' un identificatore ma una
espressione (puramente numerica o composta da identificatori), allora viene valutata l'espressione
ed il valore calcolato viene posto in una nuova locazione di memoria. L'indirizzo di questa nuova
locazione di memoria sara' quello passato.
Metodo "Copia-Ripristina"
E' un ibrido dei due metodi precedenti. L'associazione e' identica al passaggio per valore, quindi i
parametri formali verranno modificati indipendentemente da quelli attuali. Alla fine della procedura
o della funzione, pero', il valore dei parametri formali viene copiato in quelli attuali.
Questo metodo potrebbe essere utile nella programmazione parallela, dove una funzione continua
ad accedere ad un identificatore mentre viene modificato da una procedura appena chiamata.
Finche' il chiamato non avra' terminato il proprio lavoro, il chiamante usera' il valore vecchio e non
uno inconsistente.
Ricorsione
La ricorsione e' una tecnica altamente inefficiente in termini di memoria utilizzata e di tempo
pag 26
impiegato nell'esecuzione, a causa delle modalita' di implementazione nei vari linguaggi. Ha il
notevole vantaggio, pero', di introdurre nei programmi eleganza, naturalezza descrittiva, facilita' di
espressione per problemi particolari, sinteticita'.
In matematica, una funzione e' definita ricorsivamente quando, nella sua definizione, chiama se
stessa.
Un esempio puo' essere la funzione fattoriale f(x)=x! (es. 5!=5*4*3*2*1=120). Per definizione il
fattoriale vale 1 se x=0, vale x*f(x-1) se x>0
(5!=5*4!=5*4*3!=5*4*3*2!=5*4*3*2*1!=5*4*3*2*1).
Come si puo' osservare, la definizione avviene in due passi distinti: si ha un passo base ed un passo
induttivo.
Il passo base consiste nella "condizione di blocco" della funzione. Nella funzione precedente, come
in tutte le altre funzioni ricorsive, il passo base consiste nel dare un valore indipendente dalla
variabile, sotto certe condizioni. L'utilita' del passo base sta nell'evitare alla funzione di essere
applicata infinite volte (oppure, nel caso del fattoriale, di arrivare ad un punto in cui la funzione non
e' definita, rendendo senza senso la formula).
Il passo induttivo consiste nella definizione della funzione nel caso generale (in termini della
funzione stessa).
Come ulteriore esempio, vediamo la realizzazione, in Java, della funzione che calcola l'elevamento
a potenza (positiva) di una base (positiva anch'essa).
La funzione matematica e' f(x,y)=x^y. Puo' essere definita ricorsivamente come 1 per y=0 e
x*f(x,y-1) per y>0.
Il seguente codice realizza la funzione:
long Eleva(int x, int y)
{
if (y==0) return 1;
else return x*Eleva(x,y-1);
}
3.1.2
Tipi di linguaggi
NOTA: Fare una introduzione sui linguaggi classificati in senso verticale (macchina, C, … , linguaggi ad alto
livello) e in senso orizzontale (imperativi, ad oggetti , …)
Questi argomenti sono in stretto contatto con l’architettura 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
l’astrazione dalla particolare architettura della macchina "fisica" attraverso l’uso delle istruzioni di una
macchina "logica" definita dallo stesso linguaggio. Piuttosto che programmare nell’Assembler 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.
3.1.3 Linguaggi imperativi
Un poco più su come livello di astrazione ci sono i cosiddetti linguaggi imperativi, in cui l’idea di fondo è
che:
 il programmatore ha l’obbligo di definire in tutti i dettagli l’algoritmo che sta costruendo,
 ha a disposizione strumenti che richiamano più un linguaggio naturale che la macchina che lo
esegue.
pag 27
Importante è qui l’idea 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,
alternativa
ciclo.
Importanti sono anche i concetti di


procedura e programmazione ricorsiva,
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 dall’altra. 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.
3.1.4 Stile funzionale
Lo stile imperativo non è l’unico 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 l’eliminazione dell’assegnamento di valore ad una variabile, sostituita dall’uso di funzioni e relazioni.
La loro espressività è molto diversa da quella dei programmi imperativi, tanto da essere linguaggi di elezione
nei campi dell’intelligenza artificiale.
3.1.5
Programmazione ad oggetti
NOTA:Vedi “Appendice A : Oggetti” alla fine.
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 l’impressione 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 dell’altro".
La capacità di utilizzare il lavoro di altri viene, nei linguaggi OO, dai concetti di



incapsulamento,
ereditarietà
polimorfismo.
Questi permettono di dare al linguaggio che li implementa, sia la potenza dell’astrazione 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 l’altro dei due versanti.
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 dell’attenzione, come fosse il progenitore della specie.
pag 28
NOTA: 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 l’Object Pascal (Delphi), dal
Modula-2 Modula-3 e da Ada Ada 95.
Qualcuno di questi concetti è filtrato addirittura nella struttura del Basic.
3.1.6 Programmazione concorrente ed a eventi
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 (e deadlock). Il secondo
riguardo il concetto di interruzione del flusso di programma e salto alla procedura di gestione dell’evento.
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.
3.2
dati e procedure, linguaggi e tecniche di programmazione secondo i diversi paradigmi:
Una volta definito il progetto e fatta l’analisi (e anche la scelta del linguaggio che si deve adoperare) si deve
procedere alla ricerca dell’algoritmo e alla sua definizione  approccio top-down o bottom-up. [ei1 75]
NOTA: il medoto deduttivo partiva da una idea sintetica e generale per arrivare a conseguenze logiche
sempre più dettagliate. Quello induttivo invece partiva dal particolare per arrivare agli aspetti generali. La
trasposizione in informatica di questi due concetti a portato ai metodi top-down e bottom-up [ei1 75].
3.2.1 Metodo Top Down
E’ quello che va per la maggiore in quanto i linguaggi moderni sono orientati alla programmazione strutturate
che si avvicina di più a questo modello [ei1 76].
NOTA: Vedere la parte in [ei1 76 ]
Si parte dalla stesura dei concetti generali per sviluppare in seguito i dettagli in maniera ricorsiva. In questo
modo risulta facile rimanere aderenti alla realtà anche in mancanza di una analisi ferrea.
Se l’esito della fase di test dovesse risultare negativo allora si scende di un livello (nella particolarità) fino a
quando non si individua l’errore.
Questa fase di ricerca di un errore è più macchinosa che non nel metodo Bottom Up in quanto si deve salire
e scendere continuamente di livello mentre nell’altra metodologia si rimane sempre allo stesso livello.
3.2.2 Metodo Bottom Up
Vedere la parte in [ei1 75].
NOTA: Con questo metodo è difficile rimanere aderenti alle specifiche generali se queste non sono state
rigorosamente definite. Infatti partendo dalla definizione di tanti particolari si può incorrere nel rischio di
metterli insieme e arrivare a dei risultati indesiderati nati appunto dal fatto che la progettazione è stata fatta
male.
Infatti niente è più facile che partendo dalla definizione di tante piccole parti poi alla fine si arrivi, mettendole
insieme, ad avere un risultato che non è perfettamente quello desiderato.
pag 29
3.2.3 Programmazione strutturata
Vedi [ei1 76] ed [ei6 123].
NOTA: prima che si introducesse la programmazione strutturata si usavano linguaggi in cui si facevano i
salti. Questo implicava che la bravura di un programmatore dipendeva dalla sua capacità di non perdersi tra
di questi.
NOTA: La documentazione dei programmi è una voce di costo che si possono sorbire solo i CED. Questo
implica che per poter fare una buona manutenzione dei programmi devono essere più chiari possibile. Se si
usa un approccio strutturato questo viene più semplice.
La programmazione strutturata si basa su seguenti regole:



Il programma deve essere composto da tanti moduli indipendenti [ei1 77]. Ogni modulo ha un
punto di ingresso e uno di uscita (istruzione END).
La dichiarazione di variabili fatta ai livelli inferiori risulta invisibile ai livelli superiori per cui si evita
così anche la confusione sull’uso delle variabili.
ciascun programma è sviluppato usando solo i costrutti fondamentali: Sequenza, Selezione,
Iterazione
NOTA: Questi costrutti hanno un solo punto di ingresso e uno solo di uscita.
Ogni programma deve essere commentato e indentato
NOTA: E’ possibile costruire programmi strutturati anche con programmi che non sono orientati a questo
(come il BASIC) in quanto le regole usate sono molto semplici, ma si complica molto il codice e anche la
leggibilità (vedi esempi di [ei1 79])
Riassumendo:
a) La programmazione strutturata si sposa molto bene con la metodologia Top-Down (che inoltre
consente sempre di avere uno sguardo sull'’intero progetto senza mai perdersi)
b) Il programma deve essere modulare possibilmente di piccole dimensioni (si consiglia che un
modulo stia in una pagina)
c) ci deve essere un uso delle sole tre strutture fondamentali: selezione, sequenza, iterazione
Selezione
Iterazione
Sequenza
IMPORTANTE: Bohm e Jacopini hanno dimostrato che con questi tre costrutti si può costruire qualunque
programma che è rappresentabile mediante un diagramma a blocchi in maniera equivalente [ei6 123].
pag 30
Alcuni propongono di aggiungere a queste tre strutture anche un’altra che faccia da CASE e un’altra da DOTEST (che differisce solo per il fatto che il controllo lo fa alla fine). In ogni caso sono delle derivate in quanto
si possono ricostruire con gli altri tre costrutti.
3.3
Metodologia di costruzione dei programmi. Modularità
NOTA: Sarebbe interessante fare un collegamento con il concetto di ereditarietà dei linguaggi OO.
Si può parlare, in ambito di programmazione ad oggetti, di




3.4
package
classi
istanze
importazione
Ingegneria del software, tecniche di documentazione e di manutenzione dei programmi.
Come documentare un programma è trattato [ei6 122]. In particolare si descrivono i concetti principali per
fare una buona documentazione del software:
a) Un buon programma non è completo senza documentazione (anche se funziona). Infatti
dovrebbe avere almeno il manuale utente.
b) La documentazione deve essere scritta dalle prime fasi della progettazione del software e non
alla fine in quanto si perderebbero dei concetti importanti.
Fanno parte della documentazione di un progetto (NOTA: fare parallelismo con il ciclo di vita del software,
fare esempio di analisi sbagliate):
a) la descrizione della analisi del problema
b) la descrizione delle procedure per risolverlo (magari mediante diagrammi a blocco, senza
dettagli implementativi ma solo di I/O)
c) la descrizione di come i moduli sono collegati tra di loro
d) la descrizione degli INPUT e OUTPUT
e) la descrizione dei tracciati delle tabelle
f) la descrizione di tutte le variabili usate (o almeno delle più significative) con un occhio di
riguardo a quelle che causano delle scelte.
g) la descrizione di tutti i casi di prova (infatti in questo caso se si dovessero fare delle modifiche
al programma sarebbe molto facile verificare se il programma è ancora valido rieffettuando tutte
le prove già superate -> infatti spesso per risolvere un problema se ne causa un’altro). Tale
elenco dovrà essere arricchito ogni volta che si studiano nuovi casi o che si facciano delle
modifiche.
h) un manuale per l’utente è uno per l’amministratore (sviluppatore) del programma
Infine bisogna tenere presente che una ottima documentazione se non aggiornata ogni qualvolta si fanno
delle modifiche ad un programma diventa inutile.
NOTA: Un’altra utile osservazione si deve fare su come fare per rendere rintracciabile la documentazione
fatta in quanto si può incorrere nel pericolo che si scriva tanto ma nessuno sappia più dove andare a trovare
pag 31
le parti interessanti soprattutto quando si parla di moduli che possono essere utilizzati in tanti programmi
(vedi la tecnica per mettere in relazione molti moduli a molti programmi e anche di come lo standard Qualità
ISO 9000 nella parte che riguarda il software tratta la gestione della documentazione
4
LABORATORIO
4.1
cicli
Questi cicli visualizzano i primi 10 numeri:
ESEMPIO 01
for i = 1 to 10
msgbox(“ numero : “ & i )
next
ESEMPIO 02
i=1
do
msgbox(“ numero : “ & i )
i=i+1
loop while (I < 11)
ESEMPIO 03
i=1
do
msgbox(“ numero : “ & i )
i=i+1
loop while (I =< 10)
ESEMPIO 04
i=1
do
msgbox(“ numero : “ & i )
i=i+1
loop until ( i> 10)
Questi cicli visualizzano i primi 10 numeri tranne il 7
ESEMPIO 01
for i = 1 to 10
if (i <> 7) then
msgbox(“ numero : “ & i )
end if
next
ESEMPIO 02
pag 32
i=1
do
msgbox if (i <> 7) then
msgbox(“ numero : “ & i )
end if
i=i+1
loop while (I =< 10)
4.2
caricamento di vettore con numeri casuali
Public Sub carica()
'---------------------------------------------''- procedura di caricamento
'---------------------------------------------Text1.Text = ""
For i = 0 To 9
v(i) = Int((Rnd) * 100) '--- genera numeri casuali
Text1.Text = Text1.Text & " " & v(i)
Next
End Sub
4.3
Media, massimo e minimo dei valori di una matrice
4.4
Uso delle funzioni
pag 33
4.5
Calcolo del fattoriale
Il fattoriale di un numero N si indica con:
fattoriale = N !
e coincide con :
N ! = N * (N – 1) * (N – 2) * (N – 3) . . . * 1
ESEMPIO 1 : Fattoriale di 4
4 ! = 4 * 3 * 2 * 1 = 24
ESEMPIO 2 : Fattoriale di 5
5 ! = 5 * 4 * 3 * 2 * 1 = 24
Da notare che il fattoriale di
5 è uguale a 5 * (fattoriale di 4)
NOTA: il fattoriale si può implementare in maniera iterativa o ricorsiva
4.6
Algoritmo di Euclide (MCD)
Questo algoritmo serve per calcolare il Massimo Comun Divisore con un algoritmo inventato da Euclide
(questo è uno dei primi algoritmi della storia)
Si basa su un concetto semplice. Se abbiamo A e B allora il MCD di questi è uguale al MCD di B e R, dove
R è il resto della divisione tra A e B.
ES: A = 32 e B = 12








R = A / B = 32 / 12 = 2 con il resto di 8 (quindi R = 8 )
adesso A = B e B = R (quindi A = 12 e B = 8)
si ripete
R = A / B = 12 / 8 = 1 con il resto di 4
adesso A = B e B = R (quindi A = 8 e B = 4)
si ripete
R = A / B = 8 / 4 = 2 con il resto di 0
l’ultimo valore di B è 4 quindi il MCD è 4
Infatti 32 = 2^4 (2 elevato alla quarta) mentre 12 = 2^2 * 3 , quindi il Massimo dei comun divisori è 4
L’algoritmo è semplice:

siano A e B assegnati
pag 34



si calcola R
se R = 0 allora B è il MCD
altrimenti si assegnano ad A il valore di B e a B il valore di R e si ricomincia
CODICE:
Private Sub Command1_Click()
Dim x As Integer
Dim y As Integer
x = InputBox("dammi il numero")
y = InputBox("dammi l'altro numero")
R = x Mod y
If R = 0 Then
MsgBox ("il M.C.D. è " & y)
Else
Do
MsgBox (" X = " & x & " Y = " & y)
x=y
y=R
R = x Mod y
Loop Until R = 0
MsgBox ("il M.C.D. è " & y)
End If
End Sub
4.7
programma che dice se x è il fattoriale di qualche numero
Dato un numero N intero calcolare di quale numero è il fattoriale (se lo è di qualche numero)
Esempio: dato N = 24 si deve calcolare che è il fattoriale di 4 (infatti F(4) = 24 )
4.8
Uso degli operatori logici
4.9
Uso della shell per rinominare files
Il seguente esempio rinomina il file c:\q\c.txt in
d.txt (sempre nella stessa cartella
Private Sub Command1_Click()
Shell "cmd.exe /c rename c:\q\c.txt d.txt"
End Sub
pag 35
4.10 programma di conversione da decimale a binario
Fare un programma che dato un numero decimale ritorna la sua conversione in binario.
4.11 operazioni sui files
Vedi il progetto completo
4.12 funzione INPUTBOX
4.13 Calcolo di polinomio
Si può caricare un vettore con i valori della funzione di n-esima potenza:



caricare n = grado del polinomio (es: 3)
caricare i valori v0, v1, v2, v3
allora si deve calcolare risultato = v0 + v1* X + v2*X² + v3*X³
4.14 Shift di un vettore



spostamento in un vettore in avanti di n posizioni.
spostamento a partire dalla cella x e finire alla Y
fare ordinamento per inserimento usando questi criteri.
4.15 Ordinamento di un vettore
4.16 Generazione di tabella della verità
pag 36
Dato in input il numero di bit di cui calcolare le combinazioni si deve visualizzare a video la tabella della
verità per l’ OR o per l’ AND
Esempio: Se N = 2 e si deve calcolare l’ AND:
______AND_____
00
0
01
0
10
0
11
1
Esempio: Se N = 3 e si deve calcolare l’ AND:
______AND_____
000
0
001
0
010
0
011
0
100
0
101
0
110
0
111
1
Esempio: Se N = 3 e si deve calcolare l’ OR:
______OR_____
000
0
001
1
010
1
011
1
100
1
101
1
110
1
111
1
4.17 Calcolatrice
4.18 Numeri di fibonacci
La serie di fibonacci dice che ogni numero è dato dalla somma dei due precedenti.
4.19 Codifica di un testo per sostituzione
Dato un testo fare attraverso una chiave segreta la sostituzione delle lettere con numeri (compreso lo
spazio) al fine di crittarlo
pag 37
4.20 Operazioni sulle stringhe
4.21 Campo Minato
pag 38
5
VARIE
5.1
5.1.1
Appendici
Programmazione strutturata secondo il teorema di Jacopini-Böhm
NOTA: mancano le immagini
Come abbiamo visto quando si è trattato della macchina di Turing, un algoritmo è essenzialmente costituito
dai seguenti elementi:



un numero finito di operazioni, o meglio funzioni, da applicare ai dati per trasformarli;
la distinzione tra un numero finito di "stati di computazione " nel programma e la capacità di
saltare da uno all'altro tramite l'istruzione "goto" (vai a ..);
una "struttura di controllo" capace di distinguere la situazione del momento e agire di
conseguenza (cioè applicare operazioni e salti), che non è altro che la if .. then .. (se .. allora ..).
Se noi abbiamo delle operazioni e le applichiamo una di seguito all'altra sempre nello stesso modo
inventiamo solo un altro tipo di operazione. Quello che rende gli algoritmi capaci di fare molto di più di
questo è che ad ogni passo possono scegliere, a seconda dello stato in cui sono giunti, come eseguire
l'operazione presente e quale passo eseguire dopo.
La situazione è ben rappresentata da un grafico in cui ci siano un numero finito di "nodi", rappresentanti gli
stati di computazione, e da ognuno di essi escano delle frecce verso alcuni altri nodi. Il progredire del calcolo
può essere visualizzato così: si parte dal nodo iniziale, eseguendo le operazioni lì specificate, e si passa al
nodo successivo seguendo una delle frecce, quella corrispondente alla situazione in cui ci si trova; qui giunti
si ripete, eseguendo le operazioni del nodo e scegliendo il successivo; e così via finché si trova l'istruzione di
termine del calcolo.
L'aspetto grafico della computazione sarà il cosiddetto "piatto di spaghetti", con un groviglio di linee tra un
punto e l'altro. Quando l'arte della programmazione muoveva i primi passi i programmatori consideravano
dimostrazione di bravura programmare con il minor numero di istruzioni possibili, e questo faceva sì che si
tendesse a riutilizzare pezzi di codice semplicemente saltando al loro inizio.
Ne risultavano complicati collegamenti tra le varie parti del programma, non facilmente intelleggibili a chi non
lo avesse scritto.
NOTA: Il problema è che così facendo il programma risulta difficilmente comprensibile anche al suo
estensore, specie quando è grande o quando questo gli pone mano dopo una lunga pausa. Inoltre, pur se
questo modo di lavorare è ancora praticabile da un singolo programmatore, diventa del tutto inconcepibile
per un lavoro di gruppo, quando è necessario che il codice sia facilmente capito anche da persone che non
lo hanno scritto.
Si è arrivati dunque, verso la fine degli anni sessanta, a cambiare completamente la visione di cosa sia un
programma ben scritto, e a non considerare più la capacità di districarsi in mezzo ad aggrovigliate matasse
di goto come principale caratteristica del buon programmatore.
Lo schema che alla fine è emerso, detto "programmazione strutturata", si propone di dare l'aspetto di un
flusso ordinato tra un inizio ed una fine a programmi che di per sé sarebbero intricati. Il modello è il
"programma sequenziale", nel quale si applicano le varie operazioni una di seguito all'altra, in modo
ordinato, senza alternative possibili. Abbiamo detto però che la semplice sequenza non può esprimere tutta
pag 39
la potenza degli algoritmi poiché questa è essenzialmente contenuta nella capacità di scegliere tra due
alternative. Come si possono dunque conciliare queste due esigenze?
L'idea è semplicissima: basta imitare il linguaggio naturale. Il goto è concepibile solo per una macchina i cui
"pensieri" hanno dei ben determinati indirizzi, mentre in un linguaggio naturale un algoritmo è già espresso in
una forma strutturata che corrisponde allo "svolgimento temporale" di una particolare computazione, quello
cioè che si chiama un "processo". In un linguaggio naturale le costruzioni utilizzate sono "fai questo ... , dopo
fai quello ... ", "se ... fai questo ... altrimenti fai quello ... ", "finché ... fai così ... " oppure "ripeti 5 volte questo
... ", che sono proprio le costruzioni di "controllo del flusso", sequenza, alternativa e ciclo, che saranno al
centro di questa discussione.
Possiamo dire che i linguaggi vicini alla macchina, come i programmi di Turing, i linguaggi Assembler o i più
vecchi linguaggi di alto livello tendono naturalmente ad avere una struttura "a ragnatela spaziale", creata dai
goto, mentre i linguaggi naturali e quelli di più alto livello tra i linguaggi di programmazione hanno una
struttura temporale data dal "seguire lo svolgimento del processo".
Questo fatto è visibile nel Fortran, che come primo linguaggio di livello superiore a quello della macchina ha
ancora strutture di controllo molto primitive, specie nelle prime versioni. La tendenza ad imitare il linguaggio
naturale è iniziata con Algol (1960), linguaggio che era appunto noto per la sua verbosità e che ha dato in
eredità le sue strutture di controllo a quasi tutti i suoi successori. Non si può però dire che abbia inventato
niente di straordinario, semplicemente perché queste strutture sono solo l'espressione, in inglese, delle frasi
viste sopra, "if ... then ... else ... " ecc. .
L'utilizzo delle forme di controllo di flusso "alla Algol", cioè l'alternativa, nella quale il flusso si divide in due
rami per poi riunirsi, e il ciclo, in cui il flusso gira in tondo come in un gorgo, poneva il problema se tutti i
programmi potessero essere scritti solo con essi, evitando l'ormai famigerato goto. La cosa è evidente per
un programma pensato fin dall'inizio in termini naturali, lo è molto meno se si richiede di riscrivere in forma
strutturata un programma pieno di salti tra un punto e l'altro.
La cosa fu comunque risolta teoricamente nel 1966 dal teorema di Jacopini-Böhm: ogni programma di
Turing può essere espresso con sequenze, alternative o cicli di blocchi di istruzioni. Il seguito sarà dedicato
a chiarire i termini della questione, spesso non esposti accuratamente, soprattutto riguardo al fondamentale
concetto di blocco.
Esprimeremo i concetti in un quasi-C (misto a Basic e Pascal), non perché il C sia espressione di
programmazione strutturata, cosa non vera in quanto, come molti altri linguaggi, mantiene l'istruzione goto,
ma perché dispone di un simbolo, la parentesi graffa, che rende facilmente visibile il concetto, fondamentale
per la comprensione del teorema, di blocco di istruzioni. Le caratteristiche dei vari termini quindi non si
riferiscono al C reale.
Il blocco di istruzioni è un sottoprogramma, cioè un insieme di istruzioni in sé completo, che non ha bisogno
di far riferimento ad altro. E' importante notare che questo implica che il blocco ha un'unica uscita, la fine del
blocco, e tutti i processi che eseguono istruzioni del blocco terminano qui. Se infatti ci fossero delle "uscite
laterali" tramite delle istruzioni di salto, queste dovrebbero far riferimento ai nomi (etichette) dei punti di
programma a cui saltare, per cui il sottoprogramma non sarebbe più autonomo, facendo riferimento a queste
etichette. Inoltre si vuole che il blocco sia "opaco", nel senso che si possa utilizzare la sua capacità di
elaborazione complessiva senza però vederne e poterne usare le singole parti (è un blocco, in fin dei conti!).
Dall'esterno non si può quindi saltare ad una particolare sua istruzione che sia diversa dal semplice inizio del
blocco. Queste due caratteristiche fanno sì che i processi esecutivi, attraversando un blocco, trovino
un'unica entrata (l'inizio del blocco), un'unica uscita (la fine del blocco) e nessuna "uscita laterale".
Questa, anche se di solito poco sottolineata, è la caratteristica principale della programmazione strutturata,
perché prima di parlare del controllo del flusso (canali che si dividono o uniscono) bisogna sottolineare le
caratteristiche di "impermeabilità" del tubo che porta questo flusso, che sono appunto una sola entrata, una
sola uscita e niente perdite laterali.
Dal punto di vista simbolico le parentesi del C sono il modo migliore di descrivere i blocchi:
{ ..... }
L'inizio di un blocco corrisponde al simbolo "{", la fine al simbolo "}", e i blocchi possono essere annidati uno
dentro l'altro come sono i livelli di parentesi in matematica.
Un blocco può essere trattato (e pensato) a tutti gli effetti come una singola istruzione, e quindi la
combinazione più semplice è la sequenza di blocchi:
pag 40
{ B1 } { B2 } ..... { BN }
Vediamo invece come si esprimono alternative e cicli usando i blocchi. Prima di iniziare bisogna far notare
che queste istruzioni condizionali si legano ad un particolare blocco, e quindi formano con esso un nuovo
blocco che si dovrebbe indicare con due parentesi. Questo però renderebbe la notazione pesante, per cui è
meglio pensare alle parole chiave, introdotte in sovrabbondanza rispetto al vero C (if, then, else, do, while,
until, loop, ecc.), come a collanti tra le varie parti. Nel seguito per chiarezza si userà distinguere tramite colori
blocchi di questo tipo, cioè non delimitati da parentesi. In alcuni casi le parentesi si rendono comunque
necessarie. Per l'alternativa l'espressione (quasi C) è:
if (A) then { B1 } else { B2 }
Qui si ha la valutazione di una condizione, A; se questa è vera si esegue il primo blocco altrimenti si esegue
il secondo. E' da notare che i termini "then" ed "else" non sono necessari per la costruzione, servono solo
per chiarezza linguistica.
Rispetto alle tipiche rappresentazioni grafiche dei diagrammi di flusso, qui adottiamo per le istruzioni
decisionali dei cerchi invece dei soliti rombi. Questi ultimi sono usati perché sono sottointese due possibili
risposte alternative, sì o no, corrispondenti a due vertici del rombo. Qui però pensiamo nel modo più
generale, con scelta non tra due sole possibilità, ma tra quante si vuole (multibranch), per cui usiamo dei
cerchi.
Nello sforzo di essere minimali si può notare che la prima parte delle costruzione è già sufficiente scrivendo,
in quasi C, senza "then" ma con un "do":
if (A) do { B }
Questa, che potremmo chiamare "esecuzione opzionale" perché si limita ad eseguire il blocco seguente, B,
se è vera la condizione A, può infatti esprimere l'alternativa completa nel seguente modo:
if (A) do { B } if (not A) do { B }
dove "not A" è la condizione opposta ad A, e B rappresenta due volte lo stesso blocco. A stretto rigore
bisogna però prima "congelare" la condizione iniziale:
{A1 = A} if (A1) do { B } if (not A1) do { B }
cioè osservare il valore della condizione A all'inizio, scrivendolo in una nuova variabile A 1 che non sia
modificata nel passaggio intermedio. La valutazione di condizioni come la if ( ... ) infatti hanno l'importante
caratteristica di non modificare nessuna variabile (osservano solo la situazione), cosa che però non è
garantita da un semplice blocco di istruzioni.
Riguardo ai cicli, il più facile da descrivere è quello che richiede di eseguire un certo numero N di volte un
blocco di istruzioni. Esprimendoci in questo caso quasi alla Pascal (con però un "do" in più) perché la
costruzione C è poco trasparente:
for i=1 to N do { ..... }
Questo tipo di ciclo però è facilmente esprimibile usando quello generale, che valuta una condizione per
l'esecuzione. Di questo tipo di ciclo ci sono due forme (la prima è scritta con un "do" in più rispetto al C):
while (A) do { B }
oppure:
do { B } while (A)
Nel secondo si esegue il blocco una prima volta, ed alla fine si valuta la condizione e, nel caso sia vera, si
ripete finché la condizione non diventi falsa. Nel primo invece la condizione viene valutata all'inizio, quindi il
blocco potrebbe anche non essere mai eseguito, se la condizione è subito falsa. Oltre a queste due forme si
trovano costruzioni equivalenti che usano la condizione in modo opposto, cioè se è vera non eseguono il
ciclo. Abbiamo così la costruzione del Pascal:
pag 41
repeat { ..... } until (A)
e le costruzioni Basic:
do until (A) { ..... } loop
do { ..... } loop until (A)
che è:
che è:
do while (not A) { ..... } loop
do { ..... } loop while (not A)
Queste comunque implicano solo il cambio di condizione, da "A" a "not A", nelle costruzioni precedenti e non
sono quindi nulla di nuovo.
L'importante è che i due tipi di cicli while sono equivalenti e possono esprimere il ciclo for. Per questo infatti
basta scrivere:
{i = 1} while (i = < N) do { ..... i = i + 1}
in cui si mette una istruzione di inizializzazione, i = 1, si aggiunge alla fine del blocco l'istruzione di
incremento, i = i +1, con i = < N come condizione di ripetizione.
Per gli altri due, "do ... while" può essere espresso con "while ... do" semplicemente ripetendo il blocco una
prima volta, prima del ciclo, cioè:
do { B } while (A) che è: { B } while (A) do { B }
A rigore invece il "while ... do", che può avere zero esecuzioni, non può essere espresso con il "do ... while"
che di esecuzioni ne fa almeno una. Se però si utilizza anche la "if ... do" questo è possibile, pur se contorto:
while (A) do { B } che è: (A) do { do { B } while (A) }
Riassumendo quindi il contenuto del teorema di Jacopini-Böhm si può dire che ogni programma di Turing
può essere espresso organizzandolo in blocchi uno contenuto nell'altro, oppure in sequenza uno di seguito
all'altro, con eventuali blocchi alternativi (if ... then ... else ...) o blocchi da ripetere in ciclo (while ... do ...).
Graficamente si hanno dei flussi che scendono dall'alto verso il basso, con i blocchi rappresentati da
rettangoli e le istruzioni di decisione da cerchi. I blocchi sono organizzati in "piani", e le frecce passano da un
piano a quello inferiore, suddividendosi all'uscita delle istruzioni di decisione: l'unica eccezione è che una
freccia può risalire di un piano per rieseguire un blocco, nel qual caso si ha una ripetizione ciclica. All'interno
di un blocco tutta questa struttura può ripetersi rispetto a dei sottoblocchi.
Si può dare una rappresentazione più simmetrica, anche se non di uso pratico, del teorema introducendo
due costruzioni di salto all'inizio dei blocchi seguente o precedente, nel seguente modo:
1) istruzione "ometti-se", che chiamiamo "skip-if":
2)
skip-if (A) { B } che è: if (not A) do { B }
che, se è vera A, salta all'inizio del blocco successivo.
2) istruzione "ripeti-se", che chiamiamo "repeat-if":
{ B } repeat-if (A) che è: do { B } while (A)
che, se è vera A, salta all'inizio del blocco precedente (B).
In questo caso si deve pensare ad una sequenza di blocchi:
... { B-1 } { B0 } { B+1 } ...
con le istruzioni skip-if o repeat-if davanti ad un blocco, in questo caso il blocco B0:
... { B-1 } istruzione-salto { B0 } { B+1 } ...
Si hanno i casi:
1) niente istruzione
>
sequenza:
viene eseguito B0;
pag 42
2) istruzione skip-if
2) istruzione skip-if
>
>
>
>
>
omissione:
viene eseguito B+1;
ripetizione:
viene eseguito B-1.
Si vede quindi che si ha una notevole simmetria, e il teorema di Jacopini-Böhm può essere riespresso
dicendo che un programma "if ... goto ..." può essere organizzato in blocchi contenuti gerarchicamente uno
nell'altro oppure messi in sequenza con possibilità di salti condizionali di ± 1 posizioni, cioè rispettivamente
con omissioni o ripetizioni condizionali.
In questa formulazione si vede che i salti "qualsiasi" permessi dalle "if ... goto ..." vengono regolamentati.
Intanto si ha una struttura sequenziale di istruzioni, come quella tipica dell'Assembler o della numerazione
presente nelle prime versioni di Fortran e Basic; queste istruzioni sono riunite in blocchi che sono contenuti
uno nell'altro o uno di seguito all'altro (in ogni caso non possono intersecarsi); le istruzioni di salto possono
saltare in modo molto limitato, cioè solo all'inizio del blocco successivo o precedente (non servono quindi
nemmeno etichette del target); quindi, visto che i blocchi non si intersecano, le "traiettorie" dei salti non si
possono incrociare, e quindi non si ha il groviglio tipico del "piatto di spaghetti".
Dal punto di vista "visuale" c'è qualcosa da dire sul significato delle tre costruzioni della "if", la "if ... goto ...",
la "if ... do ..." e la "if ... then ... else ...". Nella prima la "if" è semplicemente una istruzione condizionale e
l'informazione di salto è tutta contenuta nel "goto". Questa è l'istruzione dei programmi non strutturati, come
quelli di Turing. La seconda, da chiamare "esecuzione opzionale", presuppone un ordinamento sequenziale
dei blocchi, come quello di cui abbiamo appena parlato, perché fa esplicito riferimento al "blocco
successivo". La terza, nominata "alternativa" e usata in tutti i linguaggi superiori oltre che nella formulazione
originale del teorema di Jacopini-Böhm, fa riferimento all'immagine del "diagramma di flusso" o "diagramma
a blocchi", e l'abbiamo vista più sopra. In questi il flusso esecutivo si divide in due, senza alcuna idea di
salto, segue paritariamente le due alternative e si torna a riunire. Dal punto di vista logico non ha nemmeno
importanza che le alternative siano due, come si vede dalle istruzioni "multibranch" che si trovano in C
(switch), Pascal (case of) e anche Basic (select case). Per descrivere questa struttura si può dire che si ha
un ordinamento "discendente" (dall'inizio alla fine) con dei punti di diramazione del flusso che si riunisce tutto
in un punto "nuovo", cioè non già attraversato in precedenza. Il ciclo ha anche in questa rappresentazione lo
stesso significato di ritorno all'inizio del blocco appena eseguito. Questi diagrammi di flusso hanno in sé più
chiarezza della struttura sequenziale con omissioni e ripetizioni.
Riguardo ai goto che permangono in molti linguaggi, bisogna dire che esiste un loro uso particolare che non
contravviene alla strutturazione del programma, cioè non distrugge la chiarezza espositiva. Questo è il caso
in cui il goto è usato per uscire dai cicli annidati, allo stesso modo delle istruzioni tipo "break" ed "exit" per un
singolo ciclo. Questo uso residuale "buono" del goto può essere del tutto sostituito, come in Java, da un
"break etichettato" per uscire da cicli annidati a più livelli.
Per terminare, un accenno alle idee alla base della dimostrazione del teorema. Sostanzialmente si tratta di
replicare il codice invece che riutilizzarlo "saltando indietro". Inoltre si eliminano i salti "a valle" costringendo
l'alternativa che salta a seguire il flusso normale, senza però subire le computazioni di questo. Questa cosa
si può fare introducendo delle variabili booleane ausiliarie con valori diversi per il flusso normale e quello
"saltante", e testando prima di ogni esecuzione il loro valore, omettendo l'esecuzione nel caso del flusso
saltante. Si vede quindi che il programma strutturato sarà più lungo, ripetitivo e, potremmo dire, ridondante
dell'originale. Questo è il prezzo da pagare alla chiarezza e all'ordine, e non è troppo alto considerati i
vantaggi che porta con sé.
5.2


Uso delle TABELLE
si usa <table> che serve a gestire la tabella su più colonne, affiancare immagini e persino i rientri e i
margini.
tener presente che una tabella si devono definire cella per cella
pag 43



<tab> e </tab> servono per definire la tabella mentre le righe si gestiscon con <tr> (cioè table row). Le
celle si gestiscono con <td> associato al parametro width: es <td width=200> crea una cella larga 200
pixel
il bordo e il colore si possono gestire con <table border=1 bgcolor=green >
con <th> si gestisce l’intestazione di una cella
<html>
<head>
<title> prove della mia prima pagina html </title>
</head>
<SCRIPT language="VBscript">
sub prova()
msgbox("CIAO da Salvatore")
end sub
</SCRIPT>
<body bgcolor=black link=red vlink=yellow>
<br><br>
<img src="c:\masetta\images\striscia.gif">
<br>
<font size=4 color=red face=Arial,Helvetica>
Toti Masetta Home Page <font>
<a href="javascript:prova()">collegamento a una sub </a>
<p>
<font size=+2 color=#aaf884> Questa è la mia prima
Home <b> Page </b> </font>
<br>
<br>
<a href="/masetta/html/prova2.htm">punta ad una
seconda pagina </a>
<table border=1 bgcolor=green >
<tr>
<td width=40 align=left valign=top> PRIMA
<td width=200> dfd
<td width=200> <font size=1> PROva </font>
<tr>
<th width=200> <color=red> INTESTAZIONE
<td width=200> dsf
</table>
<br>
<img src="c:\masetta\images\striscia.gif" width=180 >
<p>
<a href="mailto:[email protected]"> contattatemi !!! </a>
</body>
</html>
pag 44
5.3
Bibbliografia
CODICE
TESTO
[ag]
Appunti universitari su automi e grammatiche
[ce]
Appunti di Comunicazioni elettriche dell’università
[corso]
Corso di Informatica 1
[da1]
appunti di documentazione automatica
[dev59]
rivista DEV n*59 di gennaio 99
[ei1]
fotocopie di Elettronica e Informatica Volume 1
[ei2]
fotocopie di Elettronica e Informatica Volume 2
[ei9]
fotocopie di Elettronica e Informatica Volume 9
[fon]
libro “Fondamenti di informatica” della bibblioteca comunale
[hal]
libro di TNA di Fred Halsal
[java1]
libro di java 1.1 della Hoepli
[linux01]
Mensile “Linux & Co” N° 1
[rose]
Libro di testo di Marshall Rose
[russo]
"Progettazione basi relazionali" di Nino Russo
[sis1]
appunti di Sistemi I
[sis2]
appunti di sistemi II
[tna]
appunti di TNA
[comp]
libro “COMPUTER”
pag 45
INDICE
A
algoritmo, definizione..............................................................................................................................................................................9
Algoritmo, definizione di ........................................................................................................................................................................9
B
blocco ....................................................................................................................................................................................................19
Bubble Sort ..........................................................................................................................................................................................14
bucket ....................................................................................................................................................................................................12
C
caricamento vettore.................................................................................................................................................................................7
caricamento,fattore di ............................................................................................................................................................................12
cicli ........................................................................................................................................................................................................24
ciclo .......................................................................................................................................................................................................19
complessità ............................................................................................................................................................................................10
D
Decimal .................................................................................................................................................................................................16
dell’array ..............................................................................................................................................................................................17
E
ereditarietà ...........................................................................................................................................................................................20
F
fattoriale ..................................................................................................................................................................................................3
Flow.........................................................................................................................................................................................................6
I
incapsulamento ....................................................................................................................................................................................20
INPUT ..................................................................................................................................................................................................23
Integer ..................................................................................................................................................................................................16
ISO 9000 ...............................................................................................................................................................................................24
L
Long ......................................................................................................................................................................................................16
M
macchina di Turing .............................................................................................................................................................................18
matrice .....................................................................................................................................................................................................7
memoria .................................................................................................................................................................................................11
multitasking ...........................................................................................................................................................................................10
O
operatori logici ......................................................................................................................................................................................26
P
package ..................................................................................................................................................................................................23
polimorfismo ........................................................................................................................................................................................20
polinomio ...............................................................................................................................................................................................27
potenza ....................................................................................................................................................................................................4
programmazione strutturata ..............................................................................................................................................................19
pag 46
R
ricerca binaria ........................................................................................................................................................................................12
Ricerca Binaria ......................................................................................................................................................................................15
ricerca lineare ........................................................................................................................................................................................12
S
Single ....................................................................................................................................................................................................16
Sottoprogramma ....................................................................................................................................................................................10
struttogramma ........................................................................................................................................................................................13
swap.......................................................................................................................................................................................................13
T
Tipi di dati .............................................................................................................................................................................................16
Turing .....................................................................................................................................................................................................3
V
vettori ....................................................................................................................................................................................................17
pag 47
Scarica