Libro di testo Libro di testo Cay Horstmann Concetti di informatica e fondamenti di Java (versioni 5 e 6) • I vari capitoli del libro sono organizzati in paragrafi che terminano con degli esercizi di “Autovalutazione” e sono accompagnati anche da “Note di cronaca”, “Consigli pratici, per la qualità, per la produttività”, “Errori comuni” ed “Argomenti avanzati”. Seguono poi un riepilogo e degli esercizi. • Alla fine ci sono delle Appendici A-I • Es. NdC 13.1 indica: Note di Cronaca cap.13 n.1 Libro di testo • Programma dei primi sei capitoli: • Capitolo 1: tutto • Capitolo 2: tutto tranne i paragrafi 2.11, 2.12, 2.13 e Argomenti avanzati 2.2 • Capitolo 3: tutto tranne i paragrafi 3.6, 3.9 e Consigli pratici 3.2 • Capitolo 4: tutto tranne Argomenti avanzati 4.6 e 4.7 • Capitolo 5: tutto tranne Argomenti avanzati 5.3 • Capitolo 6: tutto tranne i paragrafi 6.5, 6.6, 6.7 e Argomenti avanzati 6.4, 6.5 Traduzione in linguaggio macchina • Le istruzioni scritte in un linguaggio ad alto livello (Java, C++, Pascal, Fortran) non sono comprensibili alla macchina; occorre pertanto tradurle. • Si hanno due tipi di traduttori: • interpreti • compilatori Traduzione in linguaggio macchina Interprete • L’interprete traduce ogni istruzione del programma sorgente e la esegue (Basic, Perl). • Gli interpreti sono programmi semplici. • Un programma scritto in un linguaggio con interprete ha una gestione poco efficiente perché ogni volta che si esegue il programma, le istruzioni devono essere tradotte nuovamente (esempio: errori di scrittura, esecuzione su nuovi dati). 1 Compilatore • Sia X un linguaggio. Un compilatore per X è un programma che riceve in ingresso un programma scritto in X, lo traduce in una sequenza di istruzioni scritte in linguaggio macchina (codice oggetto) ed esegue dei controlli: • analisi lessicale, sintattica, semantica. Compilatore • Analisi lessicale: • si verifica che le parole del programma siano scritte correttamente secondo le regole del linguaggio. • Analisi sintattica: • si verifica che le frasi del linguaggio siano scritte correttamente (sequenza di simboli generata con le regole della grammatica). • Analisi semantica: • si verifica che la frase abbia significato (coerenza). Compilatore e linker • Se la compilazione ha avuto buon esito, un altro programma (linker) prende il codice oggetto, esegue gli agganci necessari (con moduli di libreria e moduli oggetto scritti dall’utente) e produce un “codice eseguibile”. • Il codice eseguibile (comprensibile alla macchina) viene elaborato (programma in esecuzione). Portabilità ed efficienza • In alcuni linguaggi compilazione e link sono in un solo passo. Portabilità ed efficienza • I linguaggi interpretati sono portabili a livello di codice sorgente: possono essere eseguiti su macchine con diverse CPU. Sono poco efficienti. • I linguaggi compilati sono portabili a livello di codice sorgente, ma non a livello di codice macchina, che dipende dalla CPU. Sono efficienti perché permettono di correggere gli errori prima dell’esecuzione e la traduzione è fatta una sola volta per prove diverse. Il compilatore Java e la JVM 2 Il compilatore Java e la JVM La Java Virtual Machine • Il compilatore Java esegue una analisi lessicale, sintattica e semantica, evidenziando gli eventuali errori, e traduce il codice sorgente in un codice binario intermedio, detto bytecode, senza eseguire l’attribuzione delle allocazioni in memoria, ma solo predisponendole. • La JVM (Java Virtual Machine) è un interprete che elabora il bytecode e lo esegue. bytecode CPU virtuale (JVM) Interprete Java codice macchina CPU reale Il compilatore Java e la JVM • Si ha perciò una portabilità sia a livello di codice sorgente che a livello di bytecode, che può essere eseguito da interpreti diversi su diverse CPU. Le fasi della programmazione • Si ha l’efficienza dovuta alla compilazione, anche se parte del processo di traduzione viene svolto ad ogni esecuzione dall’interprete. Le fasi della programmazione codice sorgente librerie compilatore (caricatore) codice sorgente compilatore codice sorgente librerie file di bytecode librerie interprete file eseguibile interprete programma in esecuzione programma in esecuzione programma in esecuzione linguaggio Java linguaggi compilati linguaggi interpretati Le fasi della programmazione • L’attività di programmazione si esegue in tre fasi • scrittura del programma (codice sorgente) • compilazione del codice sorgente e creazione del codice eseguibile (codice macchina) • esecuzione del programma (Fig.14 par. 1.8) 3 Le fasi della programmazione Le fasi della programmazione • Per scrivere il codice sorgente si usa un editor di testo, memorizzando il codice in un file che deve essere di tipo java, ad esempio: • Per compilare, richiamiamo il compilatore Java scrivendo il comando Primo.java • Solitamente si hanno a disposizione più editor richiamabili o tramite icona, o con comandi espressamente scritti, ad esempio: gedit Primo.java javac Primo.java • Se la compilazione non ha dato errori, l’uscita del compilatore è un file di nome Primo.class • Per mandare in esecuzione si scrive il comando java Primo Le fasi della programmazione • Cosa fare in caso di errori. • Se ci sono errori in compilazione, questi devono essere corretti altrimenti non si può passare alla fase successiva di esecuzione. Il compilatore evidenzia l’errore (o quasi) scrivendo la riga in cui esso si trova e il tipo di errore. • Corretti gli errori si procede ad eseguire un collaudo sul programma per testarne il buon funzionamento. Il linguaggio Java • Nato nel 1991 in Sun Microsystems, da un gruppo di progettisti guidato da Gosling e Naughton. (par. 1.4) • Progettato per essere semplice e indipendente dalla CPU (come anche si dice, dall’hardware, dalla piattaforma o dall’architettura) ed ebbe grande successo per scrivere programmi per Internet (applet). Il linguaggio Java Il linguaggio Java • Il linguaggio Java ha avuto molto successo: • ha un compilatore molto preciso, il programma è portabile a livello di bytecode • ha una ricchissima libreria che mette a disposizione dei programmatori un insieme vastissimo di funzionalità, indipendenti anche dal sistema operativo (sono già compilate) 4 Il linguaggio Java Il linguaggio Java: Alfabeto • Dato che Java non è stato progettato per la didattica • non è molto semplice scrivere programmi Java molto semplici • Anche per scrivere programmi molto semplici è necessario conoscere parecchi dettagli “tecnici”, un potenziale problema nelle prime lezioni. • L’alfabeto è l’insieme di simboli che il linguaggio permette di usare e che sono chiamati caratteri. • Java utilizza UNICODE. • Per uniformità con gli altri linguaggi e per una tradizione dei linguaggi di programmazione, i simboli per costruire nomi, espressioni, operatori sono solo quelli dell’ASCII standard. Token Parole chiave • Ogni linguaggio ha le sue regole per costruire le unità lessicali (parole del linguaggio) dette anche token. • Le unità lessicali sono: • • • • • identificatori (nomi di variabili, classi, metodi) parole chiave costanti separatori (spazi, caratteri di interpunzione) operatori • Le parole chiave sono parole che hanno un preciso significato e sono riservate: non devono essere usate come nomi di identificatori. (Appendice F) • Ad esempio: • int, byte, break, … due nomi di tipo di dato intero, una istruzione di interruzione. Separatori e operatori Regole per costruire i nomi • Spazio, parentesi, segni di interpunzione, simboli di relazione, barre diritte e rovesciate, simboli aritmetici,… • Si utilizzano caratteri alfabetici e numerici, _,$ • Non si devono inserire spazi. • Ogni nome di identificatore inizia con una lettera alfabetica. • Si distinguono le lettere maiuscole dalle minuscole; i seguenti nomi sono identificatori diversi : ciao CIAO, main Main, class Class. ( ) [ ] { }; , . : ! ? = > < == <= >= != & && | || + - * / \ ++ -- ' “ • Lo spazio è un separatore. 5 Regole per costruire i nomi • L’uso nella costruzione dei nomi è: • maiuscole per le classi • minuscole per variabili, metodi, oggetti Primo programma • Quando si utilizzano nomi di classi, oggetti e metodi della “libreria”, l’uso diventa una regola. Primo programma • Tradizionalmente, il primo programma che si scrive quando si impara un linguaggio di programmazione ha il compito di visualizzare sullo schermo un semplice saluto. (par. 1.6) • Nella prima esercitazione di laboratorio si dovrà costruire il programma, compilarlo ed eseguirlo. • Utilizzando un editor di testi, costruiamo un file di nome Ciao.java Primo programma public class Ciao{//inizio classe public static void main(String[] arg){ System.out.println("benvenuto"); }//fine main }//fine classe • Occorre fare molta attenzione • il testo va inserito esattamente come è presentato • il file deve chiamarsi Ciao.java Primo programma • compiliamo il programma javac Ciao.java • il compilatore genera il file Ciao.class • eseguiamo il programma Analisi del primo programma java Ciao • otterremo la visualizzazione del messaggio benvenuto 6 Analisi del primo programma public class Ciao{//inizio classe public static void main(String[] arg){ Analisi del primo programma • La prima riga è: public class Ciao System.out.println("benvenuto"); }//fine main }//fine classe • Questo primo programma invia all’uscita standard una stringa. • Esaminiamo le varie righe del programma. Analisi del primo programma public • È una parola chiave e significa che la classe Ciao è utilizzabile da tutti • vedremo più avanti altre modalità di accesso: private, protected Ciao • È il nome della classe. • Ogni file sorgente Java può contenere una sola classe pubblica il cui nome deve coincidere con il nome del file che la contiene. • Questa riga inizia una nuova classe, descritta dalle istruzioni contenute nella coppia di parentesi graffe. Un programma Java è costituito da una o più classi (blocchi, unità fondamentali). Analisi del primo programma { //inizio classe . . . . } • Commenti. • I commenti sono istruzioni che non vengono eseguite, ma servono ad introdurre frasi di spiegazione. • // il commento occupa una sola riga • /* commento scritto su più righe */ Analisi del primo programma Analisi del primo programma public static void main (String arg[]) • Le parole public, static, void, main sono parole chiave. • main è il nome di un metodo. • Ogni programma Java deve avere un metodo main. • La JVM manda in esecuzione il programma attivando il main. • Un metodo è un insieme di istruzioni e rappresenta un algoritmo (funzioni, procedure): • • • • ha un nome main ha un tipo di ritorno void ha un tipo di accesso public possiede zero, uno o più argomenti ( ) String arg[] • se non agisce su oggetti è static • Il main è static perché è il primo metodo attivato. 7 Analisi del primo programma Analisi del primo programma String • È il nome di una classe che rappresenta le stringhe, gruppi di caratteri. • I metodi devono essere inseriti in classi. • All’interno del metodo main c’è una sola istruzione (corpo del metodo è contenuto tra graffe): System.out.println("benvenuto"); arg[] • È il nome di un array: è un contenitore per più elementi; questi possono essere inseriti sulla riga di comando al momento dell’esecuzione. • Questa istruzione manda all’uscita standard (finestra di terminale) una stringa: frase racchiusa tra virgolette. • Ogni istruzione termina con un ; Analisi del primo programma Analisi del primo programma • Ogni linguaggio gestisce una uscita standard (vedremo poi altri tipi di uscite). • In Java l’uscita standard è rappresentata da un oggetto di nome out che appartiene alla classe System. • Per utilizzare l’oggetto out della classe System dobbiamo scrivere System.out • Un oggetto viene utilizzato attraverso i suoi metodi. (uso del punto: dot notation) Analisi del primo programma • Per invocare (chiamare) il metodo scriviamo: System.out.println("benvenuto"); • Il metodo println (print) ha come argomenti stringhe e numeri: • System.out.println("ciao"); • System.out.println(25); • System.out.println(); • Vogliamo eseguire una visualizzazione (stampa) della stringa e usiamo il metodo println che invia il suo argomento (la stringa) all’uscita standard. • println invia e va a capo riga. • print invia senza andare a capo riga. Analisi del primo programma • Ogni metodo ha le parentesi rotonde, che si devono scrivere anche se non ci sono argomenti. • Invocare un metodo (non statico): oggetto.nomemetodo(parametri); • • • • oggetto System.out nome metodo println parentesi tonde ( ) zero, uno o più parametri (separati da virgola) 8 Analisi del primo programma • Abbiamo visto tre tipi di parentesi: • graffe {} individuano dei gruppi di istruzioni o blocchi (istruzioni della classe Ciao e del metodo main) • tonde () individuano un metodo (parametri del metodo main e del metodo println) • quadre [] individuano un array (dimensione dell’array arg) Errori di programmazione Attenzione a non confonderle. Errori di programmazione Errori compilazione • Quando scriviamo un programma possiamo introdurre errori. • Gli errori che possiamo fare sono di due tipi: • Errori che il compilatore evidenzia, errori di sintassi (lessicali, sintattici e semantici). • Consideriamo il seguente codice: • Errori che si verificano quando il programma esegue le istruzioni: errori logici ed errori durante l’esecuzione. • Il compilatore segnala: Errori compilazione • Consideriamo il seguente codice: public class Errore{ public static void main(String[] arg){ System.Out.println(""Ciao""); } } • Il compilatore segnala: cannot find symbol • Il simbolo Out non viene riconosciuto: appare come variabile non definita. public Class Errore{ public static void main(String[] arg){ System.out.println(""Ciao""); } } class or interface expected • Non viene riconosciuta la classe perché la parola class è scritta con la maiuscola. Errori compilazione • Consideriamo il seguente codice, memorizzato in file di nome Errore1.java: public class Errore{ public static void main(String[] arg){ System.out.println(""Ciao""); } } • Il compilatore segnala: class Errore is public, should be declared in a file named Errore.java 9 Errori logici • Consideriamo il seguente codice public class Errore{ public static void main(String[] arg){ System.out.println(""Cia0""); } // 0 al posto di o } • Questo errore non viene segnalato dal compilatore: • il programma viene eseguito ma il risultato è errato perché viene prodotto un output diverso dal previsto. Errori logici • Gli errori logici sono molto più pericolosi degli errori di sintassi • il programma viene compilato correttamente, ma non fa quello che dovrebbe fare. • L’eliminazione degli errori logici richiede molta attenzione: • si esegue il programma su vari casi e si osservano con attenzione i risultati prodotti; i casi di prova devono essere “certi” ossia si deve conoscere il risultato. Un po' di storia • Il primo strumento di calcolo utilizzato nell’antichità da Greci e Romani fu l’abaco. Un po' di storia Un po' di storia • Le prime macchine calcolatrici (eseguivano calcoli in maniera automatica) vennero costruite per risolvere problemi specifici. • Queste macchine erano di tipo meccanico e si basavano su un funzionamento simile a quello degli orologi, con ruote dentate ed ingranaggi. Un po' di storia • La macchina fu trovata nel 1900 vicino all’isola di Anticitera, a nord-est di Creta. È databile intorno al 100-150 a.C. • Era un planetario in metallo mosso da ruote dentate per calcolare il calendario solare e lunare; vi sono raffigurati i cinque pianeti allora conosciuti. • Cicerone afferma che macchine di questo tipo erano state costruite da Archimede. La macchina di Anticitera (Museo Naz. Archeol., Atene) 10 Un po' di storia Un po' di storia • Il funzionamento è descritto in un lettera di Schickard a Keplero. • John Napier (Neper, 1550-1617, scozzese), il matematico che inventò i logaritmi, costruì una macchina (detta “gli ossicini di Nepero”) che trasformava le moltiplicazioni e le divisioni in somme e sottrazioni. • Wilhelm Schickard (1592-1635, matematico ed astronomo tedesco) costruì una macchina da calcolo che eseguiva le quattro operazioni. Macchina calcolatrice di Schickard (1623) • La macchina poteva sommare e sottrarre numeri a sei cifre, e suonava una campanella quando veniva superata la sua capacità. Un po' di storia Un po' di storia • Blaise Pascal (1623-1662, francese) costruì una macchina (a soli 19 anni) che eseguiva solo somme e sottrazioni e che utilizzava le dieci cifre decimali. (Linguaggio Pascal di Niklaus Wirth 1970) (NdC 9.2).. • Gottfried Wilhelm Leibnitz (1646-1716, tedesco) costruì una macchina che eseguiva anche le moltiplicazioni e le divisioni. Pascalina Conservatorio Nazionale di e Arti e Mestieri (Parigi) Leibnitz ideò il sistema numerico binario Calcolatrice di Leibniz (1673) Museo di Berlino Un po' di storia • Charles Babbage (1791-1871, inglese), ispirandosi ai telai di Joseph-Marie Jacquard (1752-1834, francese) (schede perforate che riproducevano disegni ripetuti ciclicamente), ideò una macchina, detta “alle differenze” (NdC 13.1), con la quale si potevano costruire tavole numeriche e una, detta “analitica”, alla quale si dovevano fornire schede perforate contenenti sia i dati da elaborare che le regole (istruzioni) di calcolo da eseguire. 11 Un po' di storia • Georg Scheutz (1785-1873, svedese) si occupava di tecniche tipografiche e realizzò la parte stampante della macchina da calcolo; costruì una macchina simile a quella di Babbage, che fu acquistata dall’osservatorio astronomico di Albany (stato di New York) per costruire delle tavole con cui calcolare la posizione degli astri. Un po' di storia • Per programma (algoritmo) si intende una sequenza di istruzioni da far eseguire ad una macchina. • Chi fu il primo programmatore? Un po' di storia Un po' di storia • Ada Augusta Byron contessa di Lovelace (1815-1852, inglese), figlia del poeta Lord Byron. Ada aveva avuto una educazione matematica e collaborava con matematici dell’epoca come De Morgan. Ella scrisse una sequenza di istruzioni (programma) per la macchina di Babbage, con le quali si calcolavano numeri di Bernoulli. • La macchina da scrivere. • Il comptometro, dello statunitense D. E. Felt: prima calcolatrice da tavolo provvista di tasti per introdurre i dati. • La macchina a schede perforate, dello statunitense Hermann Hollerit: Linguaggio Ada (1979): concorrente (NdC 9.2). programmazione • usava l’elettricità, venne utilizzata in Canada nel 1890 per il censimento. • Hollerit nel 1896 fondò la Tabulating Machine Company che nel 1924 divenne la IBM (International Business Machines). Il primo calcolatore Il primo calcolatore • Konrad Zuse, tedesco, costruì nel 1938 una calcolatrice meccanica, chiamata Z1. • Successivamente nel 1941 costruì lo Z3, un calcolatore elettromeccanico considerato il primo calcolatore. • Zuse utilizzò il relè che poteva avere solo due posizioni, pertanto abbandonò il sistema decimale (ruote dentate potevano essere numerate) e passò al sistema binario. 12 Il primo calcolatore • Nel 1944 Howard Aiken, dell’università di Harvard, costruì (indipendentemente) il suo primo calcolatore elettromeccanico Mark I. • Mark I usava il sistema decimale e le schede perforate: aveva l'aspetto di un armadio, lungo 16 metri. Eseguiva 3 addizioni in un secondo, impiegava 6 secondi per una moltiplicazione e 12 per una divisione. • Modelli successivi: Mark II e Mark IV. Calcolatori moderni • A differenza delle prime macchine, i calcolatori moderni sono macchine universali, perché realizzano applicazioni diverse. • Con l’evoluzione della tecnologia i calcolatori sono così cambiati che si distinguono generazioni successive di macchine. L’ENIAC Calcolatori moderni I generazione (dalla metà degli anni '40 alla metà degli anni '50) • È l’epoca delle valvole termoioniche. • ENIAC (Electronic Numerical Integrator And Computer, integratore numerico e calcolatore elettronico), progettato presso l'università della Pennsylvania (1945-47) • Occupava una grande stanza ed era costituito da molti armadi con circa 18000 valvole, parecchie delle quali si bruciavano ogni giorno e dovevano essere sostituite; veniva programmato collegando cavi su appositi pannelli, simili a quelli dei centralini telefonici, ogni specifico problema richiedeva un diverso collegamento dei cavi; eseguiva 5000 addizioni al secondo (foto NdC 1.1). I generazione (dalla metà degli anni '40 alla metà degli anni '50) • L’ungherese J. Von Neumann progettò nel 1946 la macchina EDVAC e poi lo IAS. • IAS: le componenti erano organizzate secondo una architettura presente ancora oggi sui calcolatori: architettura di von Neumann. 13 II generazione (dalla metà degli anni '50 alla prima metà degli anni '60) III generazione (fino ai primi anni '70) • In questo periodo si ha lo sviluppo del software: si costruiscono i primi linguaggi ad alto livello e i primi sistemi operativi. • È l’epoca dei circuiti integrati: una componente unica contiene centinaia di transistor. • Si passa ai circuiti integrati su larga scala: LSI (Large Scale Integration) con migliaia di transistor. • Nel 1969 M. E. Hoff, ingegnere della Intel, riuscì a ridurre le dimensioni della CPU fino a qualche centimetro, ottenendo i cosiddetti chip (microprocessori) (NdC 8.1). IV generazione (attuale) V generazione (attuale) • È l’epoca dei transistor, molto più piccoli e meno costosi delle valvole termoioniche. • È l’epoca dei circuiti integrati su scala molto larga (VLSI: Very Large Scale Integration) e dei circuiti integrati su scala ultra-larga (ULSI: Ultra Large Scale Integration) contenenti milioni di transistor. • Tecnologia WSI (Wafer Scale Integration) con dispositivi ancora più piccoli e con decine di milioni di dispositivi su un unico chip (wafer perché sono costituiti da più strati). • Il chip del Pentium 4 contiene 50 milioni di transistor. • Il calcolatore più veloce al mondo è “attualmente” il Blue Gene dell’IBM ed esegue circa 4.78× ×1014 operazioni al secondo. • Calcolatori con architetture parallele. I Chip • I chip (pezzetto) sono sottili lamine di silicio (dopo l’ossigeno è l’elemento più diffuso) che contengono milioni di interruttori che realizzano i due stati acceso/spento e che sono collegati da tracce di alluminio come dei sottilissimi fili dello spessore di mezzo micron (1µ = 10-9 m., un capello è dello spessore di 100 µ). • I chip sono piccoli, costano poco, sono molto potenti e facilmente costruibili. • Case costruttrici: Intel, Motorola, Apple, Digital, Sun, ... Lo sviluppo teorico 14 Lo sviluppo teorico La teoria della calcolabilità • Con l’evoluzione tecnologica delle macchine da calcolo si ha parallelamente un aumento dei problemi che si vogliono far risolvere ai calcolatori: di molti problemi si cerca la soluzione per via numerica (integrali, sistemi, equazioni differenziali, ...). • Qualsiasi problema può essere risolto tramite un programma? • Il matematico e logico Alan Turing (19121954, inglese), inventore della teoria della calcolabilità, definì il concetto di funzione calcolabile e ideò un modello astratto di calcolatore (Macchina di Turing) nel quale la computazione è rappresentata da una sequenza di trasformazioni di stato (NdC 12.1). • Egli scoprì che esistono problemi indecidibili: problemi per i quali non esiste un algoritmo in grado di risolverli. Computer e Programma • Un computer è una macchina che Computer e Programma • memorizza dati (numeri, parole, immagini, suoni...) • interagisce con dispositivi (schermo, tastiera, mouse...) • esegue programmi • Un programma è una sequenza di istruzioni che il computer esegue e di decisioni che il computer prende per svolgere una certa attività. Programmi e istruzioni Cos’è la programmazione? • Nonostante i programmi siano molto sofisticati e svolgano funzioni molto complesse, le istruzioni di cui sono composti sono molto elementari, ad esempio • Un programma descrive al computer, in dettaglio, la sequenza dei passi necessari per svolgere un particolare compito. • L’attività di progettare e realizzare un programma è detta programmazione. • Usare un computer non richiede alcuna attività di programmazione, invece un informatico professionista solitamente svolge una intensa attività di programmazione. • • • • estrarre un numero da una posizione della memoria sommare due numeri inviare la lettera A alla stampante se un dato è negativo, proseguire l’esecuzione del programma da una certa istruzione anziché dalla successiva (decisione) 15 Problemi Problemi da risolvere • Quale tipo di problemi è possibile risolvere con un computer? • Dato un insieme di fotografie di paesaggi, qual è il paesaggio più rilassante? • Avendo depositato ventimila euro in un conto bancario che produce il 2% di interessi all’anno, capitalizzati annualmente, quanti anni occorrono affinché il saldo del conto arrivi al doppio della cifra iniziale? • Il primo problema non può essere risolto dal computer. Perché? Problemi • Il primo problema non può essere risolto dal computer perché non esiste una definizione di paesaggio rilassante che possa essere usata per confrontare in modo univoco due paesaggi diversi. • Un computer può risolvere soltanto problemi che potrebbero essere risolti anche manualmente: è solo molto più veloce, non si annoia, non fa errori. Problemi • I problemi da risolvere sono di varia natura: • mettere in ordine alfabetico dei nomi di persone; • trovare gli zeri di una funzione f(x); • contare quante volte una parola compare all'interno di un testo; • gestire acquisti e prestiti dei libri di una biblioteca. • Il secondo problema è certamente risolvibile manualmente, facendo un po’ di calcoli... Problemi e algoritmi • Indipendentemente da chi sarà l’esecutore (l’uomo o la macchina) la prima cosa da fare è: trovare l'algoritmo risolutivo, ossia la sequenza di operazioni che permette di passare dai dati ai risultati. Algoritmi 16 Algoritmi • Un algoritmo (Al Kowarizmi, matematico persiano del IX secolo d. C.) è un procedimento di soluzione che gode delle seguenti proprietà: 1. è un insieme di operazioni eseguibili; 2. esiste una prima operazione; 3. dopo ogni operazione è individuata la successiva; 4. esiste un’ultima operazione. Algoritmi: operazione eseguibile • non è ambigua, è univocamente interpretabile da un esecutore (uomo o macchina): • le operazioni tra numeri hanno un simbolo non ambiguo: • 2*3 (prodotto) • 2p3 è ambigua 2+3 (somma) p: per o più • è effettiva, l’esecutore deve poterla svolgere in un tempo finito: • somma di due numeri interi: sì • calcolo di un limite: no (concetto di infinito) Algoritmi Algoritmi • Le proprietà 2. e 4. rappresentano la finitezza: un algoritmo deve terminare in un tempo finito. • La proprietà 3. dice che l’algoritmo è deterministico: ad ogni passo è univocamente individuato il passo successivo. • Si possono ideare algoritmi non deterministici: • ad ogni passo c’è più di una scelta e l’algoritmo individua tra le scelte quella che porterà alla soluzione. • simulazione: si può pensare ad una esecuzione contemporanea delle scelte. Algoritmi Algoritmi • Problema. Avendo depositato ventimila euro in un conto bancario che produce il 2% di interessi all’anno, capitalizzati annualmente, quanti anni occorrono affinché il saldo del conto arrivi al doppio della cifra iniziale? • Algoritmo. 1. L’anno attuale è 0; il saldo attuale è 20000 euro 2. Ripetere i passi 3. e 4. finché il saldo attuale è minore di 40000, quindi passare al passo 5 3. Aggiungere 1 al valore dell'anno 4. Il nuovo saldo attuale è il valore del saldo attuale moltiplicato per 1.02 (1 + 2/100) 5. Il risultato è il valore dell’anno attuale 17 Algoritmi Algoritmi • Ogni operazione è eseguibile: somme e prodotti (effettive e non ambigue) • I valori delle variabili (anno, saldo, interesse) sono effettivi: • Se vogliamo fare eseguire l’algoritmo da un calcolatore, dobbiamo trasformare la sequenza di istruzioni in una sequenza scritta in un linguaggio comprensibile alla macchina: programma, codice. • I primi programmi erano scritti in linguaggio macchina (o codice binario, ossia una sequenza di simboli 0 e 1). • se l’interesse dipendesse da parametri noti solo nel futuro non calcolabili a priori non potremmo eseguire l'aggiornamento del saldo • È finito perché il 2% di 20000 euro sono 400 euro e quindi al più dopo cinquanta anni si raggiunge il saldo desiderato: il ciclo termina. Algoritmo → Programma Algoritmo →Programma • Nel corso degli anni si sono sviluppati vari linguaggi; si è passati da quelli cosiddetti a • basso livello, perché più vicini alla macchina, come Assembler • a quelli detti ad • alto livello, perché più vicini all’uomo come Pascal, C, Java. Algoritmo → Programma Analisi • La risoluzione avviene in due passi: • analisi: problema → algoritmo • codifica: algoritmo → programma (codice) • Per far eseguire il programma al calcolatore occorre imparare ad usare quei programmi che ne gestiscono le risorse (implementazione): • editor di testo, compilatore, esecutore, funzioni del sistema operativo; test di funzionamento. • Occorre inventare il procedimento di soluzione che sarà indipendente (o quasi) dal linguaggio di programmazione; si distinguono due fasi: • definizione del problema: individuare la struttura dati più idonea, risolvere un problema generale; • progettazione del programma: individuare le istruzioni e l'ordine di esecuzione; si procede dal problema generale e si scende nei particolari; si può utilizzare un linguaggio di pseudocodifica. 18 Codifica Programma • Occorre trasformare le istruzioni in frasi comprensibili al calcolatore; si deve imparare un linguaggio di programmazione. • Un buon programma deve essere facilmente utilizzato da altri, deve durare nel tempo, deve essere facilmente aggiornabile. • Noi studieremo il linguaggio Java. • Come sistema operativo utilizzeremo Linux. • Verifica: • Casi di prova e test di funzionamento • Gestione di casi eccezionali • Controllo della terminazione • Documentazione: • Inserire delle frasi di commento che spieghino “cosa fa” il programma Risorse di un calcolatore • Risorse hardware: Risorse di un calcolatore • componenti fisiche • Risorse software: • programmi di gestione e applicativi • Risorse firmware: • componenti hardware preprogrammate per specifiche funzioni Risorse hardware Risorse hardware • Unità centrale: l’unità di elaborazione centrale (CPU) e la memoria centrale o primaria • Memorie secondarie o ausiliarie o di massa • Dispositivi di ingresso/uscita • Connessioni e supporti: linee di collegamento (bus), scheda madre. (par. 1.2) 19 L'architettura di von Neumann • Ideata da Janos' (John) von Neumann: •L’elaboratore è dotato di una memoria nella quale vengono registrati sia i dati che i programmi da eseguire. •Il calcolatore diventa una “macchina universale”, perché riceve in ingresso (input) i dati e il programma e restituisce in uscita (output) i risultati (l’elaborazione dei dati eseguita dal programma). L'architettura di von Neumann CA CC C I M O R CA + CC M I O R = unità centrale di elaborazione = memoria centrale = dispositivi di ingresso = dispositivi di uscita = dispositivi di memoria ausiliaria L'architettura di von Neumann • La grande maggioranza degli elaboratori odierni ha una architettura che può essere (più o meno facilmente) ricondotta al modello di von Neumann • le eccezioni più importanti sono alcune macchine ad elaborazione parallela • Il modello è importante perché semplificò l’architettura dell’hardware rendendolo omogeneo. (Fig. 5 par. 1.2) La Memoria primaria La memoria primaria si compone di due parti: • ROM (Read Only Memory) • RAM (Random Access Memory) La Memoria primaria La memoria ROM • È una memoria a sola lettura: • costituita da celle nelle quali non è possibile immagazzinare nuovi dati, ma dalle quali si possono estrarre informazioni (lettura). • È ad accesso diretto (casuale) • Essa mantiene il suo contenuto a macchina spenta e contiene i programmi eseguiti all'accensione del sistema: • test di funzionamento, caricamento del software di base, BIOS (Basic Input/Output System) • Tali informazioni (firmware) vengono inserite dalla ditta costruttrice. 20 La memoria RAM La memoria • È il dispositivo nel quale si possono immettere, conservare, estrarre le informazioni da elaborare: il programma e i dati • Il byte è l'unità di misura della capacità della memoria • chip di memoria realizzati con la stessa tecnologia (al silicio) utilizzata per la CPU. • L'informazione elementare (unità minima di informazione) si chiama bit (binary digit) o cifra binaria. – Rappresenta i due stati di un circuito “aperto” e “chiuso”, convenzionalmente detti 0 e 1, o anche “falso” e “vero”. • 1Kbyte (kilobyte): • 1Mbyte (megabyte): • 1Gbyte (gigabyte): • 1Tbyte (terabyte): 210 byte = 1024 byte ~ 103 byte 220 byte = 1 048 576 byte ~ 106 byte 230 byte =1 073 741 824 byte ~ 109 byte 40 2 byte ~ 1012 byte (mille miliardi) • RAM vari Giga La memoria RAM La memoria RAM • La memoria è organizzata a gruppi di bit (solitamente 8) chiamati byte. • La memoria è costituita da un numero finito m di celle, o locazioni di memoria, tutte della stessa dimensione. • Ogni cella è individuata da un indirizzo. • L’indirizzo di memoria è un numero naturale progressivo (0, ..., m-1) che individua ogni cella in maniera univoca all’interno della memoria stessa. • Lo spazio di indirizzamento è l'insieme di tutti gli indirizzi possibili delle celle di memoria: • Ogni cella è composta da un numero n predefinito di bit • n = 8, 16, 32, 64 quindi 1, 2, 4, 8 byte. • Il contenuto di una cella di memoria si chiama parola (word): è un numero binario fisicamente memorizzato all'interno di essa. La memoria RAM • Lo spazio di memoria è la quantità di memoria disponibile. Dipende dal numero di indirizzi e dalla grandezza delle celle. • Accedere ad un dato significa selezionare mediante l'indirizzo la cella in cui è memorizzato e prelevarne il valore (lettura). • Memorizzare un dato significa selezionare mediante l'indirizzo la cella in cui si intende introdurlo ed introdurre il dato (scrittura). • dipende dal numero di bit destinati a rappresentare un indirizzo; • ogni bit può assumere due valori, quindi con k bit si possono rappresentare 2k indirizzi diversi, k=16, 32, 64. La memoria RAM • È una memoria ad accesso diretto (casuale) • il tempo di accesso è lo stesso per ogni indirizzo • È costituita da celle nelle quali è possibile estrarre e registrare nuove informazioni (lettura e scrittura). • È una memoria volatile • perde il suo contenuto quando la macchina viene spenta. • Nella RAM si scrivono le compongono i programmi e i dati. • La RAM è veloce e costosa. istruzioni che 21 Altre memorie • Memoria cache • area di memoria ad accesso veloce, localizzata tra CPU e RAM, utilizzata per contenere dati e istruzioni di prossimo utilizzo da parte del processore (frigorifero). • Il caricamento dei dati è sequenziale • Memoria buffer • area di memoria temporanea utilizzata nei collegamenti tra dispositivi di diversa velocità (tra CPU e tastiera, video, stampante): • salvare gli ultimi comandi usati • operazioni di copia/incolla L'unità centrale di elaborazione CPU • La CPU (Central Processing Unit) esegue le istruzioni del programma, legge e scrive le informazioni nella memoria centrale, richiede servizi alle apparecchiature periferiche, è costituta da uno o più chip. (Fig.1 par. 1.2) • È composta da: • ALU: Arithmetic Logic Unit • Unità di controllo • registri ALU: unità aritmetico-logica Unità di controllo • La ALU (Arithmetic Logic Unit) è la parte operativa: esegue le operazioni • Accede solo alle informazioni contenute nella memoria centrale ed ha il compito di gestire la successione delle operazioni da svolgere: • aritmetiche • addizione, sottrazione, moltiplicazione e divisione • logiche • confronti di valori • utilizza i valori depositati in appositi registri (celle di memoria ad accesso veloce per la memorizzazione temporanea dei dati). Principali registri della CPU • Contatore di programma (PC: Program Counter) • contiene l’indirizzo di memoria della successiva istruzione da caricare ed eseguire • Registro istruzioni (IR: Instruction Register) • contiene l’istruzione che deve essere interpretata ed eseguita • Registro dati • Contiene dati, risultati intermedi • reperire dalla memoria le istruzioni (caricamento) • interpretarle (decodifica) • farle eseguire: caricare gli operandi, eseguire l'istruzione, memorizzare il risultato (esecuzione). Funzionamento della CPU • Il funzionamento è ciclico: • Ciclo fetch-decode-execute • fetch: caricamento dalla memoria dell’istruzione da eseguire e sua memorizzazione nel registro istruzioni; incremento di una unità del contatore di programma (istruzioni eseguite in sequenza) • decode: decodifica dell'istruzione da eseguire • execute: esecuzione dell'istruzione 22 I bus I bus • I bus sono linee di collegamento tra la CPU e la memoria centrale (bus interni) e le periferiche (bus esterni) • I bus sostituiscono l'insieme (complesso) dei collegamenti reciproci tra tutte le singole componenti. • I bus trasportano vari tipi di informazioni, la velocità del “traffico smaltito” dipende dal numero di bit (32, 64) che i bus riescono a trasportare simultaneamente. • Il bus è in realtà costituito da tre bus distinti: • bus dei dati • bus degli indirizzi (destinatario, mittente) • bus dei segnali di controllo • Sul bus dei dati viaggiano dati da e verso la CPU • Sugli altri bus viaggiano indirizzi e segnali di controllo che provengono soltanto dalla CPU CPU Memoria Memoria principale secondaria Dispositivi di Input e di Output Bus Le memorie secondarie Le memorie secondarie • Sono dette anche memorie di massa o ausiliarie. • Hanno una funzione ed una realizzazione hardware diversa da quelle della memoria primaria • permettono di mantenere le informazioni anche a macchina spenta • sono meno costose • l’accesso è più lento • in alcune dipende dalla posizione del dato Le memorie secondarie • Unità a disco • Solitamente ad accesso diretto (Gbyte) • Disco rigido o hard disk • Disco flessibile o floppy disk • Dischi ottici • Nastri magnetici • Ad accesso sequenziale: il tempo di accesso dipende dalla posizione del dato Hard disk • Il disco rigido è composto da una pila di piatti rotanti rivestiti di un materiale magnetico. Ogni piatto è organizzato in tracce circolari concentriche, ciascuna traccia è divisa in settori (inizializzazione o formattazione). La testina di lettura/scrittura si sposta in senso radiale individuando univocamente la posizione. (Fig.3 par. 1.2) • Hanno elevata capacità, ma sono lenti; si utilizzano per copie di sicurezza. Sono a tecnologia magnetica. 23 Hard disk • È contenuto in una scatola sigillata per proteggerlo dalla polvere • Programmi e dati risiedono sul disco rigido e vengono caricati nella RAM quando necessario, per poi tornarvi aggiornati se e quando necessario. Altre memorie secondarie • Nei dischetti lo strato magnetico è applicato su un supporto flessibile • Nei dischi ottici la lettura avviene tramite un raggio laser ed un rilevatore del passaggio della luce (alcuni sono sequenziali con tracce a spirale, altri ad accesso diretto) • CD-ROM: Compact Disk Read Only Memory • usato per distribuire programmi o informazioni Altre memorie secondarie • CD-R (Compact Disk Recordable) • può essere scritto dall'utente una sola volta • CD-RW possono essere scritti più volte • DVD (Digital Video Disk, Digital Versatile Disk) • enorme capacità di memorizzazione • “chiavette” o “penne” USB (Universal Serial Bus) in sostituzione del floppy (capacità di vari Gbyte). Principali dispositivi per l'uscita • • • • Video (schermo) Stampante Plotter Altoparlanti Principali dispositivi di ingresso • • • • • • • Tastiera Mouse Trackball Joystick Microfono Scanner Penna luminosa La scheda madre • La scheda madre (mother-board) situata all'interno di un Personal Computer contiene la memoria primaria, la CPU, i bus, gli alloggiamenti (slot) di espansione, le porte per il controllo delle periferiche. (Fig.4 par. 1.2) 24 La scheda madre La scheda madre di un PC CPU slot Risorse software RAM Risorse software Software di base • Con la parola software si intende in generale un insieme di programmi. • Si distinguono però i programmi fatti dagli utenti dai programmi propri del calcolatore e che gli utenti utilizzano: • Il software di base è un insieme di programmi che permettono il funzionamento del calcolatore e il suo utilizzo da parte di utenti esterni. Esso interagisce direttamente con l’hardware nascondendo all’utente la struttura fisica della macchina. • Software di base • Software applicativo: • pacchetti di programmi che risolvono specifici problemi (“libreria”, “library - biblioteca”) Software di base • Le principali componenti sono: • Sistema operativo • programmi per il funzionamento del calcolatore; è strettamente legato all’hardware • Interfaccia utente • permette la comunicazione con il sistema operativo: comandi da tastiera o da menu (grafica) Linguaggi di programmazione • Software di comunicazione • comunicazione tra calcolatori: reti locali o mondiali. 25 Linguaggi di programmazione Linguaggi di programmazione • I linguaggi di programmazione sono quei linguaggi in cui si devono esprimere le istruzioni che si vogliono elaborare. • Nei linguaggi di programmazione si distinguono dei livelli e delle generazioni: • generazioni: le varie generazioni si distinguono in base ai concetti che sono stati successivamente introdotti • basso livello: • I generazione: linguaggio macchina • II generazione: linguaggi tipo per Assembler • alto livello • III generazione: linguaggi procedurali (imperativi) Fortran, Pascal, C, Java, … • IV generazione: linguaggi logici (Prolog) e funzionali (Lisp) • livello basso livello “vicini” alla macchina alto livello “vicini” all’uomo Linguaggi di programmazione Linguaggi di programmazione • I linguaggi ad alto livello sono linguaggi formali (dotati di una grammatica e con istruzioni aventi un preciso significato). • I linguaggi procedurali sono linguaggi in cui si individuano gruppi di istruzioni (procedure) che risolvono un problema specifico; sono detti anche imperativi perché le istruzioni eseguono comandi di assegnazione di valori nella memoria. • Si possono raggruppare secondo diversi modelli (detti anche paradigmi di programmazione): imperativo, funzionale e logico. Linguaggi di programmazione • Il passaggio dai linguaggi a basso livello a quelli ad alto livello si ha con l’introduzione del concetto di variabile: in tale modo si ha l’indipendenza dalla locazione di memoria. • Successivamente sono stati introdotti i concetti di tipo di dato e di tipo di dato astratto (linguaggi a oggetti). • Nei linguaggi ad alto livello si hanno le strutture di controllo che permettono di regolare il flusso delle operazioni da eseguire. Programmazione in linguaggio macchina 26 Le istruzioni macchina Le istruzioni macchina • Le istruzioni elementari eseguite da un computer (cioè dalla sua CPU) si chiamano istruzioni macchina. (par. 1.3) • I primi programmi erano scritti in linguaggio macchina e quindi un programmatore doveva conoscere tutti i codici numerici delle istruzioni macchina, oltre alle istruzioni che costituivano l’algoritmo risolutivo. • Nel linguaggio macchina le istruzioni sono scritte in codice binario (sequenze di 0 e 1); sono pertanto difficili da scrivere e poco leggibili. • L’insieme di istruzioni macchina (instruction set) è specifico di una particolare CPU (CPU diverse hanno un diverso insieme di istruzioni). Le istruzioni macchina Le istruzioni macchina • Esempio. Supponiamo di voler confrontare due numeri, il primo è memorizzato in una variabile di nome a, il secondo è il valore costante 100, e vogliamo esprimere questa situazione: se a > 100 allora stampa “ERRORE” • Le istruzioni macchina dovranno rappresentare una sequenza di operazioni di questo tipo: Le istruzioni macchina Le istruzioni macchina • In tutte le CPU, le istruzioni macchina si possono suddividere nelle seguenti categorie (i nomi delle istruzioni sono solo degli esempi) • trasferimento dati, tra i registri e la memoria principale • carica in un registro il valore contenuto nella posizione di memoria 40 • nel linguaggio macchina non esiste il concetto di variabile, ma solo la locazione di memoria individuata da un indirizzo • carica in un altro registro il valore 100 • se il primo valore è maggiore del secondo, prosegui con l’istruzione contenuta nella posizione di memoria 240 (par. 1.3) • salti, per alterare il flusso di esecuzione sequenziale (viene modificato il Program Counter) • incondizionato (JUMP): salta in ogni caso • condizionato: salta solo se un certo valore è zero (JZ) o se è maggiore di zero (JGZ) • LOAD (verso un registro), STORE (verso la memoria) • operazioni aritmetiche e logiche, eseguite dalla ALU • aritmetiche: ADD, SUB, MUL, DIV • logiche: AND, OR, NOT 27 Linguaggi per Assembler Linguaggi per Assembler • Si utilizzano dei nomi simbolici per rappresentare le operazioni da eseguire e per rappresentare le locazioni di memoria istruzioni assembler istruzioni macchina corrispondenza biunivoca Assemblatore I linguaggi assembly • Utilizzando l’assemblatore, il programmatore scrive il programma mediante dei nomi abbreviati (codici mnemonici) per le istruzioni macchina: il programma diventa più facile da scrivere e da leggere . • L’assemblatore si occupa poi di tradurre il programma in configurazioni di bit. • Tali linguaggi con codici mnemonici si dicono linguaggi assembly (uno diverso per ogni CPU). • Problema: occorrono molte istruzioni per eseguire anche le operazioni più semplici • Problema: la sequenza di istruzioni di uno stesso programma cambia al cambiare della CPU • è molto costoso scrivere programmi che possano funzionare su diverse CPU, perché praticamente bisogna riscriverli completamente Linguaggi ad alto livello Linguaggi di programmazione ad alto livello • Negli anni ‘50 furono inventati i primi linguaggi di programmazione ad alto livello • FORTRAN: primo “vero” linguaggio • BASIC, COBOL • Anni ‘60 e ‘70: programmazione strutturata • Pascal (Niklaus Wirth, 1968) • C (Brian Kernigham e Dennis Ritchie, 1970-75) • Anni ‘80 e ‘90, programmazione orientata agli oggetti • C++ (Bjarne Stroustrup, 1979) • Java (James Gosling e Patrick Naughton, 1991) 28 Linguaggi ad alto livello • Il programmatore esprime una sequenza di operazioni da compiere, senza scendere al livello di dettaglio delle istruzioni macchina • Java: if(a>100) System.out.println("Errore"); • C++: if(a>100) cout<<"Errore\n"; Linguaggi ad alto livello • Pascal: if a>100 then writeln ('Errore'); • Fortan77 if(a.gt.100) then write(6,*) 'Errore' endif Nel Fortran l’istruzione deve essere scritta su righe diverse. • Fortran90: if(a>100) then ... Il linguaggio Java e la JVM • Nel caso del linguaggio Java, l’istruzione: if(a>100) System.out.println("Errore"); viene tradotta dal compilatore in una sequenza di numeri 21 40 16 100 163 240 che la JVM interpreta (decodifica ed esegue i comandi corrispondenti alla sequenza). 29