FormularioFondamenti di Informatica: definizioni, teoremi e formule delle note, in ordine di capitolo
In questa pagina 11
I. Il calcolatore e i linguaggi
Struttura di un elaboratore
Definizione (modello di von Neumann). Un calcolatore moderno segue (con varianti) l'architettura di von Neumann: dati e istruzioni del programma stanno nella stessa memoria e una CPU li legge in sequenza.
Componente Ruolo CPU (processore) esegue le istruzioni: unità di controllo + ALU + registri Memoria centrale (RAM) contiene programma in esecuzione e dati; volatile, accesso diretto per indirizzo Memoria di massa (SSD, HDD) persistente, molto più lenta; contiene file e programmi non in esecuzione Periferiche di I/O tastiera, schermo, rete, dischi: comunicano con CPU e memoria tramite controller Bus collegamenti condivisi: bus dati, bus indirizzi, bus di controllo
Esempio. Con un bus indirizzi di linee la CPU indirizza celle da byte, cioè GiB di memoria centrale.
Formula (celle indirizzabili con bit). Sequenza di celle (tipicamente di 1 byte = 8 bit), ognuna con un indirizzo numerico. Con indirizzi a bit si indirizzano celle. Una variabile di un programma occupa una o più celle consecutive: in C l'indirizzo è esplicito (vedi Puntatori in CUn puntatore contiene un indirizzo di memoria; operatori & e *, NULL, puntatori come parametri per modificare variabili del chiamante, aritmetica dei puntatori, legame tra array e puntatori, const, puntatori a puntatori.Puntatori in C →), in Python è nascosto.
Esempio. Con indirizzi a bit si indirizzano celle.
Definizione (ciclo fetch-decode-execute)
- Fetch: legge dalla memoria l'istruzione all'indirizzo contenuto nel PC e la copia nell'IR; incrementa il PC.
- Decode: la CU interpreta il codice operativo e individua gli operandi.
- Execute: l'ALU (o l'unità di memoria/I/O) esegue l'operazione; il risultato va in un registro o in memoria.
Esempio. Con il fetch copia in IR l'istruzione all'indirizzo e porta il PC a (istruzioni di byte); il decode ne ricava codice operativo e operandi.
Rappresentazione binaria dei dati
Formula (valore di un numero in base ). Un numero in base con cifre vale
Esempio. int("2d", 16) restituisce 45 () e bin(45) restituisce '0b101101'.
Proprietà (conversioni tra basi 10, 2 e 16)
- Da base 10 a base 2: divisioni successive per 2, i resti letti dal basso verso l'alto. , quindi .
- Base 2 ↔ 16: ogni cifra esadecimale corrisponde a 4 bit (). Per questo gli indirizzi di memoria e i byte si scrivono in esadecimale.
Esempio. hex(0b00101101) restituisce '0x2d': i gruppi 0010 e 1101 sono le cifre 2 e d.
Definizione (interi senza segno e overflow). Con bit: valori da a . Somma binaria in colonna con riporto; se il risultato non sta in bit si ha overflow e il bit di riporto finale si perde (il risultato è preso modulo ).
Esempio. A bit e il risultato preso modulo è : in Python (255 + 1) % 256 restituisce 0.
Formula (valore in complemento a 2). Con bit il bit più significativo ha peso negativo :
Esempio. A bit, .
Proprietà (complemento a 2: intervallo, opposto e overflow)
- Intervallo: da a (8 bit: da a ). Asimmetrico: c'è un negativo in più.
- Bit più significativo = 1 ⇔ numero negativo. Lo zero ha una sola rappresentazione.
- Opposto di un numero: si invertono tutti i bit e si somma 1. Esempio a 8 bit: , inverto → , +1 → .
- La sommatrice è la stessa degli interi senza segno: è il motivo per cui il complemento a 2 si usa ovunque.
- Overflow: sommando due numeri dello stesso segno si ottiene un risultato di segno opposto. A 8 bit: .
Esempio. In Python (-5) & 0xFF restituisce 251, cioè 0b11111011, la rappresentazione di a bit.
Definizione (virgola mobile IEEE 754). Un reale è rappresentato come :
Formato Bit totali Segno Esponente Mantissa Cifre decimali circa singola precisione ( floatin C)32 1 8 (bias 127) 23 7 doppia precisione ( floatin Python,doublein C)64 1 11 (bias 1023) 52 15–16
Esempio. In Python 0.1 + 0.2 == 0.3 è False e 0.1 + 0.2 vale 0.30000000000000004.
Proprietà (confronto di numeri in virgola mobile). Regola: mai confrontare float con
==; usare una tolleranza (math.iscloseoabs(a - b) < eps).
Esempio. math.isclose(0.1 + 0.2, 0.3) restituisce True.
Definizione (ASCII, Unicode e UTF-8)
- ASCII: 7 bit, 128 caratteri (lettere inglesi, cifre, punteggiatura, controllo).
'A'= 65,'a'= 97,'0'= 48: le lettere e le cifre sono consecutive.- Unicode: assegna un numero (code point) a ogni carattere di ogni lingua (
'è'= U+00E8 = 232,'€'= U+20AC).- UTF-8: codifica Unicode in 1–4 byte; i caratteri ASCII occupano 1 byte ed hanno lo stesso codice. È la codifica standard per i file di testo.
Esempio. ord("A") restituisce 65, chr(97) restituisce 'a' e len("è".encode("utf-8")) restituisce 2.
Paradigmi di programmazione e linguaggi
Definizione (algoritmo, programma, sintassi e semantica)
- Algoritmo: sequenza finita di passi non ambigui che risolve un problema (trasforma un input in un output) e termina.
- Programma: algoritmo scritto in un linguaggio di programmazione, eseguibile da un calcolatore.
- Un linguaggio ha una sintassi (quali testi sono programmi validi) e una semantica (che cosa fa un programma valido).
Esempio. La ricetta «somma i numeri da 1 a 3» è un algoritmo; sum(range(1, 4)) è un programma Python che lo realizza e restituisce 6.
Definizione (paradigmi di programmazione)
Paradigma Idea Esempi Imperativo sequenza di istruzioni che modificano lo stato (variabili) C, Python, assembly Procedurale imperativo organizzato in procedure/funzioni C, Python A oggetti dati e operazioni raggruppati in oggetti; classi, incapsulamento, ereditarietà Python, Java, C++ Funzionale il programma è composizione di funzioni senza effetti collaterali; niente assegnamenti Haskell; in parte Python ( lambda,map, comprensioni)Dichiarativo / logico si descrive che cosa si vuole, non come ottenerlo SQL, Prolog
Esempio. Nello stile funzionale sum(x * x for x in [3, 4, 7, 10] if x % 2 == 0) restituisce 116, come il ciclo imperativo con accumulatore.
Definizione (compilatore e interprete)
Compilatore Interprete Quando traduce tutto il sorgente prima dell'esecuzione istruzione per istruzione durante l'esecuzione Prodotto file eseguibile in linguaggio macchina nessun eseguibile separato Errori di tipo/sintassi segnalati in compilazione molti emergono solo quando la riga viene eseguita Velocità alta più bassa Esempio gccper il Cpython
Esempio. gcc prog.c -o prog produce un eseguibile in linguaggio macchina; python prog.py compila in bytecode .pyc e lo interpreta.
II. Python: variabili, tipi ed espressioni
Eseguire un programma Python
Definizione (istruzione, espressione e blocco)
- Istruzione: unità eseguibile (assegnamento,
if,for, chiamata di funzione...). Di norma una per riga.- Espressione: combinazione di valori, variabili, operatori e chiamate che produce un valore (
2 * x + 1). Un'espressione può comparire dove serve un valore; un'istruzione no.- Blocchi definiti dall'indentazione (convenzione: 4 spazi). La riga che apre un blocco termina con
:.
Esempio. Con x = 7, 2 * x + 1 è una espressione che vale 15, mentre y = 2 * x + 1 è una istruzione che assegna 15 a y.
Definizione (tipi di errore)
- Errore di sintassi (
SyntaxError,IndentationError): il file non viene eseguito affatto.- Errore a tempo di esecuzione (eccezione:
NameError,TypeError,ZeroDivisionError...): il programma si ferma alla riga che lo provoca, con un traceback. Vedi Eccezioni in PythonEccezioni e traceback, eccezioni predefinite più comuni, try/except/else/finally, raise per segnalare errori, propagazione lungo le chiamate.Eccezioni in Python →.- Errore logico: il programma termina ma il risultato è sbagliato. Vedi Debugging e testingTipi di errore, metodo per localizzare un bug, lettura del traceback, stampe di controllo, debugger (VS Code, pdb, gdb), assert e strumenti per la memoria in C; test di unità, casi limite e partizioni, doctest e unittest.Debugging e testing →.
Esempio. 1 / 0 provoca ZeroDivisionError a tempo di esecuzione; if x > 0 senza i due punti è un SyntaxError e il file non parte.
Variabili, oggetti e riferimenti
Definizione (oggetto in Python: tipo, identità e valore). Ogni dato in Python è un oggetto con:
- tipo (
type(x)): determina valori possibili e operazioni ammesse; non cambia;- identità (
id(x)): di fatto l'indirizzo in memoria; non cambia;- valore: può cambiare solo se l'oggetto è mutabile.
Esempio. x = [1, 2] ha tipo list; x.append(3) ne cambia il valore (mutabile) ma non l'identità: id(x) resta lo stesso.
Definizione (assegnamento e variabile come nome). Una variabile è un nome che si riferisce a un oggetto, non una "scatola" con il valore dentro. L'assegnamento
x = espressione:
- valuta l'espressione a destra, ottenendo un oggetto;
- lega il nome
xa quell'oggetto (creando il nome se non esiste).
Esempio. Dopo x = 10 e y = x, x is y vale True: i due nomi si riferiscono allo stesso oggetto.
Definizione (oggetti mutabili e immutabili)
Immutabili Mutabili int,float,bool,complex,str,tuple,frozenset,Nonelist,dict,set, oggetti di classi definite dall'utente (di norma)Con un immutabile, ogni "modifica" crea un nuovo oggetto e rilega il nome:
s = "ciao" t = s s += "!" # nuovo oggetto "ciao!"; t è ancora "ciao"
Esempio. s = "ciao"; t = s; s += "!" lascia t == "ciao" e s == "ciao!", perché str è immutabile.
Definizione (aliasing). Con un mutabile, la modifica avviene sul posto ed è visibile da tutti i nomi che si riferiscono all'oggetto (aliasing):
a = [1, 2, 3] b = a # alias, non copia b.append(4) print(a) # [1, 2, 3, 4] c = a.copy() # copia (superficiale): oggetto diverso
Esempio. a = [1, 2]; b = a; b.append(3) rende a == [1, 2, 3].
Definizione (== e is)
a == b: i valori sono uguali.a is b: sono lo stesso oggetto (stessa identità).
Esempio. Con a = [1, 2] e b = [1, 2]: a == b vale True, a is b vale False.
Tipi numerici ed espressioni
Definizione (operatori aritmetici di Python)
Operatore Significato 7 op 2-7 op 2+ - *somma, differenza, prodotto 9, 5, 14 /divisione reale, sempre float 3.5-3.5//divisione intera (arrotonda verso ) 3-4%resto, stesso segno del divisore 11**potenza 49
Esempio. -7 // 2 restituisce -4, -7 % 2 restituisce 1 e 7 / 2 restituisce 3.5.
Formula (relazione tra // e %). Vale sempre
a == (a // b) * b + a % b. Attenzione: in C la divisione intera tronca verso zero e-7 / 2vale-3(vedi Tipi, operatori e controllo del flusso in CTipi interi e reali del C con dimensioni e limiti, conversioni implicite e cast, divisione intera, operatori di incremento, logici e bit a bit; if, switch, while, do-while, for, break e continue.Tipi, operatori e controllo del flusso in C →).
Esempio. Con e : .
Proprietà (tipo del risultato di un'operazione)
intopint→int(tranne/→float).- Se un operando è
float, il risultato èfloat:2 * 3.0 == 6.0.int ** intnegativo →float:2 ** -1 == 0.5.
Esempio. 2 * 3.0 restituisce 6.0 e 2 ** -1 restituisce 0.5.
Definizione (precedenza degli operatori)
**(associativo a destra:2 ** 3 ** 2 == 2 ** 9)+x,-xunari (-2 ** 2 == -4)*,/,//,%+,-- confronti
<,<=,==,!=,>,>=,in,isnotandor
Esempio. 2 ** 3 ** 2 vale 512 (associativo a destra) e -2 ** 2 vale -4.
III. Decisioni e cicli
Logica booleana e istruzione if
Definizione (operatori booleani NOT, AND, OR e XOR). Variabili con due valori (vero/falso) e tre operatori fondamentali:
(xor) F F V F F F F V V F V V V F F F V V V V F V V F
Esempio. Con vero e falso: è falso, è vero e è vero; in Python True and False vale False e True or False vale True.
Legge (di De Morgan). Leggi di De Morgan: e . Servono per negare una condizione composta:
# "x NON è in [0, 10]" not (0 <= x <= 10) x < 0 or x > 10 # equivalente per De Morgan
Esempio. Con x = 12, not (0 <= x <= 10) e x < 0 or x > 10 valgono entrambe True.
Definizione (valutazione a corto circuito).
andeorvalutano il secondo operando solo se serve:
A and B: seAè falso,Bnon viene valutato.A or B: seAè vero,Bnon viene valutato.
Esempio. Con d = 0, d != 0 and 10 / d > 1 vale False e la divisione non viene eseguita: niente ZeroDivisionError.
Proprietà (and e or restituiscono un operando).
and/orrestituiscono uno degli operandi, non necessariamente unbool:"" or "default"vale"default".
Esempio. 0 or 5 restituisce 5 e 3 and 4 restituisce 4.
Definizione (truthiness). In un contesto booleano (
if,while,and...) ogni oggetto ha un valore di verità. Sono falsi:False,None,0,0.0,"",[],(),{},set(). Tutto il resto è vero.
Esempio. bool([]) vale False, bool([0]) vale True e bool("") vale False.
Definizione (istruzione if, elif ed else)
if voto >= 18: esito = "promosso" elif voto >= 15: esito = "orale di recupero" else: esito = "respinto"
- I rami
elifsono controllati nell'ordine; si esegue solo il primo vero.elseeelifsono facoltativi.- Blocco vuoto:
pass.
Esempio. Con voto = 16 il primo test è falso, elif voto >= 15 è vero e esito diventa "orale di recupero".
Definizione (espressione condizionale)
segno = "pari" if n % 2 == 0 else "dispari" massimo = a if a >= b else b
Esempio. Con n = 8, "pari" if n % 2 == 0 else "dispari" restituisce "pari".
Ciclo while
Definizione (ciclo while)
while condizione: corpoLa condizione viene valutata prima di ogni iterazione; se è falsa all'inizio il corpo non viene mai eseguito. Il corpo deve modificare qualcosa da cui dipende la condizione, altrimenti il ciclo è infinito.
Esempio. n = 3 seguito da while n > 0: n -= 1 esegue il corpo volte e termina con n == 0.
Definizione (break, continue ed else nei cicli). ## break, continue, else
break: esce subito dal ciclo più interno.continue: salta il resto del corpo e torna al controllo della condizione.elsedopo il ciclo: eseguito se il ciclo termina senzabreak(tipico nelle ricerche).
Esempio. Con n = 15 il ciclo trova d = 3 ed esce con break stampando 15 non è primo; con n = 13 termina senza break ed esegue l'else.
Proprietà (terminazione e invariante di un ciclo)
- Terminazione: si individua una quantità intera non negativa che diminuisce a ogni iterazione (in Euclide:
b, perchéa % b < b).- Invariante di ciclo: proprietà vera prima e dopo ogni iterazione; alla fine, insieme alla condizione falsa, dà il risultato. Nel ciclo della somma con sentinella: "
sommaè la somma dei valori letti finora, escluso l'ultimo".
Esempio. Con while b != 0: a, b = b, a % b da (48, 18) si passa a (18, 12), (12, 6), (6, 0): b decresce sempre e a finale vale 6, il MCD.
Ciclo for e range
Definizione (ciclo for e iterabili)
for elemento in iterabile: corpoA ogni iterazione
elementoviene legato al successivo valore dell'iterabile (lista, stringa, tupla, range, dizionario, file...). Il ciclo termina quando gli elementi finiscono. A differenza del C, ilfordi Python non ha un contatore implicito: scorre valori.
Esempio. for c in "ciao" scorre c, i, a, o; [x * 2 for x in [3, 1, 4]] vale [6, 2, 8].
Definizione (range).
range(start, stop, step)produce gli interi dastart(incluso) astop(escluso) con passostep. Non crea una lista: genera i valori al bisogno.
Chiamata Valori range(5)0, 1, 2, 3, 4 range(2, 6)2, 3, 4, 5 range(0, 10, 3)0, 3, 6, 9 range(5, 0, -1)5, 4, 3, 2, 1 range(5, 0)nessuno
Esempio. list(range(2, 6)) vale [2, 3, 4, 5], list(range(0, 10, 3)) vale [0, 3, 6, 9] e list(range(5, 0, -1)) vale [5, 4, 3, 2, 1].
Proprietà (cicli annidati). Il ciclo interno viene eseguito per intero a ogni iterazione dell'esterno: iterazioni esterne × interne = esecuzioni del corpo (vedi Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →).
breakesce solo dal ciclo interno.
Esempio. for i in range(3): con dentro for j in range(4): esegue il corpo volte.
IV. Funzioni e moduli
Funzioni in Python
Definizione (def, parametri e return)
defcrea un oggetto funzione e lo lega al nome. Il corpo viene eseguito solo alla chiamata.- Parametri (nella definizione) e argomenti (nella chiamata).
return exprtermina la funzione e restituisce il valore; senzareturn(o conreturnvuoto) restituisceNone.- Più valori:
return q, rrestituisce una tupla, che si spacchetta conq, r = f(...)(vedi Tuple e unpackingtuple come sequenze immutabili; quando usarle al posto delle liste; spacchettamento, scambio, asterisco; tuple come valori di ritorno multipli e come chiavi di dizionario.Tuple e unpacking →).
Esempio. area_rettangolo(3, 4) restituisce 12; una funzione senza return restituisce None.
Definizione (parametri predefiniti, args e kwargs)
- I parametri con valore predefinito vanno dopo quelli senza.
*argsraccoglie gli argomenti posizionali in più in una tupla;**kwargsquelli con nome in un dizionario.
Esempio. saluta("Ada", punto=".") restituisce 'Ciao, Ada.' e somma(1, 2, 3) restituisce 6.
Proprietà (valore predefinito mutabile). Il valore predefinito viene valutato una volta sola, alla definizione:
def aggiungi(x, lista=[]): # SBAGLIATO: la stessa lista è condivisa tra le chiamate lista.append(x) return lista def aggiungi(x, lista=None): # corretto if lista is None: lista = [] lista.append(x) return lista
Esempio. Con def aggiungi(x, lista=[]), la chiamata aggiungi(1) restituisce [1] e la successiva aggiungi(2) restituisce [1, 2]: la lista è la stessa.
Definizione (passaggio degli argomenti per riferimento a oggetto). Python passa riferimenti a oggetti (call by object reference): il parametro diventa un nuovo nome per lo stesso oggetto dell'argomento.
- Riassegnare il parametro non tocca il chiamante.
- Modificare sul posto un argomento mutabile è visibile al chiamante.
Esempio. Con a, b = 5, [1] e f(a, b) che esegue n += 1 e lista.append(0), dopo la chiamata a == 5 e b == [1, 0].
Definizione (ambito delle variabili LEGB). Un nome viene cercato nell'ordine LEGB: Locale (funzione corrente) → Enclosing (funzioni che la contengono) → Globale (modulo) → Built-in.
- Ogni nome assegnato dentro una funzione è locale a quella funzione, salvo dichiarazione
global(ononlocal).- Leggere una globale da una funzione è lecito; assegnarla senza
globalcrea una locale.
Esempio. Con global contatore, incrementa() porta contatore da 0 a 1; senza, contatore += 1 provoca UnboundLocalError.
Definizione (funzioni come oggetti e lambda). Le funzioni si possono assegnare, passare come argomenti e restituire.
Esempio. applica(abs, -3) restituisce 3 e sorted(["aaa", "b", "cc"], key=len) restituisce ['b', 'cc', 'aaa'].
Moduli e libreria standard
Definizione (modulo, pacchetto e import). Un modulo è un file
.py: le funzioni, le classi e le variabili definite al suo livello più esterno diventano suoi attributi. Un pacchetto è una cartella di moduli.import math # si usa math.sqrt(2) import math as m # alias: m.sqrt(2) from math import sqrt, pi # si usa sqrt(2) direttamente from math import * # importa tutto: sconsigliato (nomi che si sovrascrivono)
Esempio. Dopo from math import sqrt, sqrt(16) restituisce 4.0; con import math si scrive math.sqrt(16).
Definizione (blocco principale di un modulo). La variabile
__name__vale"__main__"nel file avviato da riga di comando e il nome del modulo negli altri casi. Il blocco finale serve a mettere nello stesso file funzioni riusabili e codice di prova.
Esempio. Importando il modulo geometria, __name__ vale "geometria"; con python geometria.py vale "__main__".
Definizione (moduli più usati della libreria standard)
Esempio. Dopo random.seed(42), random.randint(1, 6) restituisce sempre 6: il seme rende i risultati riproducibili.
V. Stringhe, liste e tuple
Stringhe in Python
Definizione (str). Il tipo è
str: sequenza immutabile di caratteri Unicode (vedi Rappresentazione binaria dei datiBasi 2, 8 e 16; interi senza segno e in complemento a 2 con overflow; virgola mobile IEEE 754 e sue approssimazioni; caratteri ASCII, Unicode e UTF-8.Rappresentazione binaria dei dati →). Non esiste un tipo carattere: un carattere è una stringa di lunghezza 1.
Esempio. Con s = "ciao", s[0] = "C" solleva TypeError, mentre "C" + s[1:] costruisce la nuova stringa 'Ciao'.
Definizione (indici e slicing)
s = "ciao mondo" s[0], s[-1] # ('c', 'o') s[5:] # 'mondo' s[:4] # 'ciao' s[2:7] # 'ao mo' (indice 7 escluso) s[::2] # 'ca od' (passo 2) s[::-1] # 'odnom oaic' (rovesciata)
s[i]conifuori intervallo →IndexError; lo slicing invece non dà mai errore (s[100:]è"").s[i:j]ha lunghezzaj - i(per indici validi).s[0] = "C"→TypeError: le stringhe sono immutabili. Si costruisce una stringa nuova:"C" + s[1:].
Esempio. Con s = "ciao mondo", s[2:7] vale 'ao mo' (lunghezza ), s[::-1] vale 'odnom oaic' e s[100:] vale "".
Definizione (metodi principali delle stringhe)
Metodo Esempio Risultato upper(),lower(),capitalize(),title()"ciao".upper()'CIAO'strip(),lstrip(),rstrip()" x \n".strip()'x'split(sep)"a,b,,c".split(",")['a', 'b', '', 'c']split()senza argomenti" a b ".split()['a', 'b'](spazi multipli)sep.join(seq)"-".join(["a", "b"])'a-b'find(sub),index(sub)"ciao".find("a")2(finddà-1se manca,indexsollevaValueError)count(sub)"banana".count("a")3replace(old, new)"aaa".replace("a", "b", 2)'bba'startswith(),endswith()"file.py".endswith(".py")Trueisdigit(),isalpha(),isspace(),isupper()"123".isdigit()True
Esempio. "a,b,,c".split(",") restituisce ['a', 'b', '', 'c'] e "-".join(["a", "b"]) restituisce 'a-b'.
Definizione (specificatori delle f-string). Specificatori dopo
:: allineamento (<,>,^), larghezza,.ncifre decimali, tipo (dintero,fvirgola fissa,eesponenziale,%percentuale). Il metodo"{}".format(x)fa la stessa cosa con sintassi più vecchia.
Esempio. f"{27.456:.2f}" restituisce '27.46' e f"{42:05d}" restituisce '00042'.
Liste in Python
Definizione (lista mutabile). Indici, indici negativi, slicing,
len,in,+,*funzionano come per le Stringhe in Pythonstr come sequenza immutabile di caratteri Unicode; indici e slicing; operatori; metodi principali (split, join, strip, find, replace...); f-string e formattazione; confronto lessicografico.Stringhe in Python →. La differenza è che la lista è mutabile:numeri[0] = 10 # [10, 1, 4, 1, 5] numeri[1:3] = [7, 7, 7] # assegnamento a una fetta: [10, 7, 7, 7, 1, 5] del numeri[0] # [7, 7, 7, 1, 5]
Esempio. Con numeri = [3, 1, 4], numeri[0] = 10 dà [10, 1, 4] e numeri[1:3] = [7, 7, 7] dà [10, 7, 7, 7].
Proprietà (metodi delle liste e loro costo)
Operazione Effetto Costo l[i],l[i] = xlettura/scrittura per indice l.append(x)aggiunge in fondo ammortizzato l.pop()toglie e restituisce l'ultimo l.pop(i),l.insert(i, x)toglie/inserisce in posizione i, sposta gli elementi successivil.remove(x)toglie la prima occorrenza di x(ValueErrorse manca)x in l,l.index(x),l.count(x)ricerca lineare l.extend(seq)aggiunge tutti gli elementi di seql.sort()ordina sul posto, restituisce Nonel.reverse()rovescia sul posto sorted(l),reversed(l)nuova lista ordinata / iteratore al contrario min(l),max(l),sum(l)
Esempio. Con l = [3, 1, 2], sorted(l) restituisce [1, 2, 3] senza toccare l; l.sort() restituisce None e lascia l == [1, 2, 3].
Definizione (copia superficiale e profonda). Copia superficiale: nuova lista, ma gli elementi sono gli stessi oggetti. Con liste annidate non basta:
import copy m = [[1, 2], [3, 4]] s = m.copy() s[0][0] = 99 # modifica anche m[0][0]: le righe sono condivise d = copy.deepcopy(m) # copia profonda: anche le righe sono nuove
Esempio. Con m = [[1, 2], [3, 4]] e s = m.copy(), s[0][0] = 99 cambia anche m[0][0]; con copy.deepcopy(m) no.
Definizione (list comprehension).
[espressione for x in iterabile if condizione]costruisce una nuova lista.
Esempio. [x * x for x in range(5) if x % 2 == 0] restituisce [0, 4, 16].
Tuple e unpacking
Definizione (tupla). Una
tupleè una sequenza immutabile di oggetti qualsiasi.p = (3, 4) q = 3, 4 # le parentesi sono facoltative: è la virgola che crea la tupla singola = (5,) # tupla di un elemento: serve la virgola; (5) è solo l'intero 5 vuota = () t = tuple([1, 2]) # da lista
Esempio. (5,) è una tupla di un elemento mentre (5) è l'intero 5; con p = (3, 4), p[0] = 1 solleva TypeError.
Proprietà (tuple hashable come chiavi di dizionario). Le tuple sono hashable (se lo sono i loro elementi) e quindi possono essere chiavi di dizionario o elementi di insieme; le liste no (vedi Dizionari e insiemi in PythonADT mappa e insieme; dict con chiavi hashable, accesso, get, iterazione, conteggi e raggruppamenti; set e operazioni insiemistiche; tabelle hash e costo O(1) medio; Counter e defaultdict.Dizionari e insiemi in Python →).
distanze = {("Padova", "Venezia"): 37, ("Padova", "Verona"): 85}
Esempio. {("Padova", "Venezia"): 37}[("Padova", "Venezia")] restituisce 37, mentre {[1]: 2} solleva TypeError (lista non hashable).
Definizione (unpacking)
x, y = p # x = 3, y = 4 a, b = b, a # scambio: a destra si crea la tupla (b, a) nome, _, voto = ("Ada", "Lovelace", 30) # _ per un valore da ignorare primo, *resto = [1, 2, 3, 4] # primo = 1, resto = [2, 3, 4] *inizio, ultimo = "ciao" # inizio = ['c', 'i', 'a'], ultimo = 'o'Il numero di nomi deve corrispondere al numero di elementi (salvo l'asterisco): altrimenti
ValueError: too many values to unpack.
Esempio. primo, *resto = [1, 2, 3, 4] assegna primo = 1 e resto = [2, 3, 4]; a, b = b, a scambia i due valori.
VI. Input/Output ed eccezioni
Input e output da console
Definizione (flussi standard)
Flusso Python C Di default standard input sys.stdinstdintastiera standard output sys.stdoutstdoutschermo standard error sys.stderrstderrschermo
Esempio. print("errore!", file=sys.stderr) scrive su stderr: con python prog.py > out.txt il messaggio resta sullo schermo e non finisce in out.txt.
Definizione (input).
input(prompt)stampa il prompt, legge una riga fino all'invio e la restituisce senza il carattere di a capo, sempre comestr.
Esempio. Se l'utente scrive 42, input("Quanti? ") restituisce la stringa '42' e int(input("Quanti? ")) restituisce l'intero 42.
Definizione (print).
print(*oggetti, sep=" ", end="\n", file=sys.stdout)converte ogni oggetto constr(), li separa consepe termina conend.
Esempio. print(1, 2, 3, sep=", ") stampa 1, 2, 3 e print("a", "b", "c", sep="") stampa abc.
File di testo in Python
Definizione (modalità di apertura di un file)
Modalità Significato "r"lettura (default); FileNotFoundErrorse il file non esiste"w"scrittura; crea o svuota il file "a"aggiunta in fondo; crea il file se non esiste "x"creazione; errore se il file esiste già "b"aggiunto ("rb","wb")modalità binaria: si leggono bytes, nonstr
Esempio. open("out.txt", "w", encoding="utf-8") crea o svuota out.txt; con "a" il testo scritto viene aggiunto in fondo.
Definizione (istruzione with)
with open("dati.txt", encoding="utf-8") as f: for riga in f: ... # qui il file è già chiuso, anche se nel blocco è stata sollevata un'eccezione
withchiude il file automaticamente: è la forma da usare sempre.
Esempio. Con with open("dati.txt", encoding="utf-8") as f: il file f è chiuso all'uscita del blocco, anche se nel blocco c'è stata un'eccezione.
Proprietà (scrittura con write e print)
with open("out.txt", "w", encoding="utf-8") as f: f.write("prima riga\n") # write non aggiunge "\n" e accetta solo str f.write(str(42) + "\n") print("con print", 3.5, file=f) # print aggiunge spazi e "\n" e converte da solo
Esempio. f.write(str(42) + "\n") scrive 42 e va a capo; print("x", 3.5, file=f) scrive x 3.5 seguito da a capo.
Eccezioni in Python
Definizione (eccezione). Un'eccezione è un oggetto che segnala un errore a tempo di esecuzione. Quando viene sollevata, l'esecuzione normale si interrompe e l'eccezione risale lo stack delle chiamate finché qualcuno la gestisce; se nessuno la gestisce il programma termina stampando il traceback (sequenza delle chiamate, dalla più esterna alla riga dell'errore, e in fondo tipo e messaggio).
Esempio. int("abc") solleva ValueError e [1, 2][5] solleva IndexError.
Definizione (eccezioni comuni)
Eccezione Causa tipica NameErrornome non definito (refuso, variabile non ancora assegnata) TypeErroroperazione su un tipo sbagliato ( "a" + 1,None + 1, numero di argomenti errato)ValueErrortipo giusto, valore inammissibile ( int("abc"))IndexErrorindice fuori dalla sequenza KeyErrorchiave assente in un dizionario ZeroDivisionErrordivisione o modulo per zero AttributeErrormetodo o attributo inesistente ( lista.push(1))FileNotFoundErrorfile da leggere inesistente RecursionErrorricorsione troppo profonda (vedi RicorsioneFunzioni che chiamano se stesse: caso base e passo ricorsivo, stack delle chiamate, esempi su numeri, stringhe e liste, ricorsione multipla e suo costo, divide et impera, confronto con l'iterazione.Ricorsione →)
Esempio. {"a": 1}["b"] solleva KeyError, "a" + 1 solleva TypeError e 1 / 0 solleva ZeroDivisionError.
Proprietà (ordine dei rami except)
- I rami
exceptvengono provati in ordine: il primo compatibile vince.- Nel
trymettere solo le istruzioni che possono sollevare l'eccezione attesa.
Esempio. Con except ValueError prima di except ZeroDivisionError, l'input abc entra nel primo ramo e l'input 0 nel secondo.
Definizione (raise). Una funzione che riceve argomenti non validi non deve stampare un messaggio e proseguire: deve segnalare l'errore al chiamante.
Esempio. raise ValueError("n negativo") interrompe la funzione e il chiamante può intercettare l'errore con except ValueError.
File di record e controllo degli errori riga per riga
Proprietà (controllo di ogni riga di un file di record)
enumerate(f, start=1)dà il numero di riga senza contatore a mano.strip()toglie il'\n'finale e gli spazi; senza,"12\n"è accettato daintma"Rossi\n"finisce nei dati.- Una sola
trypuò coprire più conversioni; se serve distinguere il tipo di errore si usano piùexcept:splitcon troppi o pochi campi eint("abc")sollevano entrambiValueError, mentreriga.split()[1]su una riga con un solo campo sollevaIndexError.- I messaggi vanno su
sys.stderr, i risultati susys.stdout: così si possono separare con le ridirezioni della shell (2> errori.txt).int("3.5")è un errore,float("3.5")no: scegliere la conversione dal tipo del campo;float("")efloat("1,5")sonoValueError(la virgola decimale non esiste).
Esempio. Su "Rossi:12\n\nBianchi:9\nVerdi\nNeri:abc\n Gialli : 30 \n" la funzione restituisce [('Rossi', 12), ('Bianchi', 9), ('Gialli', 30)] e segnala su stderr le righe 4 e 5.
Proprietà (file inesistente: FileNotFoundError). Un file che non esiste solleva
FileNotFoundError: va gestita attorno all'open, non dentro il ciclo.
Esempio. open("manca.txt") solleva FileNotFoundError: il try va attorno all'open, non dentro il ciclo.
Proprietà (in C si controllano i valori restituiti)
fgetsrestituisceNULLa fine file; legge al piùn - 1caratteri e tiene il'\n'.strtol(s, &fine, 10)converte e mette infineil primo carattere non convertito:fine == ssignifica nessuna cifra,*fine != '\0'significa caratteri in più ("12abc").atoinon segnala errori e restituisce0per"abc": da evitare per dati non fidati.sscanf(riga, "%31[^:]:%d", nome, &costo)è più corto ma non distingue"Rossi:12abc"da"Rossi:12"; restituisce il numero di campi letti, da confrontare con quelli attesi (== 2).- Con
fscanf(f, "%d", &x)si legge un numero alla volta: il ciclo èwhile (fscanf(...) == 1), maiwhile (!feof(f)).fopenrestituisceNULLse il file non si apre: controllare sempre, e stampareperror.
Esempio. sscanf("Rossi:12", "%31[^:]:%d", nome, &costo) restituisce 2, mentre con "Rossi:abc" restituisce 1; strtol("12abc", &fine, 10) restituisce 12 e *fine è 'a'.
VII. Pattern algoritmici e complessità
Pattern algoritmici iterativi
Definizione (schema dell'accumulatore). Variabile inizializzata all'elemento neutro dell'operazione e aggiornata a ogni elemento.
somma = 0 # neutro della somma prodotto = 1 # neutro del prodotto testo = "" # neutro della concatenazione for x in valori: somma += x prodotto *= xInvariante: dopo iterazioni
sommaè la somma dei primi elementi.
Esempio. Con valori = [2, 3, 4] il ciclo dà somma = 9 e prodotto = 24.
Definizione (schema del contatore)
conta = 0 for x in valori: if x > soglia: conta += 1 # equivalente: conta = sum(1 for x in valori if x > soglia)
Esempio. Con valori = [5, 1, 8, 7] e soglia = 4, conta vale 3.
Definizione (schema del massimo e del minimo). Si inizializza con il primo elemento, non con 0 (i valori potrebbero essere tutti negativi).
def indice_massimo(v): """Indice del massimo di una lista non vuota.""" im = 0 for i in range(1, len(v)): if v[i] > v[im]: im = i return im
>restituisce la prima occorrenza del massimo,>=l'ultima.
Esempio. indice_massimo([3, 9, 2, 9]) restituisce 1, la prima occorrenza del massimo; con >= al posto di > restituirebbe 3.
Definizione (ricerca con uscita anticipata)
def contiene_negativo(v): for x in v: if x < 0: return True # basta un esempio: esco subito return False # solo dopo aver visto tutto
Esempio. contiene_negativo([4, -1, 7]) restituisce True appena vede -1; contiene_negativo([4, 1]) restituisce False.
Definizione (verifica universale)
def tutti_positivi(v): for x in v: if x <= 0: return False # basta un controesempio return TrueEquivalenti predefiniti:
any(x < 0 for x in v),all(x > 0 for x in v). Errore classico: metterereturn Truedentro il ciclo nel ramoelse, decidendo dopo il primo elemento.
Esempio. tutti_positivi([3, 0, 5]) restituisce False al secondo elemento; all(x > 0 for x in [3, 4]) restituisce True.
Definizione (finestra scorrevole). Somma di ogni blocco di
kelementi consecutivi aggiornando invece di ricalcolare ( invece di ):def max_somma_finestra(v, k): s = sum(v[:k]) migliore = s for i in range(k, len(v)): s += v[i] - v[i - k] # entra v[i], esce v[i-k] migliore = max(migliore, s) return migliore
Esempio. max_somma_finestra([1, 3, 2, 6, 1], 2) restituisce 8, la somma di 2 e 6.
Ricorsione
Definizione (funzione ricorsiva). Una funzione è ricorsiva se chiama se stessa. Si scrive come una dimostrazione per induzione:
- caso base: input abbastanza piccolo da risolvere direttamente;
- passo ricorsivo: si riduce il problema a uno (o più) problemi dello stesso tipo più piccoli e si combina il risultato.
def fattoriale(n): """n! per n >= 0.""" if n == 0: # caso base return 1 return n * fattoriale(n - 1) # passo: problema di taglia n-1Per essere corretta: ogni chiamata deve avvicinarsi al caso base e il caso base deve essere sempre raggiunto.
Esempio. fattoriale(4) restituisce 24 e si ferma sul caso base fattoriale(0) == 1.
Proprietà (limite della profondità di ricorsione). Lo stack ha dimensione limitata: in Python il limite predefinito è circa 1000 chiamate annidate (
sys.getrecursionlimit()), oltre si ottieneRecursionError. Senza caso base (o con un caso base mai raggiunto, es.fattoriale(-1)) la ricorsione è infinita. In C lo stack esaurito provoca un stack overflow (vedi Gestione della memoria in CSegmenti di memoria di un processo (codice, dati statici, stack, heap); durata delle variabili; allocazione dinamica con malloc, calloc, realloc e free; errori classici: memory leak, dangling pointer, double free, buffer overflow; strumenti di controllo.Gestione della memoria in C →).
Esempio. In Python sys.getrecursionlimit() restituisce 1000: una funzione senza caso base solleva RecursionError.
Proprietà (ricorsione multipla: chiamate di fib)
def fib(n): if n < 2: return n return fib(n - 1) + fib(n - 2)L'albero delle chiamate ricalcola gli stessi valori moltissime volte: il numero di chiamate cresce come con (esponenziale). Rimedi: versione iterativa, oppure memoizzazione (salvare i risultati già calcolati):
from functools import cache @cache def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2) # ora O(n) chiamate
Esempio. fib(5) esegue 15 chiamate; con @cache ogni valore è calcolato una volta sola.
Definizione (divide et impera). Schema: dividere il problema in sottoproblemi indipendenti, risolverli ricorsivamente, combinare. Esempi: ricerca binariaRicerca lineare su sequenze qualsiasi in O(n); ricerca binaria su sequenze ordinate in O(log n), versione iterativa e ricorsiva, invariante e errori di indice; modulo bisect.Ricerca lineare e binaria → (un sottoproblema di metà taglia) e merge sort (due sottoproblemi di metà taglia, vedi Algoritmi di ordinamentoSelection sort, insertion sort e bubble sort (quadratici), merge sort (n log n, divide et impera); stabilità, ordinamento sul posto, costo nei vari casi; sort e sorted con key.Algoritmi di ordinamento →).
def massimo(v, lo, hi): """Massimo di v[lo:hi], hi > lo.""" if hi - lo == 1: return v[lo] m = (lo + hi) // 2 return max(massimo(v, lo, m), massimo(v, m, hi))
Esempio. massimo([3, 8, 2, 9, 4], 0, 5) restituisce 9, dividendo l'intervallo [0, 5) in [0, 2) e [2, 5).
Complessità computazionale
Definizione (caso peggiore, migliore e medio). Si misura il numero di operazioni elementari (assegnamenti, confronti, operazioni aritmetiche, accessi per indice) in funzione della dimensione dell'input (lunghezza della lista, numero di cifre...). Il tempo in secondi dipende dalla macchina; la crescita al crescere di no.
- Caso peggiore: massimo numero di operazioni tra gli input di dimensione (è quello che si dà di solito: è una garanzia).
- Caso migliore: minimo.
- Caso medio: media su una distribuzione degli input.
Esempio. Nella ricerca lineare su una lista di elementi il caso migliore costa confronto e il caso peggiore .
Definizione (notazione O-grande). se esistono e tali che per ogni : cresce al più come , a meno di costanti.
Regole pratiche:
- si tiene solo il termine dominante e si tolgono le costanti: ;
- passi in sequenza: si sommano (vince il maggiore);
- cicli annidati: si moltiplicano.
Esempio. per , quindi con e .
Definizione (classi di crescita)
Classe Nome Esempio costante accesso v[i],appendlogaritmica ricerca binaria lineare ricerca lineare, somma, massimo quasi lineare merge sort, sortedquadratica selection sort, insertion sort, tutte le coppie esponenziale fibricorsivo ingenuo, tutti i sottoinsiemi
Esempio. Con e operazioni al secondo, operazioni richiedono circa s e circa minuti.
Proprietà (equazioni di ricorrenza del costo). Ricorsione: si scrive l'equazione di ricorrenza del costo .
fattoriale: → .- ricerca binaria: → .
- merge sort: → .
fibingenuo: → esponenziale.
Esempio. fattoriale(5) fa chiamate (), coerente con .
Proprietà (effetto del raddoppio di n sul tempo). Raddoppiando : un algoritmo lineare raddoppia il tempo, uno quadratico lo quadruplica.
Esempio. Se con un algoritmo quadratico impiega s, con ne servono circa .
Ricerca lineare e binaria
Definizione (ricerca lineare). Funziona su qualunque sequenza, anche non ordinata.
def ricerca_lineare(v, x): for i in range(len(v)): if v[i] == x: return i return -1
- Caso migliore , caso peggiore (elemento assente).
- Restituisce la prima occorrenza.
- È ciò che fanno
x in vev.index(x).
Esempio. ricerca_lineare([4, 7, 7, 1], 7) restituisce 1 (la prima occorrenza) e ricerca_lineare([4, 7], 5) restituisce -1.
Definizione (ricerca binaria). Richiede
vordinata. Si confrontaxcon l'elemento centrale e si scarta metà dell'intervallo a ogni passo.def ricerca_binaria(v, x): lo, hi = 0, len(v) - 1 # intervallo di ricerca [lo, hi], estremi inclusi while lo <= hi: m = (lo + hi) // 2 if v[m] == x: return m elif v[m] < x: lo = m + 1 # x, se c'è, sta a destra di m else: hi = m - 1 # x, se c'è, sta a sinistra di m return -1Invariante: se
xè inv, allora si trova inv[lo..hi]. Il ciclo termina perché a ogni giro l'intervallo si riduce di almeno un elemento (m + 1,m - 1); quandolo > hil'intervallo è vuoto exnon c'è.Costo: dopo iterazioni restano circa elementi, quindi al più iterazioni: . Con bastano 20 confronti.
Esempio. ricerca_binaria([2, 5, 8, 12, 16, 23, 38], 23) restituisce 5; con bastano al più iterazioni.
Definizione (modulo bisect)
import bisect v = [2, 5, 8, 8, 12] bisect.bisect_left(v, 8) # 2: primo indice dove inserire 8 mantenendo l'ordine bisect.bisect_right(v, 8) # 4 bisect.insort(v, 7) # inserimento ordinato: [2, 5, 7, 8, 8, 12]
bisect_left(v, x)restituisce un indiceiconv[i] == xsexc'è (ei < len(v)).
Esempio. Con v = [2, 5, 8, 8, 12], bisect_left(v, 8) restituisce 2 e bisect_right(v, 8) restituisce 4.
Algoritmi di ordinamento
Definizione (proprietà degli algoritmi di ordinamento)
- costo nel caso peggiore, migliore, medio (vedi Complessità computazionaleCosto di un algoritmo in funzione della dimensione dell'input; caso peggiore, migliore e medio; notazione O-grande e classi di crescita; come contare i passi di cicli e ricorsioni; costo delle operazioni Python.Complessità computazionale →);
- sul posto (in place): usa solo memoria aggiuntiva;
- stabile: elementi uguali restano nell'ordine relativo di partenza (conta quando si ordina per una chiave, es. studenti per voto).
Esempio. Ordinando [("b", 1), ("a", 1)] per il secondo campo con un algoritmo stabile, ("b", 1) resta prima di ("a", 1).
Proprietà (selection sort)
- Invariante:
v[:i]contiene gliielementi più piccoli, in ordine.- Confronti: sempre → in tutti i casi. Al più scambi.
- Sul posto, non stabile.
Esempio. Con v = [5, 2, 4] il selection sort esegue confronti e porta v a [2, 4, 5].
Proprietà (insertion sort)
- Invariante:
v[:i]è ordinata (ma contiene gli elementi originali div[:i], non i più piccoli).- Caso peggiore (lista al contrario) ; caso migliore (già ordinata) : molto buono su liste quasi ordinate.
- Sul posto, stabile (grazie a
>stretto nelwhile).
Esempio. L'insertion sort su [1, 2, 3, 4] esegue confronti (), su [4, 3, 2, 1] ne esegue ().
Proprietà (bubble sort). nel caso peggiore, se già ordinata; sul posto e stabile. Più lento di insertion sort in pratica.
Esempio. Con il flag, bubble_sort([1, 2, 3]) si ferma dopo un solo passo di confronti: costo .
Proprietà (merge sort: costo e memoria)
- Costo → in tutti i casi: livelli di ricorsione, ciascuno con lavoro complessivo per le fusioni.
- Memoria aggiuntiva (non sul posto); stabile.
- Questa versione restituisce una lista nuova:
v = merge_sort(v).
Esempio. merge_sort([5, 2, 4, 1]) restituisce [1, 2, 4, 5], con livelli di divisione.
Teorema (limite inferiore per l'ordinamento per confronti). Nessun ordinamento per confronti può fare meglio di nel caso peggiore.
Esempio. Per ordinare elementi servono nel caso peggiore almeno confronti.
Definizione (sort e sorted in Python). Nella pratica si usa l'ordinamento predefinito (Timsort: , stabile, su dati già ordinati).
v.sort() # sul posto, restituisce None w = sorted(v) # nuova lista sorted(v, reverse=True) # decrescente sorted(parole, key=str.lower) # ignorando maiuscole/minuscole sorted(studenti, key=lambda s: (-s[1], s[0])) # voto decrescente, poi nome
Esempio. sorted([3, 1, 2], reverse=True) restituisce [3, 2, 1] e sorted(["b", "A", "c"], key=str.lower) restituisce ['A', 'b', 'c'].
VIII. Il linguaggio C
Dal Python al C
Definizione (struttura di un programma C)
- Il programma inizia sempre da
main. Il valore restituito arriva al sistema operativo:0= terminato correttamente.- Le istruzioni finiscono con
;, i blocchi stanno tra{ }; l'indentazione serve solo alla leggibilità.- Commenti:
// fino a fine rigae/* su più righe */.#include <stdio.h>inserisce le dichiarazioni della libreria di input/output (printf,scanf, ...).
Esempio. Il programma ciao.c stampa Ciao, 3 volte e restituisce 0 al sistema operativo.
Definizione (dal sorgente all'eseguibile)
- Preprocessore: esegue le direttive
#(#includecopia il file indicato,#definesostituisce testo). Lavora sul testo, non conosce il C.- Compilatore: traduce ogni file
.cin codice macchina (file oggetto.o) e controlla sintassi e tipi.- Linker: unisce i file oggetto e le librerie in un unico eseguibile.
Esempio. gcc -c ciao.c produce l'oggetto ciao.o; gcc -o ciao ciao.o lo collega alle librerie nell'eseguibile ciao.
Definizione (differenze fondamentali tra Python e C)
Python C Variabile nome legato a un oggetto cella di memoria di tipo fisso, contiene direttamente il valore Dichiarazione nessuna obbligatoria prima dell'uso: int x;Valore iniziale assegnamento obbligatorio variabile locale non inizializzata contiene un valore indeterminato Controlli a run-time indici, tipi, overflow: eccezioni quasi nessuno: errori silenziosi o crash (comportamento indefinito) Stringhe tipo strarray di charterminato da'\0'Booleani boolint(0 falso, diverso da 0 vero);boolcon<stdbool.h>Liste listdinamicaarray di dimensione fissa Output print(x)printf("%d\n", x)con formato esplicitoMemoria automatica stack automatico; heap con malloc/free
Esempio. In C int x = 3.9; mette 3 in x; in Python x = 3.9 lega x a un float.
Definizione (printf e specificatori di formato).
printf(formato, valori...): il formato contiene testo e specificatori che devono corrispondere, per numero e tipo, ai valori passati.
Specificatore Tipo %dint%ldlong%uunsigned int%f(%.2f)double(anchefloat, promosso)%cchar%sstringa ( char *)%ppuntatore %xintero in esadecimale %%il carattere %
Esempio. printf("%d %.2f %c %x\n", 42, 3.14159, 'A', 255); stampa 42 3.14 A ff.
Tipi, operatori e controllo del flusso in C
Definizione (tipi fondamentali del C)
Tipo Dimensione tipica Intervallo tipico char1 byte −128…127 (o 0…255) short2 byte −32 768…32 767 int4 byte circa ±2,1 miliardi () long4 o 8 byte long long8 byte circa float4 byte 7 cifre significative double8 byte 15–16 cifre significative
- Le dimensioni dipendono dalla piattaforma:
sizeof(tipo)le dà in byte; i limiti sono in<limits.h>(INT_MAX,INT_MIN).unsigneddavanti a un tipo intero: solo valori non negativi, intervallo doppio ( perunsigned int).
Esempio. Sulla piattaforma di prova sizeof(int) vale 4, INT_MAX vale 2147483647 e 'A' + 1 == 'B'.
Definizione (divisione intera e reale in C)
- Tra due interi
/è divisione intera, troncata verso zero:7 / 2 == 3,-7 / 2 == -3,-7 % 2 == -1.- Basta un operando reale per avere la divisione reale:
7 / 2.0 == 3.5,(double)a / b.
Esempio. 7 / 2 vale 3, -7 / 2 vale -3, -7 % 2 vale -1 e 7 / 2.0 vale 3.5.
Definizione (conversioni implicite ed esplicite)
- Implicite: negli operatori aritmetici il tipo "più piccolo" viene promosso al più grande (
int→long→float→double);chareshortdiventanoint. Nell'assegnamento il valore si converte al tipo della variabile, con possibile perdita (int i = 3.99;→ 3).- Esplicite (cast):
(tipo)espressione.- Mescolare
inteunsignednei confronti dà sorprese:-1 < 0uè falso perché-1diventa ununsignedenorme.
Esempio. Con int a = 7, b = 2, double m1 = a / b; vale 3.0 mentre (double)a / b vale 3.5; -1 < 0u vale 0 (falso).
Definizione (incremento e decremento)
i++; /* i = i + 1 */ x = i++; /* x prende il valore VECCHIO di i, poi i aumenta */ x = ++i; /* prima i aumenta, poi x prende il nuovo valore */
Esempio. Con i = 5, x = i++ dà x = 5 e i = 6; con i = 5, x = ++i dà x = 6 e i = 6.
Definizione (operatori bit a bit).
&(and),|(or),^(xor),~(not),<<,>>(spostamenti):x << kvale per valori non negativi senza overflow;x & 1è il bit meno significativo (pari/dispari). Non confondere&con&&.
Esempio. 5 << 2 vale 20 (), 6 & 3 vale 2, 6 ^ 3 vale 5 e 7 & 1 vale 1 (numero dispari).
Proprietà (fall-through dello switch). Senza
breakl'esecuzione prosegue nelcasesuccessivo (fall-through).
Esempio. Con switch (2) senza break dopo case 2, partendo da r = 0 con case 1: r = 1; case 2: r += 10; case 3: r += 100; break; si ottiene r = 110.
Funzioni in C
Definizione (funzione C, tipo di ritorno e prototipo)
- Ogni funzione ha un tipo di ritorno e parametri con tipo.
voidcome tipo di ritorno: non restituisce niente;(void)come parametri: nessun parametro.- Una funzione va dichiarata prima dell'uso: o la si definisce sopra
main, o si scrive prima il prototipo (intestazione seguita da;).- Gli argomenti vengono convertiti al tipo dei parametri (
media(3.9, 4)passa3).- Si restituisce un solo valore. Per "restituire" più risultati si usano parametri puntatore (vedi Puntatori in CUn puntatore contiene un indirizzo di memoria; operatori & e *, NULL, puntatori come parametri per modificare variabili del chiamante, aritmetica dei puntatori, legame tra array e puntatori, const, puntatori a puntatori.Puntatori in C →) o una
struct(vedi Strutture (struct) in Cstruct per raggruppare campi di tipo diverso, typedef, accesso con punto e freccia, struct come parametri e valori di ritorno, array di struct, struct allocate dinamicamente e struct autoreferenziali.Strutture (struct) in C →).
Esempio. media(3, 4) restituisce 3.5, cioè ; media(3.9, 4) converte 3.9 in 3 e restituisce lo stesso valore.
Definizione (passaggio per valore in C). In C gli argomenti sono sempre copiati nei parametri: modificare un parametro non modifica la variabile del chiamante.
Esempio. Con void incrementa(int n) { n++; }, dopo incrementa(x) la variabile x = 5 resta 5; con void incrementa(int *p) { (*p)++; } e incrementa(&x) diventa 6.
Definizione (ambito e durata delle variabili in C)
Tipo di variabile Dove si dichiara Visibilità Durata locale (automatica) dentro un blocco { }dal punto di dichiarazione alla fine del blocco dall'ingresso all'uscita del blocco (sullo stack) parametro intestazione corpo della funzione come le locali globale fuori da ogni funzione tutto il file (dal punto di dichiarazione) intero programma locale staticdentro una funzione con staticla funzione intero programma: conserva il valore tra le chiamate
Esempio. Con static int n = 0; return ++n;, tre chiamate successive di conta_chiamate() restituiscono 1, 2, 3.
Array e stringhe in C
Definizione (array in C)
- Indici da
0aN - 1;v[i]si trova all'indirizzov + i * sizeof(int), per questo l'accesso è .- Nessun controllo degli indici:
v[N]ov[-1]leggono/scrivono memoria altrui (comportamento indefinito: valori casuali, crash, variabili vicine modificate).- Non si può assegnare un array a un altro (
w = v;è un errore) né confrontarli con==: si copia elemento per elemento.sizeof(w) / sizeof(w[0])dà il numero di elementi, ma solo dove l'array è dichiarato, non dentro una funzione che lo riceve.
Esempio. Con int w[5] = {3, 1, 4, 1, 5}; si ha w[2] == 4 e sizeof(w) / sizeof(w[0]) == 5.
Proprietà (array passato a una funzione). Il nome di un array, in quasi tutte le espressioni, vale l'indirizzo del primo elemento (vedi Puntatori in CUn puntatore contiene un indirizzo di memoria; operatori & e *, NULL, puntatori come parametri per modificare variabili del chiamante, aritmetica dei puntatori, legame tra array e puntatori, const, puntatori a puntatori.Puntatori in C →). Passandolo a una funzione si passa quell'indirizzo:
int somma(const int v[], int n) { /* equivalente a: const int *v */ int s = 0; for (int i = 0; i < n; i++) s += v[i]; return s; } void azzera(int v[], int n) { /* modifica l'array del chiamante */ for (int i = 0; i < n; i++) v[i] = 0; } /* chiamate: somma(w, 5); azzera(w, 5); */
Esempio. Con w = {3, 1, 4, 1, 5}, somma(w, 5) restituisce 14; dopo azzera(w, 5) la stessa chiamata restituisce 0, perché la funzione ha modificato l'array del chiamante.
Definizione (stringa C). Una stringa C è un array di
charche termina con il carattere nullo'\0'(valore 0). La lunghezza non è memorizzata: si conta fino a'\0'.
Esempio. Con char s[] = "ciao"; si ha sizeof(s) == 5 (cinque byte, terminatore compreso) e strlen(s) == 4.
Definizione (funzioni di string.h)
Funzione Effetto strlen(s)numero di caratteri prima di '\0'()strcpy(dst, src)copia srcindst(dstdeve essere abbastanza grande)strncpy(dst, src, n)copia al più ncaratteri (può non aggiungere'\0')strcat(dst, src)accoda srcadststrcmp(a, b)< 0,0,> 0seaprecede, è uguale, segueb(ordine dei codici)strchr(s, c),strstr(s, sub)puntatore alla prima occorrenza, o NULL
Esempio. strlen("ciao") vale 4, strcmp("abc", "abd") è negativo e dopo strcpy(nome, "Ada") si ha strcmp(nome, "Ada") == 0.
Input e output in C
Definizione (formato di printf). Forma generale
%[flag][larghezza][.precisione]tipo:-allinea a sinistra,0riempie con zeri. Specificatori principali in Dal Python al CStruttura di un programma C, compilazione con gcc (preprocessore, compilatore, linker), main e valore di ritorno, dichiarazioni con tipo statico, printf di base; corrispondenze con Python.Dal Python al C →.
Esempio. printf("%d %5d %-5d|\n", 42, 42, 42) stampa 42 42 42 |.
Proprietà (scanf: spazi bianchi, %s e valore di ritorno)
scanfsalta gli spazi bianchi prima di%d,%lf,%s(non prima di%c: per saltarli si scrive" %c").%slegge una parola (si ferma al primo spazio); la larghezza (%31s) evita di scrivere oltre l'array, lasciando posto a'\0'.- Valore di ritorno: numero di valori letti con successo, oppure
EOFa fine input. Va controllato:int a, b; if (scanf("%d %d", &a, &b) != 2) { fprintf(stderr, "input non valido\n"); return 1; } /* somma di interi fino a fine input */ int x, somma = 0; while (scanf("%d", &x) == 1) somma += x;
Esempio. sscanf("12 7", "%d %d", &a, &b) restituisce 2, mentre sscanf("12 x", "%d %d", &a, &b) restituisce 1.
Definizione (fgets).
fgets(buf, n, flusso)legge al piùn - 1caratteri fino a'\n'incluso e aggiunge'\0'; restituisceNULLa fine file o in caso di errore. Non usare maigets(eliminata dallo standard: non controlla la dimensione). Da una riga letta si estraggono i valori consscanf(riga, "%d %d", &a, &b).
Esempio. Con char buf[5], fgets(buf, 5, stdin) su abcdefgh legge al più caratteri e produce "abcd".
IX. Puntatori e memoria in C
Puntatori in C
Definizione (puntatore, operatori di indirizzo e dereferenziazione). Ogni variabile occupa celle di memoria con un indirizzo (vedi Struttura di un elaboratoreModello di von Neumann: CPU, memoria centrale, bus e periferiche; ciclo fetch-decode-execute e gerarchia di memoria.Struttura di un elaboratore →). Un puntatore è una variabile che contiene un indirizzo; il suo tipo dice a quale tipo di dato punta.
Esempio. Con int x = 10; int *p = &x; *p = 20; si ottiene x == 20.
Proprietà (aritmetica dei puntatori). Se
ppunta a un elemento di un array,p + kpunta alk-esimo elemento successivo: l'indirizzo aumenta dik * sizeof(*p)byte, non dik.
Esempio. Con int v[5] = {10, 20, 30, 40, 50}; int *p = v;, *(p + 2) vale 30 e &v[4] - &v[1] vale 3.
Proprietà (const e puntatori)
const int *p; /* puntatore a int costante: *p = 1 vietato, p = &y permesso */ int *const q = &x; /* puntatore costante: q non può cambiare, *q sì */Nei parametri
const T *documenta (e fa verificare al compilatore) che la funzione non modifica i dati puntati.
Esempio. Con int *const q = &x; l'assegnamento *q = 7 è lecito e dà x == 7, mentre q = &y è un errore di compilazione.
Gestione della memoria in C
Definizione (segmenti di memoria di un processo)
Segmento Contenuto Gestione Durata Codice (text) istruzioni macchina caricato dal sistema operativo intero programma Dati statici variabili globali e static, letterali stringaallocati all'avvio, inizializzati a zero se non indicato intero programma Stack record di attivazione: parametri, variabili locali, indirizzo di ritorno automatica: cresce a ogni chiamata, si libera al ritorno fino alla fine della funzione/blocco Heap memoria allocata dinamicamente manuale: malloc/freefinché non si chiama free
Esempio. Una variabile locale vive nello stack, un blocco ottenuto con malloc nello heap e una variabile globale nei dati statici.
Definizione (malloc, calloc, realloc e free)
Funzione Effetto malloc(byte)alloca bytebyte non inizializzati; restituiscevoid *oNULLcalloc(n, dim)alloca nelementi didimbyte, azzeratirealloc(p, nuovi_byte)ridimensiona il blocco; può spostarlo e restituire un nuovo indirizzo (copiando il contenuto); NULLse fallisce, nel qual caso il vecchio blocco resta validofree(p)libera un blocco ottenuto da malloc/calloc/realloc;free(NULL)non fa niente
Esempio. malloc(4 * sizeof(int)) riserva 16 byte non inizializzati; calloc(4, sizeof(int)) riserva gli stessi 16 byte azzerati, quindi v[2] == 0.
Proprietà (array che cresce raddoppiando la capacità). Raddoppiando la capacità, il costo totale delle copie su
ninserimenti è : è lo stesso meccanismo delle Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →.
Esempio. Partendo da capacità e raddoppiando, inserimenti copiano in tutto elementi.
Strutture (struct) in C
Definizione (struct). Una
structraggruppa in un unico tipo più campi di tipo anche diverso (come una tupla con nomi o un semplice oggetto Python con soli attributi).
Esempio. Con struct punto p = {1.0, 2.0}; p.x = 4.0; si ha p.x == 4.0 e p.y == 2.0.
Proprietà (assegnazione e dimensione di una struct)
- Una
structsi può assegnare (q = p;copia tutti i campi, anche gli array interni) ma non confrontare con==.sizeof(Studente)può essere maggiore della somma dei campi per l'allineamento in memoria (padding).
Esempio. Con Studente (char nome[32], int, double) sizeof(Studente) vale 48, più dei byte dei campi per il padding; q = p copia tutti i campi.
Definizione (struct passate a funzioni). Una
structpassata per valore viene copiata per intero; per modificarla o per evitare la copia di strutture grandi si passa un puntatore. Con un puntatore i campi si raggiungono con->:ps->mediaequivale a(*ps).media.
Esempio. medio applicata ai punti e restituisce per valore la struct .
Definizione (struct autoreferenziale). Una
structnon può contenere se stessa, ma può contenere un puntatore al proprio tipo. È la base delle strutture collegate:typedef struct nodo { int valore; struct nodo *succ; /* serve il nome "struct nodo": il typedef non è ancora completo */ } Nodo;
Esempio. Con Nodo n1 = {1, NULL}; Nodo n2 = {2, &n1}; si ha n2.succ->valore == 1.
Ordinare e cercare in C - qsort, bsearch e puntatori a funzione
Definizione (puntatore a funzione). Il nome di una funzione, senza parentesi, è il suo indirizzo. Un puntatore a funzione si dichiara indicando firma e nome tra parentesi tonde con
*:#include <stdio.h> int somma(int a, int b) { return a + b; } int prodotto(int a, int b) { return a * b; } int applica(int (*f)(int, int), int x, int y) { /* f: puntatore a funzione */ return f(x, y); /* chiamata tramite il puntatore */ } int main(void) { printf("%d %d\n", applica(somma, 3, 4), applica(prodotto, 3, 4)); /* 7 12 */ return 0; }
Esempio. Con int applica(int (*f)(int, int), int x, int y), applica(somma, 3, 4) restituisce 7 e applica(prodotto, 3, 4) restituisce 12.
Definizione (qsort e funzione di confronto)
base: indirizzo del primo elemento;n: numero di elementi;dim: dimensione in byte di un elemento (sizeof v[0]).cmp(a, b)riceve puntatori agli elementi (comeconst void *) e restituisce un numero< 0,0o> 0se*ava prima, pari o dopo*b. Si converte il puntatore al tipo vero dell'elemento.
Esempio. qsort(v, 4, sizeof v[0], cmp_int) su {5, -2, 9, 0} lascia {-2, 0, 5, 9}.
Definizione (bsearch). Ricerca binaria (Ricerca lineare e binariaRicerca lineare su sequenze qualsiasi in O(n); ricerca binaria su sequenze ordinate in O(log n), versione iterativa e ricorsiva, invariante e errori di indice; modulo bisect.Ricerca lineare e binaria →) in : restituisce il puntatore all'elemento trovato o
NULL. L'array deve essere già ordinato con lo stessocmp. La chiave è un puntatore a un elemento "di prova" (anche unVocecon solo il campo chiave riempito); l'indice si ricava con la differenza di puntatori.
Esempio. Su u = {-2, 0, 5, 9} ordinato con cmp_int, bsearch(&k, u, 4, sizeof u[0], cmp_int) con k = 9 restituisce u + 3, cioè l'indice 3.
X. Tipi di dato astratti
Tipi di dato astratti
Definizione (tipo di dato astratto). Un tipo di dato astratto (ADT, abstract data type) è un insieme di valori più un insieme di operazioni su di essi, descritto solo da che cosa fanno le operazioni, non da come sono realizzate.
- Interfaccia: nomi, parametri, risultati e comportamento atteso (con precondizioni) delle operazioni.
- Implementazione: rappresentazione concreta dei dati (array, struct, lista collegata...) e codice delle operazioni.
- Incapsulamento: chi usa l'ADT accede ai dati solo tramite le operazioni. Si può cambiare l'implementazione senza toccare il codice che la usa.
Esempio. Una pila realizzata con una lista: s = []; s.append(1); s.append(2); s.pop() restituisce 2 (LIFO); cambiando la rappresentazione l'interfaccia push/pop resta la stessa.
Definizione (classe, costruttore e metodi speciali)
Frazione(1, 2)crea l'oggetto e chiama__init__conself= il nuovo oggetto.a.numeratore()equivale aFrazione.numeratore(a).- I metodi "speciali" con doppio trattino basso (
__add__,__eq__,__str__,__len__,__lt__, ...) collegano l'ADT agli operatori e alle funzioni predefinite.- Python non impedisce l'accesso agli attributi: il prefisso
_è una convenzione di incapsulamento.- L'invariante della classe (qui: frazione ridotta,
den > 0) è stabilito dal costruttore e mantenuto da ogni operazione; per questo i dati non vanno modificati dall'esterno.
Esempio. Frazione(1, 2) + Frazione(1, 3) stampa 5/6 e Frazione(2, 4) == Frazione(1, 2) vale True: il costruttore riduce 2/4 a 1/2.
Classi ed ereditarietà in Python
Definizione (attributi di classe e di istanza, metodi di classe e statici)
- Un attributo di classe è l'analogo di un attributo
staticdi altri linguaggi: si legge anche da un'istanza, maa.n_conti = 7crea un attributo di istanza che oscura quello di classe senza modificarlo.@staticmethodè una funzione "dentro" la classe solo per raggruppamento;@classmethodserve per costruttori alternativi, che con l'ereditarietà creano la sottoclasse giusta.- Un metodo di istanza ha sempre un parametro implicito,
self; se manca nella definizione, la chiamataa.versa(5)fallisce conTypeError.
Esempio. Dopo a = Conto("Ada", 100) e b = Conto.vuoto("Bob") si ha Conto.n_conti == 2; Conto.importo_valido(-5) vale False.
Definizione (proprietà con @property).
@propertyfa sembrare un attributo un metodo con controllo:class Cerchio: def __init__(self, r): self.raggio = r # passa dal setter @property def raggio(self): return self._r @raggio.setter def raggio(self, r): if r < 0: raise ValueError("raggio negativo") self._r = r @property def area(self): # sola lettura: calcolata ogni volta return 3.14159 * self._r ** 2
Esempio. Cerchio(2).area restituisce 12.56636, mentre Cerchio(-1) solleva ValueError("raggio negativo").
Definizione (ereditarietà e override).
class Sotto(Sopra): la sottoclasse eredita attributi e metodi, può aggiungerne e ridefinirli (override: stesso nome, stessa interfaccia).super()raggiunge la versione della superclasse.
Esempio. Dopo tre s.inserisci(g, 1) su una Squadra, s.taglia() vale 3 e un quarto inserisci("d", 1) solleva ValueError("squadra al completo").
Definizione (polimorfismo). Lo stesso messaggio, comportamenti diversi: il metodo eseguito dipende dalla classe effettiva dell'oggetto, scelta a tempo di esecuzione.
Esempio. [round(f.area(), 2) for f in (Quadrato(2), Cerchio2(1))] restituisce [4, 3.14]: ogni oggetto esegue il proprio area.
Pila e coda
Definizione (pila, LIFO). Last In, First Out: esce per primo l'ultimo elemento inserito.
Operazione Effetto push(x)inserisce xin cimapop()toglie e restituisce l'elemento in cima (errore se vuota) top()/peek()restituisce l'elemento in cima senza toglierlo is_empty(),size()Tutte con una buona implementazione.
Esempio. Con pila = [], dopo pila.append(3) e pila.append(5), pila.pop() restituisce 5 e lascia pila == [3].
Definizione (controllo delle parentesi bilanciate con una pila)
def bilanciata(s): coppie = {")": "(", "]": "[", "}": "{"} pila = [] for c in s: if c in "([{": pila.append(c) elif c in coppie: if not pila or pila.pop() != coppie[c]: return False return not pila # alla fine non devono restare aperte bilanciata("a[(b)+c]") # True bilanciata("(]") # False
Esempio. bilanciata("a[(b)+c]") restituisce True, bilanciata("(]") restituisce False.
Definizione (coda, FIFO). First In, First Out: esce per primo l'elemento inserito da più tempo.
Operazione Effetto enqueue(x)inserisce in fondo dequeue()toglie e restituisce il primo (errore se vuota) first()restituisce il primo senza toglierlo is_empty(),size()
Esempio. Con coda = deque(), dopo append("a") e append("b"), popleft() restituisce 'a' e coda[0] vale 'b'.
Definizione (coda circolare su array). Due indici,
testa(primo elemento) e numero di elementin; le posizioni "girano" con il modulo, così non serve spostare elementi.
Esempio. Con CAP = 8, testa = 6 e n = 3, enqueue scrive in ; dopo un dequeue si ha testa = (6 + 1) % 8 = 7.
Liste concatenate
Definizione (lista concatenata). Una lista concatenata (linked list) è una sequenza di nodi; ogni nodo contiene un valore e il puntatore al nodo successivo. Il primo nodo è indicato da un puntatore
testa; l'ultimo ha successivoNULL.
Esempio. Con tre inserisci_testa di 1, 7, 3 la lista è 3 -> 7 -> 1; lunghezza restituisce 3 e cerca(testa, 5) restituisce NULL.
Proprietà (liste concatenate e array: costi a confronto)
Operazione Array (o lista Python) Lista concatenata accesso al k-esimo elemento: bisogna scorrere inserimento/rimozione in testa (spostamenti) inserimento/rimozione dopo un nodo noto inserimento in fondo ammortizzato , oppure tenendo un puntatore alla coda ricerca ( se ordinato) , anche se ordinata memoria contigua, compatta un puntatore in più per elemento
Esempio. Con elementi, inserire in testa richiede spostamenti in un array e una sola operazione in una lista concatenata.
Definizione (inserimento in testa). La funzione deve modificare il puntatore
testadel chiamante: o lo restituisce, o riceve un puntatore a puntatore (Puntatori in CUn puntatore contiene un indirizzo di memoria; operatori & e *, NULL, puntatori come parametri per modificare variabili del chiamante, aritmetica dei puntatori, legame tra array e puntatori, const, puntatori a puntatori.Puntatori in C →)./* versione che restituisce la nuova testa */ Nodo *inserisci_testa(Nodo *testa, int x) { Nodo *n = malloc(sizeof *n); if (n == NULL) return testa; /* allocazione fallita: lista invariata */ n->valore = x; n->succ = testa; return n; } /* versione con puntatore a puntatore */ void inserisci_testa2(Nodo **pt, int x) { Nodo *n = malloc(sizeof *n); if (n == NULL) return; n->valore = x; n->succ = *pt; *pt = n; }
Esempio. Su 3 -> 7 -> 1, inserisci_testa(testa, 9) restituisce la nuova testa della lista 9 -> 3 -> 7 -> 1.
Definizione (rimozione di un nodo da una lista).
/* rimuove la prima occorrenza di x; restituisce la nuova testa */ Nodo *rimuovi(Nodo *testa, int x) { Nodo *prec = NULL, *cur = testa; while (cur != NULL && cur->valore != x) { prec = cur; cur = cur->succ; } if (cur == NULL) return testa; /* non trovato */ if (prec == NULL) testa = cur->succ; /* era il primo */ else prec->succ = cur->succ; /* scavalca cur */ free(cur); return testa; }
Esempio. Su 3 -> 7 -> 1, rimuovi(testa, 7) restituisce la testa della lista 3 -> 1 e libera il nodo 7; con rimuovi(testa, 5) la lista resta invariata.
Dizionari e insiemi in Python
Definizione (ADT mappa e insieme)
- Mappa (dizionario): associa a ogni chiave un valore; operazioni: inserire/aggiornare una coppia, cercare il valore di una chiave, togliere una chiave, scorrere le coppie.
- Insieme: collezione senza duplicati e senza ordine; operazioni: aggiungere, togliere, verificare l'appartenenza, unione, intersezione, differenza.
Esempio. Con voti = {"Ada": 30, "Alan": 27}, voti["Ada"] = 28 aggiorna la coppia senza duplicare la chiave; {3, 1, 4, 1, 5} vale {1, 3, 4, 5}.
Definizione (dict: operazioni principali)
voti = {"Ada": 30, "Alan": 27} vuoto = {} # dizionario vuoto (NON un insieme) d = dict(a=1, b=2) quadrati = {x: x * x for x in range(5)} # dict comprehension voti["Grace"] = 29 # inserisce voti["Ada"] = 28 # aggiorna: una chiave compare una volta sola voti["Ada"] # 28 voti["Bob"] # KeyError voti.get("Bob") # None voti.get("Bob", 0) # 0: valore di default "Ada" in voti # True: cerca tra le CHIAVI del voti["Alan"] voti.pop("Grace") # toglie e restituisce 29 len(voti)
Esempio. Con voti = {"Ada": 28, "Alan": 27}: voti.get("Bob", 0) restituisce 0, "Ada" in voti vale True e voti["Bob"] solleva KeyError.
Proprietà (chiavi hashable). Le chiavi devono essere hashable (immutabili):
int,float,str,tupledi immutabili. Unalistnon può essere chiave (TypeError: unhashable type); i valori invece possono essere qualsiasi cosa.
Esempio. {(1, 2): "a"}[(1, 2)] restituisce 'a', mentre {[1]: 2} solleva TypeError.
Definizione (schemi tipici con i dizionari)
# conteggio delle frequenze freq = {} for parola in testo.split(): freq[parola] = freq.get(parola, 0) + 1 # raggruppamento per_iniziale = {} for nome in nomi: per_iniziale.setdefault(nome[0], []).append(nome) # chiave con valore massimo piu_frequente = max(freq, key=freq.get) # inversione (valori distinti) inverso = {v: k for k, v in d.items()}
Esempio. Sulle parole a b a, freq[parola] = freq.get(parola, 0) + 1 dà {'a': 2, 'b': 1}; Counter("mississippi").most_common(2) dà [('i', 4), ('s', 4)].
Definizione (operazioni sugli insiemi)
Operazione Operatore Metodo unione a | ba.union(b)intersezione a & ba.intersection(b)differenza a - ba.difference(b)differenza simmetrica a ^ ba.symmetric_difference(b)sottoinsieme a <= ba.issubset(b)
Esempio. Con a = {1, 2, 3} e b = {3, 4}: a | b è {1, 2, 3, 4}, a & b è {3}, a - b è {1, 2} e a ^ b è {1, 2, 4}.
Proprietà (dict e set come tabelle hash).
dictesetsono tabelle hash: un array di celle in cui la posizione di una chiave è calcolata dahash(chiave)modulo la dimensione dell'array. Ricerca, inserimento e rimozione costano quindi in media, indipendentemente dal numero di elementi; due chiavi nella stessa cella (collisione) vengono gestite cercando un'altra cella. Quando la tabella si riempie viene ingrandita e le chiavi ridistribuite (costo ammortizzato, come l'appenddelle liste).Per questo le chiavi devono essere immutabili: se una chiave cambiasse dopo l'inserimento, il suo hash cambierebbe e non verrebbe più trovata.
listset/dictx in ...medio ordine e indici sì no (il dict mantiene l'ordine di inserimento) duplicati ammessi no (chiavi uniche)
Esempio. Con un milione di elementi x in lista può scorrerli tutti, mentre x in set(lista) costa in media ; una lista come chiave solleva TypeError.
Realizzare contenitori su array e liste
Proprietà (raddoppio della capacità: costo ammortizzato). Costo: una
appendche ridimensiona costa , ma succede dopo inserimenti; in inserimenti le copie totali sono , quindi ammortizzato per inserimento. Con un incremento costante (capacità o ) le copie sono invece in totale. È il meccanismo dilistin Python e direallocin C (Gestione della memoria in CSegmenti di memoria di un processo (codice, dati statici, stack, heap); durata delle variabili; allocazione dinamica con malloc, calloc, realloc e free; errori classici: memory leak, dangling pointer, double free, buffer overflow; strumenti di controllo.Gestione della memoria in C →).
Esempio. Con capacità iniziale e raddoppio, append copiano in tutto elementi, meno di .
Proprietà (costi del dizionario su array ordinato). Costi:
cerca;inserisciecancellaper gli spostamenti (la ricerca del posto è comunque ). Su un array non ordinato la ricerca è , e anche l'inserimento lo è, perché prima bisogna verificare che la chiave non ci sia già. La tabella hash didict(Dizionari e insiemi in PythonADT mappa e insieme; dict con chiavi hashable, accesso, get, iterazione, conteggi e raggruppamenti; set e operazioni insiemistiche; tabelle hash e costo O(1) medio; Counter e defaultdict.Dizionari e insiemi in Python →) dà medio per tutte le operazioni ma non mantiene l'ordine delle chiavi.
Esempio. Inserendo ("Zeno", 3), ("Ada", 1), ("Mia", 2), ("Ada", 9) si ottengono le chiavi ['Ada', 'Mia', 'Zeno'] con valori [9, 2, 3] e cerca("Mia") restituisce 2.
Proprietà (coda doppia su array circolare). Tutte le operazioni sono tranne il raddoppio, ammortizzato . In C la formula è identica, ma attenzione:
(t - 1) % capcont = 0dà in C (il resto ha il segno del dividendo); si scrive(t + cap - 1) % cap. L'alternativa è la lista doppiamente concatenata (Liste concatenateLista concatenata semplice: nodi allocati dinamicamente collegati da puntatori; inserimento e rimozione in testa, scorrimento, ricerca, inserimento ordinato, deallocazione; confronto dei costi con l'array; versione in Python.Liste concatenate →) con puntatori a testa e coda: senza raddoppi, ma un puntatore in più per elemento.collections.dequeè già una coda doppia.
Esempio. Con cap = 4 e t = 0, add_first porta la testa a (0 - 1) % 4 = 3 in Python; in C serve (t + cap - 1) % cap, perché -1 % 4 vale -1.
Proprietà (coda con priorità a livelli). A parità di priorità l'ordine è FIFO. Con priorità arbitrarie si usa uno heap (
heapq): inserimento ed estrazione .
Esempio. Dopo inserisci(2, "spesa"), inserisci(0, "esame"), inserisci(0, "tesi"), tre estrai() restituiscono esame, tesi, spesa.
XI. Debugging e testing
Debugging e testing
Definizione (tipi di errore)
Tipo Quando emerge Esempio sintattico prima dell'esecuzione (Python) / in compilazione (C) :mancante,;mancantedi tipo a run-time in Python, in compilazione in C (in parte) "a" + 1a run-time durante l'esecuzione divisione per zero, indice fuori intervallo, NULLdereferenziatologico mai segnalato: risultato sbagliato <invece di<=, accumulatore inizializzato male
Esempio. if x > 0 senza i due punti è un errore sintattico, "a" + 1 di tipo, 1 / 0 a run-time e < al posto di <= un errore logico.
Definizione (assert).
assert condizione, "messaggio"interrompe il programma se la condizione è falsa. Serve a verificare ipotesi interne (precondizioni, invarianti di ciclo), non a validare l'input dell'utente.def media(v): assert len(v) > 0, "lista vuota" return sum(v) / len(v)
Esempio. Con def media(v): assert len(v) > 0, "lista vuota", la chiamata media([]) solleva AssertionError: lista vuota.
Definizione (test). Un test esegue il codice su un input con risultato noto e confronta. I test mostrano la presenza di errori, non ne dimostrano l'assenza: vanno scelti bene.
Esempio. assert ricerca_binaria([1, 3, 5, 7], 4) == -1 è un test: passa in silenzio se la funzione restituisce -1 e solleva AssertionError altrimenti.
Definizione (doctest). Con
doctestgli esempi nella docstring diventano test (python -m doctest modulo.py -v):def quadrato(x): """ >>> quadrato(3) 9 >>> quadrato(-2) 4 """ return x * x
Esempio. python -m doctest modulo.py -v esegue quadrato(3) e quadrato(-2) e verifica che restituiscano 9 e 4.