# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# + nbsphinx="hidden"
# Ricordati di eseguire questa cella con Shift+Invio

import sys
sys.path.append('../')
import jupman
# -

#
# # Liste 1 - Introduzione
#
#
# ## [Scarica zip esercizi](../_static/generated/lists.zip)
#
# [Naviga file online](https://github.com/DavidLeoni/softpython-it/tree/master/lists)
#
#
# Una lista in Python è una sequenza di elementi eterogenei **mutabile**, in cui possiamo mettere gli oggetti che vogliamo. L'ordine in cui li mettiamo viene preservato.
#

# ## Che fare
#
# - scompatta lo zip in una cartella, dovresti ottenere qualcosa del genere: 
#
# ```
#
# lists
#     lists1.ipynb
#     lists1-sol.ipynb         
#     lists2.ipynb
#     lists2-sol.ipynb         
#     lists3.ipynb
#     lists3-sol.ipynb         
#     lists4.ipynb
#     lists4-sol.ipynb         
#     jupman.py         
# ```
#
# <div class="alert alert-warning">
#
# **ATTENZIONE**: Per essere visualizzato correttamente, il file del notebook DEVE essere nella cartella szippata.
# </div>
#
# - apri il Jupyter Notebook da quella cartella. Due cose dovrebbero aprirsi, prima una console e poi un browser. Il browser dovrebbe mostrare una lista di file: naviga la lista e apri il notebook `lists1.ipynb`
# - Prosegui leggendo il file degli esercizi, ogni tanto al suo interno troverai delle scritte **ESERCIZIO**, che ti chiederanno di scrivere dei comandi Python nelle celle successive. Gli esercizi sono graduati per difficoltà, da una stellina ✪ a quattro ✪✪✪✪
#
#
# Scorciatoie da tastiera:
#
# * Per eseguire il codice Python dentro una cella di Jupyter, premi `Control+Invio`
# * Per eseguire il codice Python dentro una cella di Jupyter E selezionare la cella seguente, premi `Shift+Invio`
# * Per eseguire il codice Python dentro una cella di Jupyter E creare una nuova cella subito dopo, premi `Alt+Invio`
# * Se per caso il Notebook sembra inchiodato, prova a selezionare `Kernel -> Restart`
#
#

# ## Creare liste
#
# Possiamo creare una lista specificando tra parentesi quadre gli elementi che contiene, e separandoli da una virgola.
#
# Per esempio, in questa lista inseriamo i numeri `7`, `4` e `9`:

[7,4,9] 

# Come tutti gli oggetti in Python, possiamo associarli ad una variabile, in questo caso ce ne inventiamo una chiamata `lista`:

lista = [7,4,9] 

lista

# Vediamo meglio che succede in memoria, e compariamo la rappresentazione delle stringhe con quella delle liste:

# +
# AFFINCHE' PYTHON TUTOR FUNZIONI, RICORDATI DI ESEGUIRE QUESTA CELLA con Shift+Invio
#   (basta eseguirla una volta sola, la trovi anche all'inizio di ogni foglio)

import sys
sys.path.append('../')
import jupman

# +
stringa = "prova"

lista = [7,4,9]

jupman.pytut()
# -

# Notiamo subito una differenza rilevante. La stringa è rimasta nella regione azzurra dove appaiono le associazioni tra variabili e valori, invece dalla variabile `lista` parte una freccia che punta ad una nuova regione gialla di memoria, che si crea non appena l'esecuzione raggiunge la riga che definisce la lista.
#
# In seguito approfondiremo meglio le conseguenze di ciò.

# In una lista gli stessi elementi possono apparire più volte

numeri = [1,2,3,1,3]


numeri

# In una lista possiamo mettere qualunque elemento, per es. le stringhe:

frutti = ["mela", "pera", "pesca", "fragola", "ciliegia"] 

frutti

# Possiamo anche mischiare i tipi di oggetti contenuti in una lista, per esempio possiamo avere interi e stringhe:

misto = ["tavolo", 4 ,"sedia", 8, 5, 1, "sedia"]

# In Python Tutor apparirà così:

# +
misto = ["tavolo", 5 , 4, "sedia", 8, "sedia"]

jupman.pytut()
# -

# Per comodità possiamo anche scrivere la lista su più righe (gli spazi in questo caso non contano, ricordati solo di terminare le righe con delle virgole `,`)

misto = ["tavolo",
         5 , 
         4,
         "sedia",
         8,
         "sedia"]

# ### Esercizio - proviamo l'errore
#
# Prova a scrivere la lista qua sopra SENZA mettere una virgola dopo il 5, che errore appare?

# +
# scrivi qui


# -

# ### Tabelle 
#
# Una lista può anche contenere altre liste: 

