<jupyter_start><jupyter_text>**NOTA**: si consiglia di eseguire una riga alla volta di questo notebook, come fatto a lezione, cercando di capire sia cosa fa ciascuna funzione, sia soprattutto cercando di capire gli eventuali messaggi di errore.  Numeri Interi<jupyter_code>x=1
print(x)
type(x)
print(x, type(x))
y=2
z=(x+y)**2 * 3
print("x =", x, ", y =", y, ", z =", z)<jupyter_output><empty_output><jupyter_text>Numeri **Reali** (?)<jupyter_code>y=1.0
print(y)
type(x), type(y)
type(x+y)<jupyter_output><empty_output><jupyter_text>*Type conversion* o *type cast*Sono usati per convertire un tipo in un altro, in maniera dinamica. Per convertire un oggetto in un dato tipo, basta usare il nome del tipo come se fosse una funzione. Esempi:<jupyter_code>a = "3.0"
type(a)
b = int(a)
b = float(a)
type(b)
x = float(1)/3
y = 1/3
z = 1.0//3.0
print(x+y+z)
2.4//2.5<jupyter_output><empty_output><jupyter_text>**ATTENZIONE**: La divisione tra numeri interi non considera il *resto*<jupyter_code>x = 1.0/3
y = 1.0/3
z = 1/3
print(x+y+z)<jupyter_output><empty_output><jupyter_text>**NOTA:** In Python 2.7 questa espressione viene valutata a 0.666666 in quanto `z` vienve valutata con una divisione tra interi<jupyter_code>1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 == 1.0
1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10 + 1/10<jupyter_output><empty_output><jupyter_text>**ATTENZIONE**: Ricordarsi di come vengono rappresentati i numeri al calcolatori con la rappresentazione in virgola mobile (floating points).  Numeri Complessi<jupyter_code>x = 1+3j
y = 4+2j
z = 2+1j
print(type(x))
print(x+y*z)
print(z.real, z.imag, z.conjugate)
z.conjugate()
z.real = 3<jupyter_output><empty_output><jupyter_text>Comandi utili nel workspace Per controllare le variabili in memoria (nel workspace):<jupyter_code>who<jupyter_output><empty_output><jupyter_text>Per rimuovere una variabile dal workspace:<jupyter_code>del x
who
print(x+y+z)
help(complex)<jupyter_output><empty_output><jupyter_text>FunzioniSupponiamo di voler scrivere una funzione che calcola il doppio di un numero dato ovvero la funzione:$f : \mathbb{C} \rightarrow \mathbb{C}$     con:     - $f(x) = 2 x$In python dobbiamo scrivere (**ATTENZIONE AGLI SPAZI DEL TAB**):<jupyter_code># Notare l'identazione nella funzione seguente
def f(x):
    """Funzione che il doppio di x"""
    return x*2
print(type(f))
f(27)
f(1.2)
f(1+3j)
f("Ciao")
2*"domanda"
help(f)<jupyter_output><empty_output><jupyter_text>Vogliamo ora scrivere la funzione:$$f : \mathbb{C} \times \mathbb{C} \rightarrow \mathbb{C}$$con$$f(x,y) = x^y$$In python scriviamo:<jupyter_code>def Potenza(base, esponente):
    """
    Calcola la potenza (base)^(esponente).
    """
    
    return base**esponente
Potenza(2, 3)
def Potenza2(base, esponente=2):
    """
    Calcola la potenza (base)^(esponente).
    Se il secondo parametro non viene passato,
    calcola la potenza quadrata, ovvero (esponente=2).
    """
    
    return base**esponente
print(Potenza2(14))
print(Potenza2(14,3))
Potenza2(esponente=4, base=10)<jupyter_output><empty_output><jupyter_text>Python supporta la possibilità di usare i nomi di variabili usati nella definizione di una funzione per passare gli argomenti (chiamati **keyword arguments**). Questo si differenza dalla tipica notazione **posizionale** di molti linguaggi di programmazione, tipo il C, e permette  di rendere il codice più leggibile e di evitare ambiguità nel passare gli argomenti ad una funzione. Si noti infine che nella definizione della funzione `Potenza2` è stato definito un parametro di default (**default parameter values**) per definire il valore dell'esponente.  Liste<jupyter_code>As = [5,3,2,8,7,13]
print(As)
print(type(As))
# help(list)<jupyter_output><empty_output><jupyter_text>**IMPORTANTE**: Abituarsi ad usare il tasto **`tab`** per avere il completamento automatico<jupyter_code># Aggiungere un elemento ad una lista
As.append(27)
print(As)
As.remove(7)
print(As)
As.insert(4, 12)
print(As)
# Invertire l'ordine di una lista
As.reverse()
print(As)
# Ordinamento come funzione
Bs = sorted(As)
print(As, Bs)
# Ordinamento "IN_PLACE"
As.sort()
print(As)<jupyter_output><empty_output><jupyter_text>**COMMENTO**: Ricordarsi la differenza che alcune funzioni operano **`IN PLACE`**<jupyter_code>print(As)
for x in As:
    print(x)
# Equivalente in ANSI-C    
# for (int i=0; i<n; i++)
#    printf("%f\n", As[i]);
# Iterare gli elementi di una lista (esempio di commento in python)
for n in As:
    print("x=", str(n)," \t -> f(x^3) =",str(Potenza(n,3)))
list(map(lambda x: Potenza(x,3), As))
# Iterare una lista usando una variabile 
# per l'indice dell'elemento nella lista
for i,n in enumerate(As):
    print("indice:", i, " -> As["+str(i)+"] = " + str(n))
# Iterare una lista usando una variabile 
# per l'indice dell'elemento nella lista
for i,n in enumerate(reversed(As)):
    print("indice:", i, " -> As["+str(i)+"] = " + str(n))<jupyter_output><empty_output><jupyter_text>Operazioni di *Slicing*Per ottenere una sotto lista, si può usare un espressione di *Slicing*. Per esempio, l'espressione Lista[start:end],restituisce la sottolista che inizia in posizione "start" e finisce in posizione "end". Esempio:<jupyter_code>As
As[3:5]
As[-1]
head, tail = As[0], As[1:]
print(head, tail)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13950431843785205, 'fraction_numerical': 0.028914757791963952, 'mean_word_length': 5.576543209876543, 'pattern_counts': {'":': 0, '<': 58, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '642483', 'license': 'MIT', 'n_tokens_mistral': 2215, 'n_tokens_neox': 2144, 'n_words': 563, 'path': 'Introduzione a Python - Prima parte.ipynb', 'repo_name': 'karen013/https-github.com-mathcoding-Programmazione2', 'size': '5326'}