Informatica 30/09
Xcode è il programma utilizzabile. Un linguaggio di programmazione serve per comunicare istruzioni ad una macchina, scrivendo le istruzioni nel suo “alfabeto” binario 01. È formale, astratto e definito in modo preciso.
Tools in biologia computazionale
- NCBI toolkit (SKESA, programma in C++, sviluppato per assemblare genomi)
- QIIME (insieme di script in Python per l’analisi di sequenza di DNA)
La costruzione di un programma è associata alla necessità di risolvere in maniera efficiente uno o più problemi. Un programma è la descrizione di un algoritmo (insieme finito, ordinato e non ambiguo di istruzioni che servono per un calcolo o per risolvere un problema) usando un particolare linguaggio di programmazione. L’algoritmo deve essere eseguibile, non ambiguo (senza dubbia interpretazione), corretto ed efficiente (la soluzione è univoca). L’algoritmo può essere rappresentato in vari modi, grafici o testuali. Uno dei metodi grafici più utilizzati è il diagramma di flusso.
Affinché un programma sia comprensibile e quindi eseguibile da un calcolatore, è necessario che sia tradotto dal linguaggio originario al linguaggio della macchina. I linguaggi di alto livello vengono suddivisi in due categorie a seconda del metodo utilizzato per tradurre il testo delle istruzioni in linguaggio macchina: compilati (usano un compilatore) e interpreti (usano un interprete).
I compilatori traducono l’intero programma (senza eseguirlo) e producono in uscita il programma eseguibile convertito in linguaggio macchina. L’interprete traduce le istruzioni una per una: controlla l’istruzione, la traduce in linguaggio macchina e la esegue.
Il linguaggio C
Il linguaggio C è un linguaggio di programmazione di uso e portata generale, risulta particolarmente efficiente ed è un linguaggio di alto livello compilato, che può maneggiare l’attività di basso livello (risulta efficiente perché fa un uso contenuto della memoria e perché è utile per la comprensione della programmazione). È multiforme e imperativo. Ci possono essere errori tra il programma sorgente e il programma eseguibile che però vengono rilevati dal programma (come gli errori di sintassi).
La nascita di C (Dennis Ritchie) è legata al destino del sistema operativo UNIX (Ken Thompson) e prende questo nome perché rappresenta l’evoluzione del linguaggio B presente in UNIX: eredita la sintassi di B, introduce un sistema di tipi articolato e la possibilità di manipolare i bit in memoria ed inoltre rappresenta un linguaggio standardizzato (ANSI-C) e multiforma (non dipende dal sistema operativo).
Le componenti del programma in C
Il linguaggio C è imperativo e ciascun blocco è indicato da indentazione e parentesi graffe (evidenzia la struttura del programma).
- #include<stdio.h> è una direttiva di compilazione (valutata in fase di compilazione) - #INCLUDE dice al compilatore di includere il contenuto del file indicato nel punto corrente - <stdio.h> è il file contenente la libreria standard con le istruzioni per le operazioni di I/O contenuto, ad esempio la funzione printf è contenuta in <stdio.h>
- INT MAIN - Intestazione della funzione principale (main) di un programma C. L’esecuzione del programma inizia da quella del main. Le funzioni C prendono in input un insieme di argomenti e restituiscono un valore anche nullo: in questo caso le parentesi () dopo main indicano assenza di argomenti.
- PRINTF: istruzione semplice, in questo caso permette di visualizzare sullo schermo la frase scritta tra apici
- RETURN 0: se usato nella funzione main determina la fine dell’esecuzione del programma. In questo caso il programma termina restituendo il valore 0 e indica solo che il programma è terminato correttamente.
- I messaggi in verde sono dei commenti, utili per far capire agli altri quello che viene fatto nel codice.
Altro esempio: tab Fahrenheit-Celsius
Ho bisogno di variabili che rappresentano simbolicamente i dati all’interno dei programmi. Una variabile identifica una locazione (posizione) della memoria in cui può essere memorizzato un dato a cui il programma può accedere. Ogni variabile è caratterizzata da nome, tipo, indirizzo e valore; possiamo immaginarcele come contenitori etichettati e posizionati su degli scaffali.
Per usare una variabile in C va dichiarata (tutte quelle utilizzate!): si dichiara il tipo e si assegna un nome (ad esempio: int a), in questo modo si alloca una variabile di nome a e tipo int (intero).
Sintassi allocazione: <tipo_var> <nome_var> [variabili dello stesso tipo possono essere dichiarate insieme]
Alla domanda “che valore assume ^a^ dopo la sua dichiarazione con int a?” non si può rispondere. La dichiarazione di una variabile alloca spazio in memoria, ma non dice niente sul suo contenuto! È consigliato inizializzare sempre tutte le variabili, contestualmente alla loro dichiarazione ( ) o in seguito.
La sintassi da utilizzare per dichiarare una variabile è la seguente: tipo nome_variabile [=valore iniziale];
Il C prevede un numero ristretto di tipi di dati fondamentali:
- INT => un intero
- CHAR => singolo carattere
- FLOAT => un numero con virgola mobile, precisione singola
- DOUBLE => un numero con virgola mobile, precisione doppia
Ogni informazione ha una rappresentazione digitale che utilizza due stati: dal punto di vista logico, essi vengono rappresentati con 0 e 1, dal punto di vista fisico corrispondono alla assenza/presenza di tensione. L’unità digitale viene chiamata BIT da binary digit (cifra binaria) ed il BYTE è pari a 8 bit e può rappresentare 28 = 256 diverse informazioni. La scelta che facciamo del tipo di variabile dipende dalla dimensione stessa.
INT e CHAR
- INT: Un intero rappresentabile sulla macchina (segnaposto &d). L’occupazione in memoria e di conseguenza l’intervallo di rappresentazione del tipo int dipende dalla macchina su cui viene compilato il programma. La funzione predefinita sizeof () fornisce l’occupazione in byte di un qualsiasi tipo o variabile C. Maggiore è il numero di byte per un tipo, più grande è il massimo valore rappresentabile. Gli attributi short e long si applicano agli interi per denotare interi che occupano un diverso numero di byte. Se i valori da rappresentare sono positivi, l’attributo unsigned permette di usare tutti i byte per il numero positivo.
- Caratteri di un testo vengono tradotti mediante sequenze di bit tramite un codice di traduzione: ogni carattere occupa nel calcolatore 1 byte. Il codice più usato è il codice ASCII (American Standard Code for Information Interchange) a 7 bit, quindi un massimo di 128 caratteri. Distinguiamo:
- Caratteri di comando (0-31, 127): la loro scrittura su una periferica non determina una stampa, ma un’esecuzione di un comando. Per esempio “andare a capo”.
- Caratteri stampabili (32-126): sono caratteri alfanumerici (maiuscole da A a Z e cifre da 0 a 9) oppure simboli (operatori aritmetici e punteggiatura). Non c’è relazione tra il carattere cifra e la cifra stessa: ‘2’ non ha codice 2; tutti i codici delle cifre, delle minuscole e delle maiuscole sono consecutivi. [NOTA ‘5’-‘0’ = 5]
- CHAR Occupa un byte, in grado di contenere un carattere - codifica ASCII (segnaposto %c) [i caratteri si indicano tra apici ‘ ’]. I caratteri sono visti in C come interi. L’attributo const può essere applicato alla dichiarazione di qualsiasi variabile, con effetto di affermare che il suo valore non cambierà.
FLOAT e DOUBLE
- Sono tipi utilizzati per rappresentare numeri reali (precisione singola o doppia) (segnaposto %f e %lf). Hanno dimensioni differenti: double viene tipicamente rappresentato con 8 byte, float con 4. È possibile anche usare l’attributo long per variabili di tipo double.
Nel C sono importantissimi gli operatori aritmetici come + - * / % per definire e modificare il valore di variabili e combinare espressioni. [% restituisce il resto della divisione intera dei valori delle due espressioni, ad esempio 5 % 3 = 2] L’ operatore modulo non può essere applicato a variabili float e double. Gli operatori possiedono regole di precedenze e associativi che determinano come avviene la valutazione delle espressioni e si raggruppano da sinistra a destra (le regole di precedenza e associativi possono essere alterate inserendo delle parentesi tonde).
Operatori di assegnamento e incremento
- Var = expo ; il valore dell’espressione diventa il nuovo valore della variabile
- var op = expo; equivale a var = var op expr; Gli operatori op= permettono
- Operatori di incremento/decremento ++ e - -
- Possono essere postfissi (var++ e var-- incrementano/decrementano dopo l’uso del valore della variabile) oppure prefissi (prima in questo caso ++var --var). Questi operatori sono applicabili solo a variabili e comportano sempre la variazione di un’unità del valore delle variabili alle quali sono applicati. Il valore dell’intera espressione cambia a seconda che l’operatore di incremento/decremento sia postfisso o prefisso.
Operatori logici e relazionali
Nel linguaggio C i valori vero o falso vengono rappresentati numericamente, infatti non esiste un tipo booleano ma si usa il tipo int:
- 0 rappresenta falso
- 1 (o un valore diverso da 0) rappresenta vero
Se operatore logico è soddisfatto allora il valore risultante è 1, altrimenti è 0.
- ! (NOT): operatore unario. Esempio: se a rappresenta vero, !a rappresenta falso;
- && (AND): operatore binario. Esempio: se a rappresenta vero, ma b rappresenta falso, allora a && b rappresenta falso;
- || (OR): operatore binario. Esempio: se a rappresenta vero, ma b rappresenta falso, allora a || b rappresenta vero.
Hanno come operandi due espressioni e restituiscono un risultato di tipo int che può essere 0 o 1: (< > <= >=) esempio: expr1 < expr2: vero se il valore di expr1 è minore del valore di expr2.
Le istruzioni di un programma vengono normalmente eseguite in sequenza, ma la maggior parte dei programmi richiede una modifica al normale flusso sequenziale del controllo. I comandi condizionali esprimono un’alternativa fra due o più possibili prosecuzioni della compilazione sulla base della valutazione di un’espressione logica.
Lezione 2 (05/10/2020)
Le funzionalità di input/output sono demandate a librerie esterne, ad esempio la libreria stdio.h implementa un semplice modello di in/output di dati testuali. Le funzioni PRINTF e SCANF sono in essa definite: la prima viene utilizzata per visualizzare l’output, la seconda per inserire l’input. Entrambe ricevono una stringa di controllo, che può contenere le specifiche di conversione indicate con il simbolo % (segnaposto), e una serie di parametri, che possono essere ad esempio le variabili da stampare o leggere.
Funzione PRINTF
In generale è una funzione che converte, formatta e visualizza i propri argomenti sullo standard output: int printf (<stringa-formato>[,<espressione>]…). Stampa la stringa indicata nel primo parametro e restituisce il numero di caratteri emessi. Stringa-formato può contenere tre tipi di oggetti:
- Caratteri ordinati, che vengono copiati in uscita
- Metavariabili (segnaposto) del tipo %d, %c, %f, che determinano la conversione e la visualizzazione del corrispondente argomento;
- Sequenze escape, del tipo \n \t, utilizzate per specificare azioni come il ritorno a capo e le tabulazioni.
Per stampare una variabile di un determinato tipo dobbiamo utilizzare il relativo segnaposto:
- Interi: %d, %u (unsigned). Si antepone h per short e l per long;
- Reali: %f, %e (notazione scientifica), %g la più breve notazione tra standard e scientifica. Per i double non si antepone nulla, per i long double si antepone L;
- Caratteri: %c;
- Stringhe: %s
Se il carattere che segue % non dà luogo a una specifica di conversione, il comportamento del programma è indefinito e non sappiamo cosa tratti. (per stampare il simbolo di % dobbiamo mettere 2 simboli %% uno di seguito all’altro).
Tra il % e il carattere di conversione possiamo mettere alcuni flag:
- - : specifica l’allineamento a sinistra, nel suo campo, dell’argomento convertito
- + : specifica che il numero sia visualizzato con il segno;
- Un numero intero n: specifica l’ampiezza minima del campo.
- Un parametro .d : per i numeri reali specifica il numero di cifre decimali d (con arrotondamento)
Funzione SCANF
La funzione SCANF è analoga a PRINTF ma viene utilizzata per la lettura: permette di immettere input da tastiera. Anche qui abbiamo una stringa di controllo e gli specificato di formato sono quelli visti prima, tranne che per i reali:
- Double: si antepone l;
- Long double: si antepone L.
Le variabili inserite come parametri vengono usate per ricevere i valori inseriti e devono essere passate per indirizzo. Int scanf (<stringa-formato>,&var1[,&var2]…). SCANF copia ordinatamente in memoria i valori digitali da tastiera nelle variabili elencate. Le variabili nella lista delle destinazioni devono essere precedute da & che ne indica l’indirizzo. La stringa-formato contiene metavariabili e restituisce il numero di oggetti memorizzati. Se nella stringa di controllo sono presenti caratteri questi devono essere inseriti in fase di input.
[operatori ++ e --]
Il flusso di esecuzione è l’ordine in cui le istruzioni di un programma sono eseguite (eccetto contrordine, è in sequenza). Il flusso sequenziale è specificato da un gruppo di istruzioni detto blocco di istruzioni. Un blocco è un insieme di istruzioni logicamente collegate delimitato da parentesi graffe { } ; ogni istruzione per essere valida deve essere terminata dal carattere punto e virgola.
Esempio:
{ istruzione1; Istruzione2; …istruzioneN; }
Le norme di buona programmazione sono:
- Scrivere istruzioni chiare, una per riga
- Utilizzare l’indentazione dei diversi blocchi del programma per una lettura più facile del codice stesso.
- Evidenziare blocchi di istruzioni con le parentesi graffe, anche se consiste di un solo comando.
Modifiche al flusso sequenziale
Le modifiche al flusso sequenziale possono essere fatte per:
- Selezione: permette di scegliere tra alternative da eseguire in base ad una condizione (un’espressione logica combinazione di operatori logici e relazionali).
- Ripetizione: continua ad eseguire un’azione fino a quando non si verifica una condizione di termine.
Selezione semplice e complessa
- A) Il comando di selezione semplice è IF, IF - ELSE.
- B) Il comando di selezione complessa è IF-ELSE IF.
Esempio di IF:
If (espressione) { Blocco } Istruzione; // sempre eseguita
Esempio di IF - ELSE:
If (espressione) { blocco_1 } else { blocco_2 } istruzione; // sempre eseguita
Esempio di IF - ELSE IF:
If (condizione_1) { blocco_1 } else if (condizione_2) { blocco_2 } else if (condizione_3) { blocco_3 } … else { blocco_N } Istruzione; // sempre eseguita
Switch
SWITCH è un comando di selezione multipla.
Switch (espressione) { case espressione_costante: blocco_1 case espressione_costante: blocco_2 case espressione_costante: blocco_3 … default: blocco_N } Istruzione ; // sempre eseguita
Lo switch è una struttura di controllo che può risultare simile al costrutto if-else. In realtà, vi sono importanti differenze:
- Il costrutto if-else sceglie tra due diverse azioni, corrispondenti alla verità o falsità della condizione valutata. Il costrutto switch ha tante possibili azioni quanti gli N casi considerati (compreso eventualmente il caso default).
- Nel costrutto if-else si scrive esplicitamente l’espressione logica da valutare, mentre nel costrutto switch non viene scritta nessuna condizione: implicitamente, le condizioni verificate sono date dall’uguaglianza tra l’espressione indicata e ciascuna delle costanti corrispondenti ai casi.
- Nel costrutto switch è necessario terminare ogni case con un break. Non farlo può significare eseguire un codice non voluto.
Comandi iterativi
I comandi iterativi permettono di eseguire un blocco di codice per più di un’iterazione. Esistono tre costrutti:
- FOR: si sa in anticipo il numero di iterazioni;
- WHILE: non si sa in anticipo il numero di iterazioni;
- DO-WHILE: non si sa in anticipo il numero di iterazioni, ma sappiamo che deve essere eseguita almeno una iterazione.
For
FOR viene utilizzato per ripetere un blocco di istruzioni un numero specificato di volte.
Sintassi:
For (espressione_1; controllo ; espressione_2) { blocco_istruzioni; }
Semantica:
- Esegui espressione_1
- Se il controllo (booleano) è falso, salta al punto 6; se il controllo è vero, vai al punto 3
- Esegui blocco_istruzioni
- Esegui espressione_2
- Vai al punto 2.
- Continua con l’esecuzione del programma (esci dal ciclo)
Esempio:
For (i=0; i<5; i=i+1) { blocco_istruzioni; }
- Espressione_1 è tipicamente un’istruzione di assegnamento che imposta il valore iniziale della variabile di controllo del ciclo, che ha la funzione di contatore.
- Controllo è un’espressione condizionale su questa variabile e determina se il ciclo continua o termina.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.