tabella = [  ['a','b','c'],  ['d','e','f']  ]

# Tipicamente, quando abbiamo strutture come questa, conviene disporle su più righe (non è obbligatorio ma conviene per chiarezza): 

tabella = [                 # inizio listona esterna
            ['a','b','c'],    # lista interna 1
            ['d','e','f']     # lista interna 2
          ]                 # fine listona esterna

tabella

# Vediamo come viene mostrata in Python Tutor: 

# +
tabella = [  
            ['a','b','c'],  
            ['d','e','f']  
          ]

jupman.pytut()
# -

# Come detto in precedenza, in una lista possiamo mettere gli elementi che vogliamo, quindi possiamo mischiare  liste di dimensioni diverse, stringhe, numeri, etc:

ditutto = [ 
               ['ciao',3,'mondo'],            
               'una stringa',
               [9,5,6,7,3,4],
               8,
         ]

print(ditutto)

# Vediamo anche come appare in Python Tutor:

# +
ditutto = [ 
               ['ciao',3,'mondo'],            
               'una stringa',
               [9,5,6,7,3,4],
               8,
         ]

jupman.pytut()
# -

# ### Lista vuota
#
# Ci sono due modi per creare una lista vuota.
#
# 1) con parentesi quadre:

lista_vuota = []

lista_vuota

# 2) Oppure con `list()`:

altra_lista_vuota = list()

altra_lista_vuota

# <div class="div alert alert-warning">
#
# **ATTENZIONE**: Quando crei una lista vuota (indipendentemente dalla notazione usata), in memoria viene allocata una NUOVA regione di memoria per accogliere la lista
#
# </div>
#
# Vediamo meglio cosa vuol dire con Python Tutor: 

# +
a = []
b = []

jupman.pytut()
# -

# Nota che sono apparse due frecce che puntano a regioni di memoria **differenti**. Lo stesso sarebbe accaduto inizializzando le liste con degli elementi:

# +
la = [8,6,7]
lb = [9,5,6,4]

jupman.pytut()
# -

# E avremmo avuto due liste in regioni di memoria diverse anche mettendo elementi identici dentro le liste:

# +
la = [8,6,7]
lb = [8,6,7]

jupman.pytut()
# -

# Le cose si complicano quando cominciamo ad usare operazioni di assegnazione:

la = [8,6,7]

lb = [9,5,6,4]

lb = la

# Scrivendo `lb = la`, abbiamo detto a Python di 'dimenticare' l'assegnazione precedente di `lb` a `[9,5,6,4]`, e di associare invece `lb` alla stesso valore già associato ad `la`, cioè `[8,6,7]`. Quindi, nella memoria vedremo una freccia che parte da `lb` ed arriva a `[8,6,7]`, e la regione di memoria dove stava  la lista `[9,5,6,4]` verrà rimossa (non è più associata ad alcuna variabile). Guardiamo che succede con Python Tutor:

# +
la = [8,6,7]
lb = [9,5,6,4]
lb = la

jupman.pytut()
# -

# ### Esercizio - scambi di liste
#
# Prova a scambiare le liste associate alle variabili `la` ed `lb` usando solo assegnazioni e **senza creare nuove liste**. Se vuoi, puoi sovrascrivere una terza variabile `lc`. Verifica che succede con Python Tutor.
#
# * il tuo codice deve poter funzionare con qualunque valore di `la`, `lb` ed `lc`
#
# Esempio - dati: 
#
# ```python
# la = [9,6,1]
# lb = [2,3,4,3,5]
# lc = None
# ```
#
# Dopo il tuo codice, deve risultare:
#
# ```python
# >>> print(la)
# [2,3,4,3,5]
# >>> print(lb)
# [9,6,1]
# ```

# +
la = [9,6,1]
lb = [2,3,4,3,5]
lc = None

# scrivi qui

lc = la
la = lb
lb = lc

#print(la)
#print(lb)
# -

# ### Domanda - creazione di liste
#
#
# Guarda questi due pezzi di codice.  Per ciascun caso, prova a pensare come possono essere rappresentati in memoria e verifica poi con Python Tutor.
#
# - che differenza ci potrà essere?
# - quante celle di memoria verranno allocate in totale ? 
# - quante frecce vedrai ?
#
# ```python
# # primo caso
# lb = [
#        [8,6,7],
#        [8,6,7],
#        [8,6,7],
#        [8,6,7],       
#      ]
# ```
#
# ```python
# # secondo caso
# la = [8,6,7]
# lb = [
#        la,
#        la,
#        la,
#        la
#      ]
# ```

# primo caso
lb = [
       [8,6,7],
       [8,6,7],
       [8,6,7],
       [8,6,7],       
     ]
jupman.pytut()

# +
# secondo caso

