MATERIALE DIDATTICO IDONEITA CLASSE 5 I.T.I. INFORMATICA INFORMATICA TECNICHE DI TESTING DISTINZIONE DI RUOLO DI LINGUAGGI ARCHITETTURA DI VON NEUMANN PROGETTAZIONE TOP-DOWN E BOTTOM-UP PROGRAMMAZIONE FUNZIONALE LINGUAGGI DI SPECIFICA MODELLO CONCETTUALE DI UN SISTEMA INFORMATICO SISTEMI PER LA GESTIONE DI BASI DI DATI (DBMS) MODELLO RELAZIONALE FILE HASH TECNICHE DI TESTING Nella Programmazione informatica, lo unit testing è una procedura usata per verificare singole parti di un codice sorgente . Per unità si intende genericamente la minima parte testabile di un codice sorgente: nella programmazione procedurale un'unità può rappresentare un singolo programma, funzione, procedura, etc.; nella Programmazione orientata agli oggetti, la più piccola unità può essere il metodo. Lo Unit Testing si articola in en:test case ciascuno dei quali dovrebbe essere indipendente dagli altri. Lo Unit Testing viene normalmente eseguito dagli sviluppatori, non da utenti final Lo scopo dell'Unit testing è quello di verificare il corretto funzionamento di parti di programma permettendo così una precoce individuazione dei bug. Uno unit testing accurato può dare una prova certa se un pezzo di codice funziona correttamente, con importanti vantaggi Lo unit testing facilita la modifica del codice del modulo in momenti successivi (en:refactoring) con la sicurezza che il modulo continuerà a funzionare correttamente. Il procedimento consiste nello scrivere en:test case per tutte le funzioni e i metodi, in modo che se una modifica produce un fallimento del test, si possa facilmente individuare la modifica responsabile. Lo unit testing semplifica l'integrazione di moduli diversi perché limita i malfunzionamenti dovuti a problemi nella interazione tra i moduli e non nei moduli stessi, rendendo i test di integrazione più semplici. In generale il testing non riesce ad identificare tutti gli errori in un programma e lo stesso vale per lo Unit Testing che, analizzando per definizione le singole unità, non può identificare gli errori di integrazione, problemi legati alla performance e altri problemi legati al sistema in generale. Lo unit testing è più efficace se utilizzato in congiunzione con altre tecniche di testing del software. Come ogni forma di testing, anche lo Unit Testing non può individuare l'assenza di errori ma può solo evidenziarne la presenz Per ottenere gli sperati benefici dallo unit test, è richiesto un rigoroso senso di disciplina durante tutto il processo di sviluppo. È essenziale mantenere traccia non solo dei test che non stati sviluppati ed eseguiti, ma anche di tutte le modifiche effettuate al codice funzionale dell'unità in esame e di tutte le altre. L'uso di un sistema di controllo versione è essenziale. Se una versione successiva di una unità fallisce un test che aveva passato in precedenza, il sistema di controllo versione permette di evidenziare le modifiche al codice intervenute nel frattempo. Con la locuzione alpha testing, si indica una particolare fase del collaudo del software, che si svolge internamente al gruppo degli sviluppatori ed ha lo scopo di ricercare eventuali bug e malfunzionamenti. Solitamente durante lo sviluppo di un software vengono realizzate diverse versioni Alpha, in alcuni casi (soprattutto nel caso di software opensource) pubbliche, per introdurre nuove caratteristiche del software delle quali è necessario controllare il funzionamento a fondo. Le versioni Alpha sono quindi da considerare incomplete e mancanti di alcune funzionalità e, spesso, instabili e perciò non adatte all'utente inesperto. Al termine dell'Alpha testing si passa al beta testing dedicato alla scoperta e soluzione dei bug, quindi si rilasciano una o più release candidate, prima della versione finale. il beta testing (o beta-verifica) è una fase di prova e collaudo di un software non ancora pubblicato, con lo scopo di trovare eventuali errori (bug). Questa operazione può essere svolta da professionisti pagati, oppure, molto spesso, da semplici amatori, chiamati beta tester. La loro importanza è legata, oltre al test dei sistemi operativi, anche ai programmi minori poiché tendono a comportasi diversamente in base all'hardware su cui girano, per cui conviene avere la possibilità di provarli su più hardware differenti. Alla fine del beta testing, il programma è considerato completo e distribuibile. Si parlerà quindi, a questo punto, di versione build. Se si tratta di software commerciale, non è detto che i beta tester ricevano una copia gratuita del software. DISTINZIONE E RUOLO DEI LINGUAGGI L'Elaborazione del linguaggio naturale, detta anche NLP (dall'inglese Natural Language Processing), è il processo di estrazione di informazioni semantiche da espressioni del linguaggio umano o naturale, scritte o parlate, tramite l'elaborazione di un calcolatore elettronico. Questo processo è reso particolarmente difficile e complesso a causa delle caratteristiche intrinseche di ambiguità del linguaggio umano. Per questo motivo il processo di elaborazione viene suddiviso in fasi diverse, tuttavia simili a quelle che si possono incontrare nel processo di elaborazione di un linguaggio di programmazione: • analisi lessicale: scomposizione di un'espressione linguistica in token (in questo caso le parole) • analisi grammaticale: associazione delle parti del discorso a ciascuna parola nel testo • analisi sintattica: arrangiamento dei token in una struttura sintattica (ad albero: parse tree) • analisi semantica: assegnazione di un significato (semantica) alla struttura sintattica e, di conseguenza, all'espressione linguistica In informatica, un linguaggio di programmazione è un linguaggio formale, dotato di un lessico, una sintassi e una semantica ben definite, utilizzabile per il controllo del comportamento di una macchina formale, o di una implementazione di essa (tipicamente, un computer). Tutti i linguaggi di programmazione esistenti possiedono (almeno) questi due concetti chiave: • Variabile: un dato o un insieme di dati, noti o ignoti, già memorizzati o da memorizzare; ad una variabile corrisponde sempre, da qualche parte, un certo numero (fisso o variabile) di locazioni di memoria che vengono allocate, cioè riservate, per contenere i dati stessi. Molti linguaggi inoltre attribuiscono alle variabili un tipo, con differenti proprietà (stringhe di testo, numeri, liste, atomi ecc.). • Istruzione: un comando, una funzione, oppure una regola descrittiva: anche il concetto di istruzione è molto variabile fra i vari linguaggi. A prescindere dal particolare linguaggio però, ogni volta che un'istruzione viene eseguita, lo stato interno del calcolatore (che sia lo stato reale della macchina oppure un ambiente virtuale, teorico, creato dal linguaggio) cambia. Alcuni concetti sono poi presenti nella gran parte dei linguaggi: • Espressione: una combinazione di variabili e costanti, unite da operatori; le espressioni sono state introdotte inizialmente per rappresentare le espressioni matematiche, ma in seguito la loro funzionalità si è estesa. Una espressione viene valutata per produrre un valore, e la sua valutazione può produrre "effetti collaterali" sul sistema e/o sugli oggetti che vi partecipano. • Strutture di controllo, che permettono di governare il flusso dell'esecuzione del programma, alterandolo in base al risultato di una espressione (che può ridursi al contenuto di una variabile, o essere anche molto complessa). • Sottoprogramma: un blocco di codice che può essere richiamato da qualsiasi altro punto del programma. • Strutture dati, meccanismi che permettono di organizzare e gestire dati complessi. Programmare in un dato linguaggio di programmazione significa generalmente scrivere uno o più semplici file di testo ASCII, chiamato codice sorgente. I font, i colori e in generale l'aspetto grafico sono irrilevanti ai fini della programmazione in sé: per questo i programmatori non usano programmi di videoscrittura ma degli editor di testo (come emacs e brief) che invece offrono funzioni avanzate di trattamento testi (espressioni regolari, sostituzioni condizionali e ricerche su file multipli, possibilità di richiamare strumenti esterni ecc). Se un dato editor è in grado di lavorare a stretto contatto con gli altri strumenti di lavoro (compilatore, linker, interprete ecc.: vedi più avanti) allora più che di semplice editor si parla di IDE o ambiente di sviluppo integrato. Va notato che alcuni linguaggi di programmazione recenti consentono anche una forma mista di programmazione, in cui alla stesura di codice sorgente ASCII si associano anche operazioni di programmazione visuale, attraverso le quali il programmatore descrive alcuni aspetti del programma disegnando a video attraverso il mouse; un'applicazione tipica di quest'ultima forma di programmazione è il disegno interattivo della GUI del programma (finestre, menù, e così via). Il codice sorgente, contenente le istruzioni da eseguire e (spesso) alcuni dati noti e costanti, può essere poi eseguito passandolo ad un interprete che eseguirà le istruzioni in esso contenute, il che è la prassi normale per i linguaggi di scripting; oppure può venire compilato, cioè tradotto in istruzioni di linguaggio macchina da un programma compilatore: il risultato è un file binario 'eseguibile' (codice eseguibile) che non ha bisogno di altri programmi per andare in esecuzione, ed è anche molto più veloce di un programma interpretato. In passato, la compilazione è stata la norma per tutti i linguaggi di programmazione di uso generale; attualmente vi sono numerosi linguaggi interpretati e di uso generale, come il linguaggio Java o quelli della piattaforma .NET, che applicano un approccio ibrido fra le due soluzioni, utilizzando un compilatore per produrre del codice in un linguaggio intermedio (detto bytecode) che viene successivamente interpretato. La differenza di prestazioni tra i linguaggi interpretati e quelli compilati è stata ridotta con tecniche di compilazione just-in-time, sebbene si continui ad utilizzare i linguaggi compilati (se non addirittura l'assembly) per le applicazioni che richiedono le massime prestazioni possibili. La compilazione è il processo per cui il programma, scritto in un linguaggio di programmazione ad alto livello, viene tradotto in un codice eseguibile per mezzo di un altro programma detto appunto compilatore. La compilazione offre numerosi vantaggi, primo fra tutti il fatto di ottenere eseguibili velocissimi nella fase di run (esecuzione) adattando vari parametri di questa fase all'hardware a disposizione; ma ha lo svantaggio principale nel fatto che è necessario compilare un eseguibile diverso per ogni sistema operativo o hardware sul quale si desidera rendere disponibile l'esecuzione. ARCHITETTURA DI VON NEUMANN Con l'espressione architettura di von Neumann (o macchina di von Neumann) ci si riferisce a uno schema di progettazione di calcolatori elettronici che prende nome dal matematico John von Neumann e che fu sviluppato per il sistema IAS machine e dell'Institute for Advanced Study. Lo schema si basa su cinque componenti fondamentali: 1. CPU o unità di lavoro che si divide a sua volta in 1. Unità operativa, nella quale uno dei sottosistemi più rilevanti è l'ALU (Arithmetic Logic Unit) 2. Unità di controllo 2. Unità di memoria, intesa come memoria di lavoro o memoria principale (RAM, Random Access Memory) 3. Unità di input, tramite la quale i dati vengono inseriti nel calcolatore per essere elaborati 4. Unità di output, necessaria affinché i dati elaborati possano essere restituiti all'operatore 5. Bus, un canale che collega tutti i componenti fra loro All'interno dell'ALU è presente un registro detto accumulatore, che fa da buffer tra input e output grazie a una speciale istruzione che carica una parola dalla memoria all'accumulatore e viceversa. È importante sottolineare che tale architettura, a differenza di altre, si distingue per la caratteristica di immagazzinare all'interno dell'unità di memoria, sia i dati dei programmi in esecuzione che il codice di questi ultimi. Bisogna comunque precisare che questa è una schematizzazione molto sintetica, sebbene molto potente: basti pensare che i moderni computer di uso comune sono progettati secondo l'architettura Von Neumann. Inoltre, quando si parla di unità di memoria si intende la memoria principale, mentre le memorie di massa sono considerate dispositivi di I/O. Il motivo di ciò è innanzitutto storico, in quanto negli anni Quaranta, epoca a cui risale questa architettura, la tecnologia non lasciava neanche presupporre dispositivi come hard disk, CD-ROM, DVD-ROM o anche solo nastri magnetici, ma anche tecnico, se si considera che in effetti i dati da elaborare devono comunque essere caricati in RAM, siano essi provenienti da tastiera o da hard-disk. PROGETTAZIONE TOP-DOWN E BOTTOM-UP I modelli top-down e bottom-up (ing. dall'alto verso il basso e dal basso verso l'alto, rispettivamente) dell'informazione e di sono gestione strategie delle di conoscenze, elaborazione riguardanti principalmente il software e, per estensione, altre teorie umanistiche e teorie dei sistemi. In linea generale, esse sono metodologie adoperate per analizzare situazioni problematiche e costruire ipotesi adeguate alla loro soluzione: il concetto di situazione problematica è riconducibile ad ambiti tra i più vari come ad esempio la elaborazione di un programma informatico, la risoluzione di un problema geometrico ovvero matematico, la elaborazione di un testo, la risoluzione di un problema pratico/operativo. Nel modello top-down è formulata una visione generale del sistema senza scendere nel dettaglio di alcuna delle sue parti. Ogni parte del sistema è successivamente rifinita (decomposizione, specificazione o identificazione) [1] specializzazione e aggiungendo maggiori dettagli dalla progettazione. Ogni nuova parte così ottenuta può quindi essere nuovamente rifinita, specificando ulteriori dettagli finché la specifica completa è sufficientemente dettagliata da validare il modello. Il modello top-down è spesso progettato con l'ausilio di scatole nere che semplificano il riempimento ma non consentono di capirne il meccanismo elementare. In contrasto con il modello top-down c'è la progettazione bottom-up, nella quale parti individuali del sistema sono specificate in dettaglio. Queste parti vengono poi connesse tra loro in modo da formare componenti più grandi, che vengono a loro volta interconnessi fino a realizzare un sistema completo. Le strategie basate sul flusso informativo bottom-up sembrano potenzialmente necessarie e sufficienti dato che sono basate sulla conoscenza di tutte le variabili che possono condizionare gli elementi del sistema. Il top down richiama l'immagine di una piramide con la cima (top) in alto e la base (down) posta in posizione orizzontale; la caratterizzazione dinamica del metodo prevede di partire dal top e scendere verso il down. Top, termine molto usato nel linguaggio contemporaneo (top ten, top model, ecc.), può essere efficacemente tradotto in obiettivo inteso come obiettivo da raggiungere per risolvere una situazione problematica; down è riconducibile alla strategia necessaria a conseguire il fine predeterminato. Il top down parte dall'obiettivo e da esso fa scaturire la strategia direttamente adatta a determinare l'obiettivo stesso, quindi valorizza il perché e da esso fa dipendere il come, ovvero la strategia; individua, quindi, le risorse necessarie, precisa quelle disponibili e identifica quelle mancanti, propone successivamente ogni risorsa mancante come subobiettivo ovvero come sotto-problema in cui ciascun sub-obiettivo richiede una sub-strategia ad esso correlata. Il top down struttura e sistematizza la riflessione, motiva ogni passaggio in modo logico e ripulisce il metodo di lavoro da tentativi casuali, intreccia strettamente analisi ed elaborazione, dà alla strategia complessiva un carattere di eleganza sostanziale e formale che si estrinseca come trasparenza e sistematicità. È soprattutto adatto, ma non solo, a situazioni complesse, inedite, di differenti tipologie. Il bottom up richiama invece un'immagine raffigurante una freccia in cui la coda è il bottom (la parte bassa) mentre up è la punta: dal punto di vista dinamico si parte dal bottom e si procede verso up. Il bottom up prende corpo dal punto di partenza (bottom) ovvero dalla situazione iniziale; considera l'obiettivo finale, induce a costruire un percorso sequenziale organizzato in passaggi successivi in cui l'ancoraggio tra traguardi intermedi e obiettivo finale è ricercato con metodo generalmente improntato a tentativo ed errore quindi di tipo casuale, nei casi migliori intuitivo. È per lo più adatto a situazioni problematiche lineari, semplici, delle quali la dinamica è generalmente nota. Nel processo di sviluppo software, gli approcci top-down e bottom-up giocano un ruolo fondamentale. L'approccio top-down enfatizza la pianificazione ed una completa comprensione del sistema. È ovvio che nessuna codifica può iniziare finché non si è raggiunto almeno un sufficiente livello di dettaglio nella progettazione di una parte significante del sistema. Questo, comunque, ritarda la fase di test delle ultime unità funzionali di un sistema finché una parte significativa della progettazione non è stata completata. L'approccio bottom-up enfatizza la codifica e la fase di test precoce, che può iniziare appena il primo modulo è stato specificato. Questo approccio, comunque, induce il rischio che i moduli possano essere codificati senza avere una chiara idea di come dovranno essere connessi ad altre parti del sistema, e quel tipo di link potrebbe non essere facile. La riusabilità del codice è uno dei principali benefici dell'approccio bottom-up La programmazione top-down è uno stile di programmazione, fondamento dei tradizionali linguaggi procedurali, nel quale la progettazione inizia specificando parti complesse e suddividendole successivamente in parti più piccole. Eventualmente, i componenti sono specificati quanto basta per la codifica ed il programma viene anche scritto. Questo è l'esatto opposto della programmazione bottom-up che è comune nei linguaggi orientati agli oggetti come C++ o Java. La tecnica per la scrittura di un programma mediante l'utilizzo dei metodi top-down indica di scrivere una procedura principale che indica dei nomi per le principali funzioni di cui avrà bisogno. In seguito, il gruppo di programmazione esaminerà i requisiti di ognuna di queste funzioni ed il processo verrà ripetuto. Queste sotto-procedure a comparto eseguiranno eventualmente azioni così semplici che porteranno ad una codifica semplice e concisa. Quando tutte le varie sotto-procedure sono state codificate, il programma è realizzato. PROGRAMMAZIONE FUNZIONALE in informatica la programmazione funzionale è un paradigma di programmazione in cui il flusso di esecuzione del programma assume la forma di una serie di valutazioni di funzioni matematiche. Solitamente questo approccio viene usato maggiormente in ambiti accademici piuttosto che industriali. Il punto di forza principale di questo paradigma è la mancanza di effetti collaterali (side-effect) delle funzioni, il che comporta una più facile verifica della correttezza e della mancanza di bug del programma e la possibilità di una maggiore ottimizzazione dello stesso. Un uso particolare del paradigma, per l'ottimizzazione dei programmi, è quello di trasformare gli stessi per utilizzarli nella programmazione parallela. La programmazione funzionale pone maggior accento sulla definizione di funzioni, rispetto ai paradigmi procedurali e imperativi, che invece prediligono la specifica di una sequenza di comandi da eseguire. In questi ultimi, i valori vengono calcolati cambiando lo stato del programma attraverso delle assegnazioni; un programma funzionale, invece, è immutabile: i valori non vengono trovati cambiando lo stato del programma, ma costruendo nuovi stati a partire dai precedenti. L'esempio più vecchio di linguaggio funzionale è il Lisp, anche se né il LISP originale né i Lisp moderni, come il Common Lisp, sono puramente funzionali. Le varianti del Lisp includono il Logo, lo Scheme, Dylan e Clojure. Esempi di linguaggi funzionali moderni sono l'Haskell e i linguaggi della famiglia ML, quali ML e OCaml. Un altro linguaggio derivato da ML è F#, sviluppato da Microsoft all'interno del framework .NET. Altri sono l'Erlang, Mathematica, il Clean e Miranda. Altri linguaggi, come per esempio Ruby, Python, Perl e TCL, possono essere usati in stile funzionale, dato che hanno funzioni di ordine superiore e altre astrazioni utili. Oggi si sta cercando anche di sviluppare linguaggi di programmazione funzionali quantistici, cioè che possano esprimere algoritmi quantistici. Alcuni esempi sono il linguaggio di Peter Selinger ed il linguaggio Haskell-like QML LINGUAGGI DI SPECIFICA Il termine specifica, nell’ingegneria del software, viene usato in diversi contesti con significati diversi. In genere si può definire come un accordo tra un produttore di servizi ed un utente. A seconda del contesto il produttore e l’utente saranno diversi. Si avrà una specifica dei requisiti tra sviluppatore e committente o utente finale, una specifica di progetto tra progettista e implementatore e una specifica di modulo tra i programmatori che hanno prodotto il modulo e programmatore che lo integra. La Specifica dei Requisiti è usata per • Definire le necessità dell’utente • Definire le caratteristiche del sistema implementato • Comprendere il sistema nelle attività di manutenzione La fase di specifica dei requisiti è molto critica, se i requisiti non prendono in esame alcuni aspetti il progetto rischierebbe degli inutili ritardi e inevitabili manutenzioni. Per questo motivo è necessario effettuare la validazione approvazione dell’utente. della specifica, ossia sottoporla alla Non esiste un unico modo di scrivere una specifica • Dipende dalla tipologia del sistema • Dipende dal livello di formalità • Dipende dallo stile di specifica che si vuole adottare Una specifica deve essere • Chiara, precisa e comprensibile • Coerente • Completa L’uso del linguaggio naturale (italiano, inglese,...) fa sì che spesso le qualità di chiarezza, precisione e comprensibilità non siano presenti. Tipicamente c’è la tendenza ad essere imprecisi. L’uso di tecniche formali consente di scoprire le ambiguità. Ci sono formule diverse che definiscono lo stesso comportamento informale. La coerenza è l’assenza di contraddizioni. Più il sistema è complesso più è facile che vi siano delle inconsistenze. L’uso di tecniche formali può permettere l’individuazione di inconsistenze. La completezza riguarda la presenza di tutte le informazioni necessarie ad una corretta comprensione. Vi sono due tipi di completezza: • Interna • Rispetto ai requisiti Una specifica è internamente completa se definisce tutti i concetti di cui fa uso e si può ottenere mediante l’uso di glossari. La completezza rispetto ai requisiti richiede che tutti gli aspetti siano definiti. Se il sistema è complesso. Un approccio incrementale può essere utile. Le specifiche possono essere poste in maniera formale o in maniera informale. Le specifiche informali fanno uso di linguaggio naturale per descrivere i requisiti. Possono essere utilizzati diagrammi e tabelle per aumentare le informazioni. La sintassi e la semantica non sono formalmente definite. Le specifiche formali usano linguaggi che hanno sintassi e semantica definite in modo formale e sono usate principalmente per sistemi safetycritical. Consentono animazione, simulazione e verifica di proprietà. Esistono anche le specifiche semiformali: • Fanno uso di sintassi formalmente definita, ma la semantica è informale • Molto spesso sono linguaggi grafici La seconda distinzione è tra specifiche operazionali e specifiche descrittive. La prima descrive il sistema desiderato specificando il comportamento desiderato, fornendo di solito un modello del sistema, le seconde esprimono le proprietà desiderate in modo puramente dichiarativo. MODELLO CONCETTUALE Un modello concettuale rappresenta concetti (entità e relationi tra le entità, a differenza di un modello mentale che descrive idee di un certo dominio del problema. La modellazione o progettazione concettuale è una tecnica molto nota di progettazione dati, assieme alla progettazione logica e alla progettazione fisica. Il modello concettuale deve essere per definizione indipendente dai dettagli dell'implementazione, come la concorrenza o la memorizzazione deidati. Lo scopo del modello concettuale è esprimere il significato di termini e concetti usati dagli esperti del dominio per discutere il problema, e a trovare le giuste relazioni tra concetti differenti. Questo modello è anche chiamatomodellosemantico. Il modello concettuale cerca di chiarire il significato di vari termini spesso ambigui, e assicura che non ci siano problemi con una differente interpretazione di termini e concetti. Questo perché, tali interpretazioni possono portare errori nel progetto software basato su tale interpretazione dei concetti. Una volta che i concetti del dominio sono stati modellati, il modello diventa una base stabile per lo sviluppo successivo dell'applicazione nel dominio. I concetti del modello concettuale possono essere usati come base di una progettazione orientata agli oggetti e implementati in un programma, come le classi di un linguaggio orientato agli oggetti. La realizzazione di modelli concettuali di domini diversi possono essere combinati tra di loro a formare una piattaforma coerente. Un modello concettuale può essere descritto usando varie notazioni, come UML o OMT per la progettazione a oggetti, o IE o IDEF1X per il Modello entità-relazione. Nella notazione UML, il modello concettuale è spesso descritto con un class diagram nel quale le classi rappresentano concetti, le associazioni rappresentano relazioni tra i concetti e i role type di un'associazione rappresentano i "role types" assunti dalle istanze dei concetti nelle varie situazioni. Nella notazione ER, il modello concettuale è descritto con un diagramma ERnel quale le entità rappresentano i concetti, cardinalità and opzionalità rappresentano le relazioni tra i concetti. SISTEMI PER LA GESTIONE DI BASI DI DATI In informatica, il termine database, banca dati, base di dati (soprattutto in testi accademici) o anche base dati, indica un archivio strutturato in modo tale da consentire la gestione dei dati stessi (l'inserimento, la ricerca, la cancellazione ed il loro aggiornamento) da parte di applicazioni software. Il database è un insieme di informazioni, di dati che vengono suddivisi per argomenti in ordine logico (tabelle) e poi tali argomenti vengono suddivisi per categorie (campi). Informalmente e impropriamente, la parola "database" viene spesso usata come abbreviazione dell'espressione Database Management System (DBMS), che si riferisce a una vasta categoria di sistemi software che consentono la creazione e la manipolazione efficiente di database. La base di dati, oltre ai dati veri e propri, deve contenere anche le informazioni sulle loro rappresentazioni e sulle relazioni che li legano. Spesso, ma non necessariamente, una base dati contiene le seguenti informazioni: • Strutture dati che velocizzano le operazioni frequenti, tipicamente a spese di operazioni meno frequenti. • Collegamenti con dati esterni, cioè riferimenti a file locali o remoti non facenti parte del database. • Informazioni di sicurezza, che autorizzano solo alcuni profili utente ad eseguire alcune operazioni su alcuni tipi di dati. • Programmi che vengono eseguiti, automaticamente o su richiesta di utenti autorizzati, per eseguire elaborazioni sui dati. Un tipico automatismo consiste nell'eseguire un programma ogni volta che viene modificato un dato di un certo tipo. In un sistema informatico, una base di dati può essere manipolata direttamente dai programmi applicativi, interfacciandosi direttamente con il sistema operativo. Tale strategia era quella adottata universalmente fino agli anni sessanta, ed è tuttora impiegata quando i dati hanno una struttura molto semplice, o quando sono elaborati da un solo programma applicativo. A partire dalla fine degli anni Sessanta, tuttavia, per gestire basi di dati complesse condivise da più applicazioni si sono utilizzati appositi sistemi software, detti sistemi per la gestione di basi di dati (in inglese "Database Management System" o "DBMS"). Uno dei vantaggi di questi sistemi è la possibilità di non agire direttamente sui dati, ma di vederne una rappresentazione concettuale. La ricerca nel campo delle basi di dati studia le seguenti problematiche: • Progettazione di basi di dati. • Progettazione e implementazione di DBMS. • Interpretazione (o analisi) di dati contenuti in database. Le basi di dati spesso fanno uso di tecnologie derivate da altre branche dell'informatica. È usuale utilizzare tecniche derivate dall'intelligenza artificiale, come ad esempio il data mining, per cercare di estrarre relazioni o più in generale informazioni presenti nelle banche dati ma non immediatamente visibili. È possibile distinguere i linguaggi per basi di dati secondo il loro utilizzo: • Data Definition Language (DDL) - consente di definire la struttura della base di dati e le autorizzazioni per l'accesso. • Device Media Control Language (DMCL) - permette alla struttura fisica del database di far riferimento alle particolari unità di memoria di massa utilizzate dal sistema. • Data Manipulation Language (DML) - consente di interrogare e aggiornare le istanze della base di dati. • Data Control Language (DCL) - permette la gestione dell'accesso al database con relative restrizioni di operazioni come aggiornamento, selezione e cancellazione. • Query language (QL) - permette di interrogare il database al fine di ritrovare i dati relativi alla chiave di ricerca impostata dall'utente. Inoltre è possibile suddividere i linguaggi come: • Linguaggi testuali interattivi, come l'SQL, di cui sono stati pubblicati diversi standard, che attualmente è il linguaggio più utilizzato. • Linguaggi testuali interattivi immersi in linguaggi di in linguaggi di programmazione comuni, quali C, Basic ecc. • Linguaggi testuali interattivi immersi programmazione proprietari. • Linguaggi grafici e user-friendly, come QBE (Query By Example), che possono essere utilizzati anche dai meno esperti. MODELLO RELAZIONALE Il modello relazionale è un modello logico di rappresentazione dei dati implementato su sistemi di gestione di basi di dati (DBMS), detti perciò sistemi di gestione di basi di dati relazionali (RDBMS). Esso si basa sull'algebra relazionale e sulla teoria degli insiemi ed è strutturato attorno al concetto di relazione (detta anche tabella). Venne proposto da Edgar F. Codd nel 1970 per semplificare la scrittura di interrogazioni sui database e per favorire l’indipendenza dei dati; venne reso disponibile come modello logico in DBMS reali nel 1981. Oggi è uno dei modelli logici più utilizzati, implementato su moltissimi DBMS sia commerciali che open source. L'assunto fondamentale del modello relazionale è che tutti i dati sono rappresentati come relazioni; i dati sono manipolati con gli operatori dell'algebra relazionale. Il modello relazionale consente al progettista di database di creare una rappresentazione consistente e logica dell'informazione. La consistenza viene ottenuta inserendo nel progetto del database appropriati vincoli, normalmente chiamati schema logico. La teoria comprende anche un processo di normalizzazione in base al quale viene selezionato tra le diverse alternative lo schema maggiormente "desiderabile". Il piano di accesso e altri dettagli operativi vengono gestiti dal motore del DBMS e non dovrebbero trovare spazio nello schema logico: questo è in contrasto con la pratica corrente in molti DBMS di ottenere miglioramenti delle prestazioni attraverso modifiche dello schema logico. La struttura base del modello relazionale è il dominio o tipo di dato, definito come l'insieme dei valori che può assumere un determinato attributo. Una tupla è un insieme non ordinato di valori degli attributi. Un attributo è una coppia ordinata di "nome di attributo" e "nome di tipo", mentre un valore di attributo è un valore specifico valido per quel tipo di dato. Una relazione consiste di una testata e di un corpo, dove la testata è un insieme di attributi e il corpo è un insieme di n tuple. La testata di una relazione è anche la testata di ciascuna delle sue tuple. La tabella è la rappresentazione grafica normalmente accettata per rappresentare la relazione. Il principio base del modello relazionale è che tutte le informazioni siano rappresentate da valori inseriti in relazioni (tabelle); dunque un database relazionale è un insieme di relazioni contenenti valori e il risultato di qualunque interrogazione (o manipolazione) dei dati può essere rappresentato anch'esso da relazioni (tabelle). Il tipo di dato come viene usato nei database relazionali può essere un insieme di numeri interi, un insieme di caratteri alfanumerici, l'insieme delle date, i valori booleani vero e falso, e così via. I corrispondenti "nomi di tipo" per saranno le stringhe "int", "char", "date", "boolean", etc. Da sottolineare che da una parte la teoria relazionale non definisce quali tipi vadano supportati, e dall'altra molti sistemi garantiscono la possibilità di definire tipi di dati definiti dall'utente, in aggiunta a quelli "standard" forniti dal sistema. Attributo è il termine usato nella teoria per ciò che normalmente si definisce come colonna. Allo stesso modo tabella è normalmente usato al posto del termine teorico relazione. La struttura di una tabella è specificata come una lista di colonne, ciascuna delle quali ha un nome univoco e un dominio, un insieme cioè di valori accettati. Un valore di attributo è il valore di una cella identificata da una specifica coppia riga colonna, come ad esempio "Mario Rossi" o "2006". Una tupla è praticamente la stessa cosa di una riga. Una relazione è la definizione di una tabella (cioè un insieme di colonne) insieme ai dati che vi compaiono. La definizione della tabella è la testata e i dati che vi appaiono sono il corpo, un insieme di righe. Il modello relazionale risponde al requisito dell'indipendenza dei dati e prevede una distinzione tra il livello fisico e il livello logico: questa capacità di astrazione ha fatto la sua fortuna nel mondo della gestione dati. Prima dell'avvento del modello relazionale, i DBMS utilizzavano altri due modelli logici di database: il modello gerarchico e il modello reticolare. Sebbene oggi la gran parte dei DBMS aderiscano (o dicano di aderire: v. le 12 regole di Codd) al modello relazionale, in alcuni centri elettronici sono ancora utilizzati DBMS gerarchici o reticolari, specialmente in casi in cui la migrazione avrebbe costi proibitivi. Secondo alcuni autori il modello relazionale potrebbe in futuro cedere il passo a un modello object oriented, ma da una parte i DBMS objectoriented (OODBMS) stentano ad affermarsi, dall'altra i migliori DBMS relazionali stanno rilasciando funzionalità object-oriented, come la possibilità di definire tipi di dato utente. FILE HASH Nel linguaggio matematico e informatico, la funzione hash è una funzione non iniettiva che mappa una stringa di lunghezza arbitraria in una stringa di lunghezza predefinita. Esistono numerosi algoritmi che realizzano funzioni hash con particolari proprietà che dipendono dall'applicazione. Nelle applicazioni crittografiche si chiede, per esempio, che la funzione hash abbia le seguenti proprietà: • resistenza alla preimmagine: sia computazionalmente intrattabile la ricerca di una stringa in input che dia un hash uguale a un dato hash; • resistenza alla seconda preimmagine: sia computazionalmente intrattabile la ricerca di una stringa in input che dia un hash uguale a quello di una data stringa; • resistenza alle collisioni: sia computazionalmente intrattabile la ricerca di una coppia di stringhe in input che diano lo stesso hash. Nelle applicazioni di basi di dati la funzione hash è usata per realizzare una particolare struttura dati chiamata hash table. In questa applicazione non occorrono proprietà crittografiche e generalmente l'unica proprietà richiesta è che non ci siano hash più probabili di altri È possibile utilizzare le funzioni di hash per creare una hash table: struttura dati molto efficiente per immagazzinare generici dati associati ad una chiave. Questo genere di struttura dati viene spesso utilizzata nei Database per generare gli indici dato che permette di realizzare funzioni di ricerca che individuano gli elementi in un tempo costante, indipendente (almeno in teoria) dal numero di elementi presenti nell'indice. L'uso delle funzioni hash per trovare errori nelle trasmissioni è molto comune. La funzione hash viene calcolata a partire dai dati dal trasmettitore e il suo valore è inviato insieme ai dati. Il ricevitore calcola di nuovo la funzione hash, e se i valori hash non corrispondono, significa che è avvenuto un errore durante la trasmissione. Questo metodo è chiamato controllo ridondante.