# --- # jupyter: # jupytext: # formats: ipynb,py:percent # text_representation: # extension: .py # format_name: percent # format_version: '1.3' # jupytext_version: 1.14.0 # kernelspec: # display_name: Python 3 (ipykernel) # language: python # name: python3 # --- # %% [markdown] toc=true slideshow={"slide_type": "notes"} #

Table of Contents

#
# %% [markdown] slideshow={"slide_type": "slide"} # # # Fondamenti di Programmazione # # # **Andrea Sterbini** # # lezione 5 - 17 ottobre 2022 # %% [markdown] slideshow={"slide_type": "slide"} # ## Quesito con la Susi 1 # # ![susi-1.jpg](susi-1.jpg) # %% slideshow={"slide_type": "fragment"} # Per calcolare la 200° cifra: # costruisco una stringa con le cifre (fin dove? 200?) # estraggo il 200° carattere def cerca_carattere(N): testo = '' i = 1 while len(testo) < N: testo += str(i) i += 1 #print(testo) return testo[N-1] # Semplice ma crea tante stringhe cerca_carattere(2000) # %% slideshow={"slide_type": "fragment"} # Seconda idea # Per calcolare la 200° cifra: # trovo in che numero sta la 200° cifra # e quante cifre sono rimaste da scandire # la tiro fuori def cerca_senza_stringhe(): numero, mancanti = cerca_numero() print(numero,mancanti) testo = str(numero) return testo[mancanti-1] # per trovare in che numero sta la 200° cifra def cerca_numero(): # scandisco i numeri da 1 in poi cifre = 200 for i in range(1, 200): # calcolo il numero di cifre X del numero corrente X = logaritmo_boh(i) # se è < del numero di cifre rimaste if X < cifre: # tolgo X dal numero di cifre ancora da scandire cifre -= X # incremento il numero I # altrimenti else: # l'ho trovato, lo ritorno # assieme al numero di cifre ancora da scandire return i, cifre import math from math import log10 # per calcolare il numero di cifre di un numero def logaritmo_boh(N): # ne calcolo il logaritmo in base 10 # lo tronco ad intero # gli sommo 1 return int(math.log10(N))+1 cerca_senza_stringhe() # %% [markdown] slideshow={"slide_type": "fragment"} # ## Moduli e import # # Per caricare in memoria funzionalità realizzate in altri file # # ```python # import modulo # solo 'modulo' viene inserito nel namespace # # chiamare la funzione # modulo.funzione(argomenti) # # from modulo import funzione # solo 'funzione' -> namespace # # chiamare la funzione # funzione(argomenti) # ``` # %% [markdown] slideshow={"slide_type": "slide"} # ## Contenitori # %% [markdown] slideshow={"slide_type": "subslide"} # ### Operazioni sulle liste (list) # - **`elemento in L`** True se l'elemento è presente in L # - **`L1 + L2`** nuova lista concatenazione di L1 e L2 # - **`L1 * N`** replicazione N volte e concatenazione # - **`L.append(elemento)`** aggiunta di un elemento # - **`L[i]`** elemento all'indice **i** (lettura o assegnamento) # - **`L.pop()`** estrazione distruttiva dell'ultimo elemento (ERR se vuota) # - **`L.pop(i)`** estrazione distruttiva dell'elemento all'indice **i** (ERR se **i** non esiste) # - **`L.insert(i, elemento)`** inserisce l'elemento all'indice i (o in cima o in fondo) # %% slideshow={"slide_type": "fragment"} # Esempi di operazioni sulle liste L1 = [ 1, 2, 3, 4, 5, ] L2 = [ 11, 22, 33, ] L1 + L2 print(L1.pop(2)) L1.insert(78, 55) L1 L2[-2:] = [] L2 # %% [markdown] slideshow={"slide_type": "subslide"} # ### Altre operazioni sulle liste (list) # - **`L.remove(elemento)`** elimina la prima occorrenza dell'elemento (ERR se non presente) # - **`L.index(elemento)`** trova il primo indice in cui c'è l'elemento (ERR se non presente) # - **`L.count(elemento)`** conta l'elemento # - **`L.reverse()`** rovesciamento distruttivo della lista # - **`L.sort()`** ordinamento distruttivo della lista # %% slideshow={"slide_type": "fragment"} # Altri esempi di operazioni sulle liste L = [10, 43, 92, 1, -43, 90, -200, int('1') ] L.count(3) L.index(-43) L.sort() L # %% [markdown] slideshow={"slide_type": "subslide"} # ### Operazioni sulle tuple # - **`elemento in T`** True se l'elemento è presente in T # - **`T1 + T2`** nuova tupla concatenazione di T1 e T2 # - **`T * N`** replicazione N volte e concatenazione # - **`T[i]`** accesso all'elemento all'indice i (SOLO lettura) # - **`L.index(elemento)`** trova il primo indice in cui c'è l'elemento (ERR se non presente) # - **`L.count(elemento)`** conta l'elemento # %% slideshow={"slide_type": "fragment"} # Esempi di operazioni sulle tuple T1 = 1, 2, 3, 4 T2 = 24, 52, 67, 31 T1 + T2 T1[1:3] # %% [markdown] slideshow={"slide_type": "subslide"} # ### Operazioni sugli insiemi (set) # - **`elemento in S`** True se elemento è presente in S # - **`S1 | S2`** unione (or) # - **`S1 & S2`** intersezione (and, elementi in comune) # - **`S1 - S2`** insieme degli el. di S1 che non sono in S2 # - **`S1 ^ S2`** insieme degli elementi NON in comune (xor) # - **`S.pop()`** estrazione distruttiva di un el. (ERR if empty) # - **`S.add(elemento)`** aggiunta di un elemento # - **`S.remove(elemento)`** rimozione di un el. (ERR if missing) # - **`S1.update(S2)`** come **`S1 |= S2`** (distruttiva) # %% slideshow={"slide_type": "fragment"} # Esempi di operazioni sugli insiemi S1 = set(range(3,101,3)) # insieme dei multipli di 3 in [1..100] # %pprint S1 # %% [markdown] slideshow={"slide_type": "subslide"} # ### Operazioni sui dizionari (dict) # - **`key in D`** True se la chiave è presente # - **`D[key]`** accesso al valore con chiave key (R/W) (ERR se non presente) # - **`D.keys()`** elenco delle chiavi (che sono uniche) # - **`D.values()`** elenco dei valori (con duplicati) # - **`D.items()`** elenco delle coppie (chiave, valore) # - **`D.popitem()`** torna l'ultima coppia (k,v) # e la rimuove # - **`D1 | D2`** nuovo dizionario con tutte le coppie
di D1 **e** di D2 (prese nell'ordine) # - **`D1.update(D2)`** modifica D1 # aggiungendo le coppie (K,V) di D2 nell'ordine # %% slideshow={"slide_type": "fragment"} # Esempi di operazioni sui dizionari D1 = { 1: 'uno', 2: 'due', 3:'tre' } D2 = { 11: 'undici', 2: 'ventidue', 33:'trentatre' } D1 | D2 # %% [markdown] slideshow={"slide_type": "subslide"} # ### Altre operazioni sui dizionari # - **`D.get(key, default)`** se la chiave è presente ne torna il valore, altrimenti torna il valore di default # - **`D.setdefault(key, default)`** se la chiave è presente ne torna il valore # altrimenti la inserisce col valore di default (e lo torna) # - **`D.pop(key, default)`** se la chiave è presente ne torna il valore e la rimuove, altrimenti torna il valore di default (o ERRORE se no default) # - **`D.fromkeys(keys, value)`** costruisce un dizionario con le chiavi fornite, tutte associate allo stesso valore indicato # %% slideshow={"slide_type": "fragment"} # Esempi di operazioni sui dizionari dict.fromkeys('ABCADEF', 0) help(dict) # %% [markdown] slideshow={"slide_type": "slide"} # ## Quesito con la Susi 2 # ![susi-2.png](susi-2.png) # # - un amico della Susi ha fatto un esame a crocette True/False con 100 domande # - le risposte giuste **True** erano tutte quelle **multiple di 3** e il resto **False** # - ma lui ha marcato **False** tutte le domande **multiple di 4** ed il resto **True**. # Quante ne ha azzeccato? # %% slideshow={"slide_type": "subslide"} # per contare le risposte giuste def conta_azzeccate(): # inizializzo una variabile conteggio a 0 azzeccate = 0 # scandisco i numeri da 1 a 100 for i in range(1,101): # calcoliamo la risposta corretta corretta = risposta_corretta(i) # calcoliamo la risposta data data = risposta_data(i) # se la riposta data è uguale alla risposta corretta if corretta == data: # incremento il conteggio azzeccate += 1 # alla fine torno il conteggio return azzeccate # %% slideshow={"slide_type": "subslide"} # per calcolare la risposta corretta def risposta_corretta(X): # se il numero è divisibile per 3 # la risposta corretta è True # altrimenti # la risposta corretta è False return X%3 == 0 # %% slideshow={"slide_type": "subslide"} # per calcolare la risposta data def risposta_data(X): # se il numero è divisibile per 4 # lui ha risposto False # altrimenti # lui ha risposto True return X % 4 != 0 # Noooo Non vi do subito il risultato 3-) # %% slideshow={"slide_type": "subslide"} # un altro modo di rispondere: con gli insiemi # raccolgo le risposte corrette # in due insiemi corrette_True e corrette_False domande = set(range(1,101)) # i numeri da 1 a 100 corrette_True = set(range(3,101,3)) # i multipli di 3 corrette_False= domande - corrette_True # i non multipli di 3 # raccolgo le risposte date True # in due insiemi date_True e date_False date_False = set(range(4,101,4)) date_True = domande - date_False # %% slideshow={"slide_type": "subslide"} # Le risposte giuste sono: # l'intersezione delle risposte date_False con le risposte corrette_False azzeccate = (corrette_True & date_True) | (corrette_False & date_False) # assieme a # l'intersezione delle risposte date_True con le risposte corrette_True # il risultato cercato è il numero di elementi dell'insieme # Noooo ... non ve lo dico, prima dovete risolverlo voi # %% ## Un terzo modo usando i dizionari # costruisco i dizionari # numero -> risposta giusta # numero -> risposta data dizionario_risposte = {} for i in range(1, 101): dizionario_risposte[i] = i % 3 == 0 dizionario_date = {} for i in range(1, 101): dizionario_date[i] = i % 4 != 0 # e poi calcolo l'intersezione dei due insiemi di items azzeccate_diz = set(dizionario_risposte.items()) & set(dizionario_date.items()) # La risposta dopo il break print(azzeccate) print(azzeccate_diz) len(azzeccate_diz) conta_azzeccate() # %% [markdown] slideshow={"slide_type": "subslide"} # ### Wooclap: secondo voi quante risposte ha azzeccato ? # # ![wooclap5.png](wooclap5.png) # # %% slideshow={"slide_type": "fragment"} # vediamo finalmente quante ne ha azzeccate print('azzeccate', conta_azzeccate()) len(azzeccate) # %% [markdown] slideshow={"slide_type": "slide"} # # Ancora argomenti formali delle definizioni delle funzioni # # Avrete notato che la funzione **print** accetta un numero indefinito di argomenti # # Ma come fa? E' speciale? # # No, approfitta della sintassi degli **assegnamenti multipli** e del **packing/unpacking** # # %% [markdown] slideshow={"slide_type": "subslide"} # ### Assegnamento multiplo? (WTF?) # # **elenco di variabili = contenitore con stesso numero di elementi** # # Ciascuna variabile viene assegnata, nell'ordine, con i valori elencati a destra dell' **`=`** # # - **PRIMA** viene calcolato tutto il contenitore a destra # - **POI** viene fatto l'assegnamento # %% slideshow={"slide_type": "fragment"} A, B = 1, 2 A, B = B, A # fa uno SCAMBIO! print(A, B) # %% [markdown] slideshow={"slide_type": "subslide"} # ### Ci permette di gestire facilmente gruppi di dati coerenti (struct o record in altri linguaggi) # %% slideshow={"slide_type": "fragment"} # p.es. se abbiamo altezza (H), larghezza (L) e profondità (P) # di una scatola in quest'ordine in una lista, dimensioni_scatola = [ 10, 20, 30, ] # per stamparli dovremmo prima estrarli H = dimensioni_scatola[0] L = dimensioni_scatola[1] P = dimensioni_scatola[2] # poi stamparli print('altezza',H) print('larghezza',L) print('profondità',P) # ma che strazio!!!! Bisogna ricordarsi gli indici! (0, 1, 2) # %% slideshow={"slide_type": "fragment"} # MOOOLTO meglio! H, L, P = dimensioni_scatola # li estraggo in ordine e li stampo print('altezza',H) print('larghezza',L) print('profondità',P) # NOTA: NON dobbiano ricordarci la posizione, # basta mettere le variabili nell'ordine giusto # %% [markdown] slideshow={"slide_type": "subslide"} # ## "packing" ed "unpacking" # # In un assegnamento possiamo raccogliere in una sola variabile tutti i valori rimanenti di una lista (**packing**) # # Il nome della variabile deve essere preceduto da asterisco **`*`** # %% slideshow={"slide_type": "fragment"} # PACKING: raccolgo in C tutti i valori # tranne i primi due che vanno in A e B # mettendo un asterisco subito prima del nome della variabile C A, B, *C = [ 1, 2, 3, 4, 5, 6] print('A:',A) print('B:',B) print('C:',C) # %% slideshow={"slide_type": "fragment"} # Esempio di packing che raccoglie *lista* vuota A, B, *C = (1,) print('C ora è', C) # %% slideshow={"slide_type": "fragment"} # ancora meglio, posso mettere la variabile "packed" all'inizio!!! *A, B, C = [ 1, 2, 3, 4, 5, 6] print('A:',A) print('B:',B) print('C:',C) # %% slideshow={"slide_type": "fragment"} # oppure in mezzo!!! A, *B, C = [ 1, 2, 3, 4, 5, 6] print('A:',A) print('B:',B) print('C:',C) # %% [markdown] slideshow={"slide_type": "fragment"} # **Basta che ci sia una sola variabile "packed"
# in modo che Python sappia cosa mettere nelle altre,
così metterà il resto in quella con asterisco** # # %% slideshow={"slide_type": "subslide"} ## Quindi la definizione di print dev'essere simile a def my_print(*roba_da_stampare, end='\n', sep=' '): # roba_da_stampare è una *tupla* con tutti i valori passati # NOTA: print prima converte tutto in stringhe stringhe = [] for valore in roba_da_stampare: stringhe.append(str(valore)) # e poi stampa (qui uso print per semplicità) print(sep.join(stringhe), end=end) my_print(1, 1.3, [1, 2, 3]) # %% [markdown] slideshow={"slide_type": "subslide"} # ### E l' "unpacking" cos'è? # # Sempre con l'asterisco possiamo "spacchettare" una variabile che contiene più valori all'interno di una altra espressione # ```python # X = sequenza # [ ..., *X, ... ] # diventa # [ ..., elementi di X, ... ] # ``` # %% slideshow={"slide_type": "fragment"} # esempio di unpacking di un *set* X = {11, 22, 33, 44} D = 1, 2, *X, 4 print('D ora è', D) # %% slideshow={"slide_type": "fragment"} # Esempio di unpacking di una sequenza di caratteri X = 'ABCDE' [ 1, 2, 3, 4, *X, 5, 6, 7, 8, ] # la espando in mezzo all'altra sequenza # %% [markdown] slideshow={"slide_type": "subslide"} # ### RIASSUMENDO: se un asterisco precede il nome di una variabile: # - in un **assegnamento** fa il **packing** di zero o più valori in una **lista** # ( **tupla** se sono gli argomenti di una funzione) # - in una **espressione** fa l'**unpacking** dei valori contenuti nella variabile # %% [markdown] slideshow={"slide_type": "slide"} # # Funzioni ricorsive # - le funzioni possono chiamare se stesse
# (ogni **chiamata** ha spazio di memoria separato e personale) # - questo è utilissimo per alcuni tipi di problemi
(lo vedremo bene nella seconda metà del corso) # %% [markdown] slideshow={"slide_type": "slide"} # Esempio classico: # ```python # def fattoriale(N): # if N < 2: # return 1 # else: # return N * fattoriale(N-1) # ``` # N | 1 | 2 | 3 | 4 | 5 | 6 | ... # --|--|--|--|--|--|--|-- # F(N) | 1 | 2 | 6 | 24 | 120 | 720 | ... # %% slideshow={"slide_type": "subslide"} from rtrace import trace # per tracciare l'esecuzione # la "annoto" in modo che si fermi ad ogni chiamata ed uscita @trace(pause=True) def fattoriale(N): if N < 2: return 1 # caso base (conosciuto) else: return N * fattoriale(N-1) # caso ricorsivo (riduzione e ricomposizione) # vediamo che fa print(fattoriale(5)) # vediamo il risultato # %% slideshow={"slide_type": "fragment"} fattoriale.trace(5) # vediamo la traccia delle chiamate # %% [markdown] slideshow={"slide_type": "subslide"} # ## Ogni soluzione ricorsiva ha 4 proprietà # - esiste almeno una soluzione conosciuta (**caso base**) # # %% [markdown] slideshow={"slide_type": "fragment"} # - il problema può essere decomposto in sottoproblemi
"più piccoli" (**riduzione**) # %% [markdown] slideshow={"slide_type": "fragment"} # - a forza di ridurre i problemi si casca sempre in uno dei casi base (**convergenza**) # %% [markdown] slideshow={"slide_type": "fragment"} # - dalle soluzioni dei sottoproblemi possiamo costruire la soluzione del problema (**ricomposizione**) # %% [markdown] slideshow={"slide_type": "fragment"} # # Questo schema ci può aiutare a trovare una soluzione ricorsiva ad un nuovo problema (seconda metà del corso) # %% [markdown] slideshow={"slide_type": "subslide"} # ## Wooclap: quanto vale `x` alla fine dei due programmi ? # # ![wooclap5.png](wooclap5.png) # # %% slideshow={"slide_type": "subslide"} # soluzioni: 0, 1, 3, 6, *10*, ... def increase(x): if x == 0: return 0 # caso base x += increase(x-1) # chiamata ricorsiva return x x = increase(4) x # non è molto diversa dal fattoriale, ma fa la somma # %% slideshow={"slide_type": "fragment"} # soluzione x = 4 increase(x) increase(x) x # la variabile x non viene modificata # anche se si chiama come l'argomento x # %% [markdown] slideshow={"slide_type": "slide"} # ## Disegnare alberi (esempio di figura [frattale](https://it.wikipedia.org/wiki/Frattale) ) # - Un albero di livello 0 è una foglia (cerchietto) (caso base) # - Un albero di livello N è formato da: (ricomposizione) # - un tronco lungo X # - un **albero di livello N-1** inclinato a sinistra, con tronco 80% di X (sottoproblema) # - un **albero di livello N-1** inclinato a destra, con tronco 70% di X (sottoproblema) # # Questa è una definizione **ricorsiva**: i due rami sono due alberi! # %% [markdown] slideshow={"slide_type": "subslide"} # Mi serve uno strumento di disegno: # - col modulo **turtle** posso tracciare movimenti relativi alla tartaruga # - col modulo **random** posso generare colori casuali # %% slideshow={"slide_type": "fragment"} # Per disegnare un albero frattale serve una tartaruga e dei numeri casuali import turtle from random import randint # generatore di interi casuali turtle.colormode(255) # setto i colori in modalità RGB t = turtle.Turtle() # creo una tartaruga t.penup() # alzo la penna t.left(90) # giro verso l'alto t.back(200) # mi posiziono in basso t.pensize(5) # con penna cicciotta t.speed(0) # e velocità alta #help(turtle) # see also (oppure 'pydoc turtle') # %% slideshow={"slide_type": "subslide"} # cos'è un albero frattale? # ha un tronco che regge due rami # i rami sono inclinati a sinistra e a destra del tronco # ciascun ramo ha la stressa struttura di un albero # ma leggermente più piccolo # un albero di livello zero è una foglia def albero(t, tronco, angolo, livelli): 'disegno un albero con un certo tronco iniziale e # di livelli' # TODO if livelli == 0: draw_leaf(t) else: # disegno il tronco (e mi sposto alla fine) draw_trunk(t, tronco) # mi giro a sinistra t.left(angolo) # disegno il ramo sinistro, più piccolo 80% albero(t, tronco * 0.8, angolo, livelli-1) # mi giro a destra t.right(angolo*2) # disegno il ramo destro, più piccolo 70% albero(t, tronco * 0.7, angolo, livelli-1) # torno nella direzione iniziale t.left(angolo) # torno alla base del tronco t.back(tronco) # %% slideshow={"slide_type": "subslide"} # devo definire come disegnare il tronco e la foglia def draw_trunk(t, lunghezza): 'Disegno un tratto di colore casuale' # TODO # cambio colore a caso R = randint(100, 200) G = randint(100, 200) B = randint(100, 200) t.color(R,G,B) # abbasso la penna t.pendown() # mi muovo in avanti di lunghezza pixel t.forward(lunghezza) # alzo la penna t.penup() # NOTA: ora sono all'estremo opposto del tronco # %% slideshow={"slide_type": "subslide"} def draw_leaf(t): 'disegno una foglia col colore corrente' # TODO # abbasso la penna t.pendown() # disegno un pallino t.dot() # alzo la penna t.penup() # %% slideshow={"slide_type": "subslide"} ## Vediamo se funziona :-) t.clear() # pulisco il foglio albero(t,100,30,6)