la = [8,6,7]
lb = [
       la,
       la,
       la,
       la
     ]
jupman.pytut()
# -

# **RISPOSTA**:  Nel primo caso, abbiamo una 'listona' associata alla variabile `lb` che contiene 4 sottoliste ciascuna da 3 elementi. Ogni sottolista viene creata come nuova, quindi in totale in memoria abbiamo 4 celle della listona `lb` + (4 sottoliste * 3 celle ciascuna) = 16 celle 
#
# Nel secondo caso invece abbiamo sempre la 'listona' associata alla variabile `lb` da 4 celle, ma al suo interno contiene dei puntatori alla stessa identica lista `la`. Quindi il numero totale di celle occupate è 4 celle della listona `lb` + (1 sottolista * 3 celle) = 7 celle

# ### Esercizio - domino
#
# Nel tuo quartiere stanno organizzando un super torneo di domino, visto che il primo premio consiste in una tessera per ottenere 10 crostate dalla mitica Nonna Severina decidi di impegnarti seriamente.
#
# Inizi a pensare a come allenarti e decidi di iniziare a accodare le tessere in maniera corretta.
#
# ```python
# tessera1 = [1,3]
# tessera3 = [1,5]
# tessera2 = [3,9]
# tessera5 = [9,7]
# tessera4 = [8,2]
# ```
#
# Date queste tessere genera una lista che conterrà a sua volta due liste: nella prima inserisci una possibile sequenza di tessere collegate; nella seconda lista invece metti le tessere rimaste escluse dalla sequenza di prima.
#
# Esempio:
#
# ```python
# [  [ [1, 3], [3, 9], [9, 7] ],   [ [1, 5], [8, 2]  ]  ]
# ```
#
# * **NON scrivere numeri**
# * **USA** solo liste di variabili

# +
#jupman-purge-output
tessera1 = [1,3]
tessera2 = [3,2]
tessera3 = [1,5]
tessera4 = [2,4]
tessera5 = [3,3]
tessera6 = [5,4]
tessera7 = [1,2]

# scrivi qui
sequenza = [tessera1, tessera5, tessera2, tessera4]
spaiate = [tessera3, tessera6, tessera7]
print([sequenza, spaiate])
# -

# ### Esercizio - creare liste 2
#
# Inserisci dei valori nelle liste `la`, `lb` tali per cui
#
# ```python
# print([[la,la],[lb,la]])
# ```
#
# stampi
#
# ```
# [[[8, 4], [8, 4]], [[4, 8, 4], [8, 4]]]
# ```
#
# * **inserisci solo dei NUMERI**
# * Osserva in Python Tutor come vengono raffigurate le frecce

# +
la = []  # inserisci dei numeri
lb = []  # inserisci dei numeri

print([[la,la],[lb,la]])

# +
# SOLUZIONE
#jupman-purge-output

la = [8,4]
lb = [4,8,4]

print([[la,la],[lb,la]])
# -

# ### Esercizio - creare liste 3
#
# Inserire dei valori come elementi delle liste `la`, `lb` e `lc` tali per cui 
#
# ```python
# print([[lb,lb,[lc,la]],lc])
# ```
#
# stampi
#
# ```
# [[[8, [7, 7]], [8, [7, 7]], [[8, 7], [8, 5]]], [8, 7]]
# ```
#
# * **inseriri solo NUMERI oppure NUOVE LISTE DI NUMERI**
# * Osservare in Python Tutor come vengono raffigurate le frecce

# +

la = []  # inserisci elementi  (numeri o liste di numeri)
lb = []  # inserisci elementi  (numeri o liste di numeri)
lc = []  # inserisci elementi  (numeri o liste di numeri)

print([[lb,lb,[lc,la]],lc])

# +
# SOLUZIONE
#jupman-purge-output

la = [8,5]
lb = [8,[7,7]]
lc = [8,7]

print([[lb,lb,[lc,la]],lc])
# -

# ### Esercizio - creare liste 4
#
#
# Inserire dei valori nelle liste `la`, `lb` tali per cui 
#
# ```python
# print([[la,lc,la], lb])
# ```
#
# stampi
#
# ```
# [[[3, 2], [[3, 2], [8, [3, 2]]], [3, 2]], [8, [3, 2]]]
# ```
#
# * **inserire solo NUMERI oppure VARIABILI** `la`,`lb` o `lc`
# * Osservare in Python tutor come vengono raffigurate le frecce

# +
la = []  # inserisci numeri o variabili la, lb, lc
lb = []  # inserisci numeri o variabili la, lb, lc
lc = []  # inserisci numeri o variabili la, lb, lc

print([[la,lc,la], lb])

# +
# SOLUZIONE
#jupman-purge-output
la = [3,2]
lb = [8,la]
lc = [la,lb]

