# ---
# 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 6 - 20 ottobre 2022
# %% [markdown] slideshow={"slide_type": "subslide"}
# # Wooclap: quanto ho capito finora ?
# 
# %% [markdown] slideshow={"slide_type": "subslide"}
# # Ordinamento di un contenitore
#
# Abbiamo visto che il metodo **sort** delle liste ci permette di ordinarle in ordine crescente.
#
# Come fare per ordinarle in modo più sofisticato?
#
# Esempio:
# **`[ 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette']`** :
# - per **lunghezza crescente delle parole**
# - e se sono di lunghezza uguale, **in ordine alfabetico**
#
#
#
#
#
# %% [markdown] slideshow={"slide_type": "fragment"}
# Potrei **trasformare ciascun elemento in una coppia**:
# - lunghezza della parola
# - parola
#
# e cercare di ordinare la nuova lista di coppie
# %% slideshow={"slide_type": "fragment"}
L = [ 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette']
# costruisco la lista di coppie
L1 = []
for elemento in L:
L1.append((len(elemento), elemento))
L1
# la ordino
L1.sort()
L1
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### FUNZIONA! MA PERCHE'?
# Perchè quando dobbiamo ordinare una lista di soppie
per confrontare due coppie:
# - prima confrontiamo il primo (lunghezza della parole)
# - poi solo in caso di parità passiamo a confrontare il secondo
# (ordine alfabetico delle parole)
#
# ... e questo era proprio quello che volevamo!
# %% slideshow={"slide_type": "fragment"}
# a questo punto basta estrarre dalle coppie solo la parola originale
L2 = []
for lunghezza,elemento in L1:
L2.append(elemento)
L2
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Ma se volessi che l'ordinamento fosse **STABILE** ?
# Ovvero che elementi "uguali" che erano in un certo ordine relativo
#
# mantengano lo stesso ordine relativo
#
# **BASTA aggiungere alla tupla anche la posizione originale**
# %% slideshow={"slide_type": "fragment"}
## Ma se volessi che l'ordinamento fosse **STABILE** ?
L = [ 'uno', 'due', 'tre', 'sette', 'cinque', 'sei', 'sette']
# costruisco la lista di coppie
L1 = []
# assieme all'elemento ne estraggo la posizione
for i,elemento in enumerate(L):
# aggiungo la posizione *i* come ulteriore criterio
L1.append((len(elemento), elemento, i))
L1
# la ordino
L1.sort()
L1
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### Questo tipo di trasformazione di ciascun elemento si chiama trasformazione di Schwartz
#
# e può essere semplificata introducendo una piccola funzione che trasforma ciascun elemento
#
# da passare a **sorted** (che tra l'altro è stabile)
# %% slideshow={"slide_type": "fragment"}
# trasformazione di un solo elemento nella coppia corrispondente
def trasforma_elemento(parola):
return len(parola), parola
# con la funzione *sorted* costruiamo una nuova lista ordinata
# fornendo col parametro *key* la funzione di trasformazione
sorted(L, key=trasforma_elemento)
# %% [markdown] slideshow={"slide_type": "fragment"}
# **MA INVECE** che definire una nuova funzione apposta
#
# (che magari ci serve solo in questa occasione)
#
# sarebbe comodo avere un modo per creare funzioni **USA e GETTA**
#
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Funzioni anonime (espressioni *lambda*)
#
# Notate che la funzione precedente è estremamente semplice ed ha un solo compito:
# - riceve dei parametri
# - ritorna SOLO una espressione **SENZA ESEGUIRE ALTRE ISTRUZIONI**
#
# E può essere abbreviata senza usare la keyword **return** in
# ```python
# lambda argomenti: espressione
# ```
# %% slideshow={"slide_type": "subslide"}
# quindi la funzione *trasforma_elemento*
# può essere scritta direttamente come
#
trasforma_elemento = lambda elemento: (len(elemento), elemento)
trasforma_elemento('Paperino')
#
sorted(L, key=lambda elemento: (len(elemento), elemento))
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### RIASSUNTO
#
# Per ordinare un contenitore di elementi secondo criteri complessi:
# - si definisce una funzione di trasformazione (o una lambda) che:
# - riceve l'elemento da trasformare
# - torna una tupla contenente nell'ordine i dati
# che servono a rappresentare i criteri complessi
# %% [markdown] slideshow={"slide_type": "subslide"}
# **Esempio:** ordiniamo la lista di parole
# - in ordine CRESCENTE di lunghezza
# - e in caso di parità in ordine alfabetico DECRESCENTE (Z->A)
#
# qui abbiamo un problema in più, **i due ordinamenti sono opposti!**
#
# Uno dei due va rovesciato,
#
# perchè i confronti tra sequenze sono sempre monodirezionali
#
# cioè tutti gli ordinamenti vanno nella stessa direzione
# %% slideshow={"slide_type": "fragment"}
# definiamo la funzione di trasformazione adatta
# NON possiamo rovesciare l'ordinamento alfabetico!
# ALLORA rovesciamo l'ordinamento delle lunghezze
def trasforma_elemento(el):
return -len(el), el # lunghezza negativa
# vediamo cosa succede
sorted(L, key=trasforma_elemento)
# E' tutto a rovescio!
sorted(L, key=trasforma_elemento, reverse=True)
# ora va bene
# %% slideshow={"slide_type": "fragment"}
# A questo punto possiamo riscrivere la trasformazione
# usando una funzione anonima con lambda
# (notate le parentesi per creare la tupla)
sorted(L, key=lambda el: (-len(el),el), reverse=True)
# %% [markdown] slideshow={"slide_type": "subslide"}
# # Wooclap: Secondo voi cosa ottengo da questo sorted?
# 
# %%
# Soluzione
# Supponiamo di sapere età
# e genere di alcuni personaggi
L = [ (27, 'M', 'Paperino'),
(31, 'M', 'Topolino'),
(26, 'F', 'Paperina'),
(32, 'F', 'Minnie')]
def trasforma_elemento(terna):
eta, genere, nome = terna
return len(nome), genere, eta
sorted(L, key=trasforma_elemento)
# %% [markdown] slideshow={"slide_type": "slide"}
# # List comprehension
# ## una sintassi semplificata per costruire contenitori
#
# Spessissimo dobbiamo raccogliere in un contenitore più elementi e scriviamo roba del tipo di
# %% slideshow={"slide_type": "fragment"}
# dati dei valori
lista_di_interi = [12, 34, 61, 2, 4, 7]
# voglio costruire la lista dei cubi
lista_di_cubi = []
for x in lista_di_interi:
lista_di_cubi.append(x**3)
lista_di_cubi
# %% [markdown] slideshow={"slide_type": "fragment"}
# - iniziamo costruendo una lista vuota
# - iteriamo su un contenitore di elementi
# - trasformiamo ciascun elemento
# - lo aggiungiamo alla lista
# - torniamo la lista ottenuta
#
# Sarebbe bello poter usare una sintassi più leggibile
#
# (che non ci obbliga a **simulare** il codice nella testa)
# %% slideshow={"slide_type": "subslide"}
lista_di_interi = [12, 34, 2, 61, 2, 4, 7]
# Con la sintassi della list-comprehension
[ X**3 for X in lista_di_interi ]
# - le parentesi indicano che contenitore stiamo costruendo (lista)
# - il *for* indica su quale sequenza stiamo iterando
# - l'espressione all'inizio (X**3) come produciamo ogni nuovo valore
# %% slideshow={"slide_type": "fragment"}
# possiamo costruire altri tipi di contenitore
# INSIEMI
{ X**3 for X in lista_di_interi }
# %% slideshow={"slide_type": "fragment"}
# DIZIONARI che contengono coppie chiave:valore
{ X : X**3 for X in lista_di_interi }
# %% slideshow={"slide_type": "fragment"}
# TUPLE (qui ci vuole la parola tuple)
tuple( X**3 for X in lista_di_interi )
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### e possiamo anche condizionare quali elementi trasformare
#
# ```python
# [ espressione for variabile in contenitore
# if condizione ]
# ```
# %% slideshow={"slide_type": "fragment"}
# Voglio i cubi SOLO dei numeri dispari
{ x : x**3 for x in lista_di_interi if x%2 }
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### oppure costruire for nidificati
# ```python
# [ espressione for var1 in contenitore1
# for var2 in contenitore2 ]
# ```
# %% slideshow={"slide_type": "fragment"}
# costruisco l'insieme dei prodotti della tabellina del 10
# %pprint
{ X*Y for X in range(1,11) for Y in range(1,11) }
# %% [markdown] slideshow={"slide_type": "subslide"}
# ### oppure costruire contenitori nidificati
# ```python
# [ [ espressione for var1 in contenitore1 ]
# for var2 in contenitore2 ]
# ```
# %% slideshow={"slide_type": "fragment"}
# Ad esempio la tabellina del 10 come lista di liste
# (una lista per ogni riga)
# %pprint
[ [ X*Y for X in range(1,11) ] # per ogni Y costruisco la riga
for Y in range(1,11) ]
# %% [markdown] slideshow={"slide_type": "subslide"}
# # Wooclap: Secondo voi cosa ottengo da questa list comprehension?
# 
# %% slideshow={"slide_type": "subslide"}
## Vediamo la soluzione
alfabeto = "abcdefghijk"
parola = "bigia"
[ ''.join( c for c in alfabeto[alfabeto.index(X):] )
for X in parola]
# %% [markdown] slideshow={"slide_type": "slide"}
# # INTERVALLO (RAI - anni 70)
# %% slideshow={"slide_type": "fragment"} language="html"
#
# %% [markdown] slideshow={"slide_type": "slide"}
# # COME analizzare un problema?
# - cerchiamo di capire come lo faremmo su carta
# - descrivendo gli **input ed output**
# - eventuali **controlli da fare sui dati**
# - possibili **errori da generare**
# - possibili **effetti collaterali**
# - possibili **scelte non indicate**
# %% [markdown] slideshow={"slide_type": "subslide"}
# - lo dividiamo in **problemi più piccoli**
# - ripetendo il metodo di analisi
#
# - **continuando a frammentarlo** finchè
# - la sua **descrizione è così semplice**
# - da poter essere **implementato**
#
# - mano a mano **testiamo le sottofunzioni** realizzate
# - **semplificando enormemente il debug**
# %% [markdown] slideshow={"slide_type": "slide"}
# ## Problema: trovare i k massimi di una lista L di valori numerici
#
# - **rappresentazione dei dati**?
(in questo caso lo sappiamo)
# - INPUT: una lista di interi ed un valore intero K
# - OUTPUT: la lista dei K massimi valori
# %% [markdown] slideshow={"slide_type": "fragment"}
# - controlliamo se ci sono **condizioni di validità** dei dati
# - cosa fare se **K negativo**? ERRORE?
# - cosa fare se **K > len(L)**?
# - diamo **errore**?
# - torniamo un numero di elementi **minori di K**?
# %% [markdown] slideshow={"slide_type": "fragment"}
# - ci sono **effetti collaterali**?
# - **modifichiamo distruttivamente** L ?
# - non la modifichiamo?
# %% [markdown] slideshow={"slide_type": "fragment"}
# - vogliamo **caratteristiche particolari** del risultato?
# - **ordinato**?
# - **disordinato**?
# %% [markdown] slideshow={"slide_type": "subslide"}
# ## Scegliamo ad esempio:
# - se K è sbagliato diamo un **errore**
# - **modifichiamo distruttivamente** L
# - il risultato può essere in **qualsiasi ordine**
# %% slideshow={"slide_type": "subslide"}
# per estrarre i k massimi da L
def k_massimi_distruttivo(L, k):
# controllo che k sia valido e do errore altrimenti
assert len(L), "L è vuota"
assert 0