print([[la,lc,la], lb])
# -

# ## Convertire sequenze in liste

# `list` può anche servire per convertire una qualsiasi sequenza in una NUOVA lista. Un tipo di sequenza che già abbiamo visto sono le stringhe, quindi proviamo a vedere cosa succede se usiamo `list` come fosse una funzione e gli passiamo come parametro una stringa: 

list("treno")

# Abbiamo ottenuto una lista in cui ogni elemento è costituito da un carattere della stringa originale.

# Se invece chiamiamo `list` su un'altra lista cosa succede?

list( [7,9,5,6] )

# Apparentemente, niente di particolare, otteniamo una lista con gli stessi elementi di partenza. Ma è proprio la stessa lista? Guardiamo meglio con Python Tutor:

# +
la = [7,9,5,6] 

lb = list( la )

jupman.pytut()
# -

#
# Notiamo che è stata creata una NUOVA regione di memoria con gli stessi elementi di `la`.

# ### Esercizio - gulp
#
# Data una stringa con caratteri misti minuscoli e maiuscoli, scrivere del codice che crea una lista contente come primo elemento una lista coi caratteri della stringa tutti minuscoli e come secondo elemento una lista contenente tutti i caratteri maiuscoli
#
# * il tuo codice deve funzionare con qualunque stringa
# * se non ricordi i metodi delle stringhe, [guarda qui](https://it.softpython.org/strings/strings3-sol.html)
#
# Esempio - dato
#
# ```python
# s = 'GuLp'
# ```
#
# il tuo codice dovrà stampare
#
# ```
# [['g', 'u', 'l', 'p'], ['G', 'U', 'L', 'P']]
# ```
#

# +
#jupman-purge-output
s = 'GuLp'

# scrivi qui
print([list(s.lower()), list(s.upper())])
# -

# ### Domanda - maratona
#
# Questo codice:
#
# - produce un errore o assegna qualcosa ad `x` ? 
# - Dopo la sua esecuzione, quante liste rimangono in memoria?
# - Possiamo accorciarlo?

# ```python
# s = "maratona"
# x = list(list(list(list(s))))
# ```

# **RISPOSTA**: Il codice assegna alla variabile `x` la lista `['m', 'a', 'r', 'a', 't', 'o', 'n', 'a']`. La prima volta `list(s)` genera la NUOVA lista `['m', 'a', 'r', 'a', 't', 'o', 'n', 'a']`. Le successive chiamate a `list` prendono in input la lista appena generata `['m', 'a', 'r', 'a', 't', 'o', 'n', 'a']` e continuano a creare NUOVE liste con contenuto identico. Dato che però nessuna lista prodotta eccetto l'ultima viene assegnata ad una variabile, quelle intermedie alla fine dell'esecuzione vengono di fatto eliminate. Possiamo quindi tranquillamente accorciare il codice scrivendo 
#
# ```python
# s = "maratona"
# x = list(s)
# ```

# ### Domanda - catena
#
# Questo codice:
#
# - produce un errore o assegna qualcosa ad `x` ? 
# - Dopo la sua esecuzione, quante liste rimangono in memoria?
#
#
# ```python
# s = "catena"
# a = list(s)
# b = list(a)
# c = b
# x = list(c)
# ```

# **RISPOSTA**: Rimangono in memoria 3 liste ognuna contenente 6 celle. Questa volta le liste permangono in memoria perchè sono associate alle variabili `a`,`b` e `c`. Abbiamo 3 e non 4 liste perchè con l'istruzione `c = b` la variabile `c` viene associata alla stessa identica regione di memoria associata alla variabile `b`

# ### Esercizio - garaga
#
# Date 
#
# ```python
# sa = "ga"
# sb = "ra"
# la = ['ga']
# lb = list(la)
# ```
#
# * Assegnare ad `lc` una lista costruita in modo che una volta stampata produca
#
# ```python
# >>> print(lc)
# ```
# ```python
# [['g', 'a', 'r', 'a'], ['ga'], ['ga'], ['r', 'a', 'g', 'a']]
# ```
#
# * **in Python Tutor, TUTTE le frecce dovranno puntare ad una regione di memoria diversa**
#

# +
sa = "ga"
sb = "ra"
la = ['ga']
lb = list(la)

# inserire del codice nella lista
lc = []  

print(lc)
jupman.pytut()

# +
# SOLUZIONE

sa = "ga"
sb = "ra"
la = ['ga']
lb = list(la)
lc = [list(sa + sb), list(la), list(lb), list(sb + sa) ]

print(lc)
jupman.pytut()
# -

# ## Continua
#
# Prosegui con il foglio [Liste 2 - Operatori](https://it.softpython.org/lists/lists2-sol.html)



StarcoderdataJupyterScriptsDedupFiltered