Chapter 3 FunzioniNell’ambito della programmazione, una funzione è una serie di istruzioni che esegue un calcolo, alla quale viene assegnato un nome. Per definire una funzione, dovete specificarne il nome e scrivere la sequenza di istruzioni. In un secondo tempo, potete “chiamare” la funzione mediante il nome che le avete assegnato. 3.1 Chiamate di funzioneAbbiamo già visto un esempio di una chiamata di funzione: >>> type(42) <class 'int'> Il nome di questa funzione è type. L’espressione tra parentesi è chiamata argomento della funzione, e il risultato che produce è il tipo di valore dell’argomento che abbiamo inserito. Si usa dire che una funzione “prende” o “riceve” un argomento e, una volta eseguita l’elaborazione, “ritorna” o “restituisce” un risultato. Il risultato è detto valore di ritorno. Python fornisce una raccolta di funzioni che convertono i valori da un tipo all’altro. La funzione int prende un dato valore e lo converte, se possibile, in intero. Se la conversione è impossibile compare un messaggio d’errore: >>> int('32') 32 >>> int('Ciao') ValueError: invalid literal for int(): Ciao int può anche convertire valori in virgola mobile in interi, ma non arrotonda bensì tronca la parte decimale. >>> int(3.99999) 3 >>> int(-2.3) -2 La funzione float converte interi e stringhe in numeri a virgola mobile: >>> float(32) 32.0 >>> float('3.14159') 3.14159 Infine, str converte l’argomento in una stringa: >>> str(32) '32' >>> str(3.14159) '3.14159' 3.2 Funzioni matematichePython è provvisto di un modulo matematico che contiene le più comuni operazioni matematiche. Un modulo è un file che contiene una raccolta di funzioni correlate. Prima di poter usare le funzioni contenute in un modulo, dobbiamo dire all’interprete di caricare il modulo in memoria con un’istruzione di importazione: >>> import math Questa istruzione crea un oggetto modulo chiamato math. Se visualizzate l’oggetto modulo, ottenete alcune informazioni a riguardo: >>> math <module 'math' (built-in)> L’oggetto modulo contiene le funzioni e le variabili definite all’interno del modulo stesso. Per chiamare una funzione inclusa in un modulo, dobbiamo specificare, nell’ordine, il nome del modulo che la contiene e il nome della funzione, separati da un punto. Questo formato è chiamato notazione a punto o dot notation. >>> rapporto = potenza_segnale / potenza_rumore >>> decibel = 10 * math.log10(rapporto) >>> radianti = 0.7 >>> altezza = math.sin(radianti) Il primo esempio utilizza la funzione Il secondo esempio calcola il seno della variabile radianti. Il nome della variabile spiega già che sin e le altre funzioni trigonometriche (cos, tan, ecc.) accettano argomenti espressi in radianti. Per convertire da gradi in radianti occorre dividere per 180 e moltiplicare per π: >>> gradi = 45 >>> radianti = gradi / 180.0 * math.pi >>> math.sin(radianti) 0.707106781187 L’espressione math.pi ricava la variabile pi dal modulo matematico. Il suo valore è un numero decimale, approssimazione di π, accurata a circa 15 cifre. Se ricordate la trigonometria, potete verificare il risultato precedente confrontandolo con la radice quadrata di 2 diviso 2: >>> math.sqrt(2) / 2.0 0.707106781187 3.3 ComposizioneFinora, abbiamo considerato gli elementi di un programma - variabili, espressioni e istruzioni - separatamente, senza parlare di come utilizzarli insieme. Una delle caratteristiche più utili dei linguaggi di programmazione è la loro capacità di prendere dei piccoli mattoni e comporli tra loro. Per esempio, l’argomento di una funzione può essere un qualunque tipo di espressione, incluse operazioni aritmetiche: x = math.sin(gradi / 360.0 * 2 * math.pi) E anche chiamate di funzione: x = math.exp(math.log(x+1)) Potete mettere quasi ovunque un valore o un’espressione a piacere, con una eccezione: il lato sinistro di una istruzione di assegnazione deve essere un nome di una variabile. Ogni altra espressione darebbe un errore di sintassi (vedremo più avanti le eccezioni a questa regola). >>> minuti = ore * 60 # giusto >>> ore * 60 = minuti # sbagliato! SyntaxError: can't assign to operator 3.4 Aggiungere nuove funzioniFinora abbiamo usato solo funzioni predefinite o “built-in”, che sono parte integrante di Python, ma è anche possibile crearne di nuove. Una definizione di funzione specifica il nome di una nuova funzione e la sequenza di istruzioni che viene eseguita quando la funzione viene chiamata. Ecco un esempio: def stampa_brani(): print("Terror di tutta la foresta egli è,") print("Con l'ascia in mano si sente un re.") def è una parola chiave riservata che indica la definizione di una nuova funzione. Il nome della funzione è Le parentesi vuote dopo il nome indicano che la funzione non accetta alcun argomento. La prima riga della definizione di funzione è chiamata intestazione; il resto è detto corpo. L’intestazione deve terminare con i due punti, e il corpo deve essere obbligatoriamente indentato, cioè deve avere un rientro rispetto all’intestazione. Per convenzione, l’indentazione è sempre di quattro spazi. Il corpo può contenere un qualsiasi numero di istruzioni. Le stringhe nelle istruzioni di stampa sono racchiuse tra virgolette ( Virgolette e apici devono essere alti e di tipo indifferenziato, quelli che trovate tra i simboli in alto sulla vostra tastiera. Altre virgolette “tipografiche”, come quelle in questa frase, non sono valide in Python. Se scrivete una funzione in modalità interattiva, l’interprete mette tre puntini di sospensione (...) per indicare che la definizione non è completa: >>> def stampa_brani(): ... print("Terror di tutta la foresta egli è,") ... print("Con l'ascia in mano si sente un re.") ... Per concludere la funzione, dovete inserire una riga vuota. La definizione di una funzione crea un oggetto funzione che è di tipo >>> print(stampa_brani) <function stampa_brani at 0xb7e99e9c> >>> type(stampa_brani) <class 'function'> La sintassi per chiamare la nuova funzione è la stessa che abbiamo visto per le funzioni predefinite: >>> stampa_brani() Terror di tutta la foresta egli è, Con l'ascia in mano si sente un re. Una volta definita una funzione, si può utilizzarla all’interno di un’altra funzione. Per esempio, per ripetere due volte il brano precedente possiamo scrivere una funzione def ripeti_brani(): stampa_brani() stampa_brani() E quindi chiamare >>> ripeti_brani() Terror di tutta la foresta egli è, Con l'ascia in mano si sente un re. Terror di tutta la foresta egli è, Con l'ascia in mano si sente un re. Ma a dire il vero, la canzone non fa così! 3.5 Definizioni e loro utilizzoRaggruppando assieme i frammenti di codice del Paragrafo precedente, il programma diventa: def stampa_brani(): print("Terror di tutta la foresta egli è,") print("Con l'ascia in mano si sente un re.") def ripeti_brani): stampa_brani() stampa_brani() ripeti_brani() Questo programma contiene due definizioni di funzione: Ovviamente, una funzione deve essere definita prima di poterla usare: la definizione della funzione deve sempre precedere la sua chiamata. Come esercizio, spostate l’ultima riga del programma all’inizio, per fare in modo che la chiamata della funzione appaia prima della definizione. Eseguite il programma e guardate che tipo di messaggio d’errore ottenete. Ora riportate la chiamata della funzione al suo posto, e spostate la definizione di 3.6 Flusso di esecuzionePer assicurarvi che una funzione sia definita prima del suo uso, dovete conoscere l’ordine in cui le istruzioni vengono eseguite, cioè il flusso di esecuzione del programma. L’esecuzione inizia sempre dalla prima riga del programma e le istruzioni sono eseguite una alla volta dall’alto verso il basso. Le definizioni di funzione non alterano il flusso di esecuzione del programma ma va ricordato che le istruzioni all’interno delle funzioni non vengono eseguite fino a quando la funzione non viene chiamata. Una chiamata di funzione è una sorta di deviazione nel flusso di esecuzione: invece di proseguire con l’istruzione successiva, il flusso salta alla prima riga della funzione chiamata ed esegue tutte le sue istruzioni; alla fine della funzione il flusso riprende dal punto dov’era stato deviato. Sinora è tutto abbastanza semplice, ma dovete tenere conto che una funzione può chiamarne un’altra al suo interno. Nel bel mezzo di una funzione, il programma può dover eseguire le istruzioni situate in un’altra funzione. Ma mentre esegue la nuova funzione, il programma può doverne eseguire un’altra ancora! Fortunatamente, Python sa tener bene traccia di dove si trova, e ogni volta che una funzione viene completata il programma ritorna al punto che aveva lasciato. Giunto all’ultima istruzione, dopo averla eseguita, il programma termina. In conclusione, quando leggete un programma non limitatevi sempre a farlo dall’alto in basso. Spesso ha più senso cercare di seguire il flusso di esecuzione. 3.7 Parametri e argomentiAlcune delle funzioni che abbiamo visto richiedono degli argomenti. Per esempio, se volete trovare il seno di un numero chiamando la funzione math.sin, dovete passarle quel numero come argomento. Alcune funzioni ricevono più di un argomento: a math.pow ne servono due, che sono la base e l’esponente dell’operazione di elevamento a potenza. All’interno della funzione, gli argomenti che le vengono passati sono assegnati ad altrettante variabili chiamate parametri. Ecco un esempio di definizione di una funzione che riceve un argomento: def stampa2volte(bruce): print(bruce) print(bruce) Questa funzione assegna l’argomento ricevuto ad un parametro chiamato bruce. Quando la funzione viene chiamata, stampa il valore del parametro (qualunque esso sia) due volte. Questa funzione lavora con qualunque valore che possa essere stampato. >>> stampa2volte('Spam') Spam Spam >>> stampa2volte(42) 42 42 >>> stampa2volte(math.pi) 3.14159265359 3.14159265359 Le stesse regole di composizione che valgono per le funzioni predefinite si applicano anche alle funzioni definite da un programmatore, pertanto possiamo usare come argomento per >>> stampa2volte('Spam '*4) Spam Spam Spam Spam Spam Spam Spam Spam >>> stampa2volte(math.cos(math.pi)) -1.0 -1.0 L’argomento viene valutato prima della chiamata alla funzione, pertanto nell’esempio appena proposto le espressioni Potete anche usare una variabile come argomento di una funzione: >>> michael = 'Eric, the half a bee.' >>> stampa2volte(michael) Eric, the half a bee. Eric, the half a bee. Il nome della variabile che passiamo come argomento (michael) non ha niente a che fare con il nome del parametro nella definizione della funzione (bruce). Non ha importanza come era stato denominato il valore di partenza (nel codice chiamante); qui in 3.8 Variabili e parametri sono localiQuando create una variabile in una funzione, essa è locale, cioè esiste solo all’interno della funzione. Per esempio: def cat2volte(parte1, parte2): cat = parte1 + parte2 stampa2volte(cat) Questa funzione prende due argomenti, li concatena e poi ne stampa il risultato due volte. Ecco un esempio che la utilizza: >>> riga1 = 'Bing tiddle ' >>> riga2 = 'tiddle bang.' >>> cat2volte(riga1, riga2) Bing tiddle tiddle bang. Bing tiddle tiddle bang. Quando >>> print(cat) NameError: name 'cat' is not defined Anche i parametri sono locali: al di fuori della funzione 3.9 Diagrammi di stackPer tenere traccia di quali variabili possono essere usate e dove, è talvolta utile disegnare un diagramma di stack. Come i diagrammi di stato, i diagrammi di stack mostrano il valore di ciascuna variabile, ma in più indicano a quale funzione essa appartiene. Ogni funzione è rappresentata da un frame, un riquadro con il nome della funzione a fianco e la lista dei suoi parametri e delle sue variabili all’interno. Il diagramma di stack nel caso dell’esempio precedente, è illustrato in Figura 3.1. I frame sono disposti in una pila che indica quale funzione ne ha chiamata un’altra e così via. Nell’esempio, Ogni parametro fa riferimento allo stesso valore del suo argomento corrispondente. Così, parte1 ha lo stesso valore di riga1, parte2 ha lo stesso valore di riga2, e bruce ha lo stesso valore di cat. Se si verifica un errore durante la chiamata di una funzione, Python mostra il nome della funzione, il nome della funzione che l’ha chiamata, il nome della funzione che a sua volta ha chiamato quest’ultima e così via, fino a raggiungere il primo livello che è sempre Ad esempio se cercate di accedere a cat dall’interno di
Traceback (innermost last): File "test.py", line 13, in __main__ cat2volte(riga1, riga2) File "test.py", line 5, in cat2volte stampa2volte(cat) File "test.py", line 9, in stampa2volte print(cat) NameError: name 'cat' is not defined Questo elenco di funzioni è detto traceback. Il traceback vi dice in quale file è avvenuto l’errore, e in quale riga, e quale funzione era in esecuzione in quel momento. Mostra anche la riga di codice che ha causato l’errore. L’ordine delle funzioni nel traceback è lo stesso di quello dei frame nel diagramma di stack. La funzione attualmente in esecuzione si trova in fondo all’elenco. 3.10 Funzioni “produttive” e funzioni “vuote”Alcune delle funzioni che abbiamo usato, tipo le funzioni matematiche, restituiscono dei risultati; in mancanza di definizioni migliori, personalmente le chiamo funzioni “produttive”. Altre funzioni, come Quando chiamate una funzione produttiva, quasi sempre è per fare qualcosa di utile con il suo risultato, tipo assegnarlo a una variabile o usarlo come parte di un’espressione. x = math.cos(radianti) aureo = (math.sqrt(5) + 1) / 2 Se chiamate una funzione in modalità interattiva, Python ne mostra il risultato: >>> math.sqrt(5) 2.2360679774997898 Ma in uno script, se chiamate una funzione produttiva così come è, il valore di ritorno è perso! math.sqrt(5) Questo script in effetti calcola la radice quadrata di 5, ma non conserva nè visualizza il risultato, per cui non è di grande utilità. Le funzioni vuote possono visualizzare qualcosa sullo schermo o avere qualche altro effetto, ma non restituiscono un valore. Se provate comunque ad assegnare il risultato ad una variabile, ottenete un valore speciale chiamato None (nulla). >>> risultato = stampa2volte('Bing') Bing Bing >>> print(risultato) None Il valore None non è la stessa cosa della stringa >>> type(None) <class 'NoneType'> Le funzioni che abbiamo scritto finora, sono tutte vuote. Cominceremo a scriverne di produttive tra alcuni capitoli. 3.11 Perché le funzioni?Potrebbe non esservi ancora ben chiaro perché valga la pena di suddividere il programma in funzioni. Ecco alcuni motivi:
3.12 DebugUna delle più importanti abilità che acquisirete è quella di effettuare il debug (o “rimozione degli errori”). Sebbene questa possa essere un’operazione noiosa, è anche una delle parti più intellettualmente vivaci, stimolanti ed interessanti della programmazione. In un certo senso, il debug può essere paragonato al lavoro investigativo. Siete messi di fronte a degli indizi e dovete ricostruire i processi e gli eventi che hanno portato ai risultati che avete ottenuto. Il debug è come una scienza sperimentale: dopo aver ipotizzato quello che può essere andato storto, modificate il programma e riprovate. Se l’ipotesi era corretta, allora avete saputo predire il risultato della modifica e vi siete avvicinati di un ulteriore passo verso un programma funzionante. Se l’ipotesi era sbagliata, dovete formularne un’altra. Come disse Sherlock Holmes: “Quando hai eliminato l’impossibile, qualsiasi cosa rimanga, per quanto improbabile, deve essere la verità.” (A. Conan Doyle, Il segno dei quattro) Per alcuni, programmazione e debug sono la stessa cosa, intendendo con questo che la programmazione è un procedimento di graduale rimozione degli errori, fino a quando il programma non fa quello che vogliamo. L’idea è quella di partire da un programma funzionante, e fare via via piccole modifiche con rimozione degli errori. Linux, per fare un esempio, è un sistema operativo che contiene milioni di righe di codice, ma nacque come un semplice programma che Linus Torvalds usò per esplorare il chip Intel 80386. Secondo Larry Greenfields, “Uno dei progetti iniziali di Linus era un programma che doveva scambiare una sequenza di AAAA in BBBB e viceversa. Questo in seguito diventò Linux”. (The Linux Users’ Guide Beta Version 1). 3.13 Glossario
3.14 EserciziEsercizio 1
Scrivete una funzione chiamata >>> giustif_destra('monty') monty Suggerimento: usate concatenamento delle stringhe e ripetizione. Inoltre,
Python contiene una funzione predefinita chiamata len che restituisce la lunghezza di una stringa, ad esempio il valore di
Esercizio 2
Un oggetto funzione è un valore che potete assegnare a una variabile o passare come argomento. Ad esempio, def fai2volte(f): f() f() Ecco un esempio che usa def stampa_spam(): print('spam') fai2volte(stampa_spam)
Soluzione: http://thinkpython2.com/code/do_four.py.
Esercizio 3
Nota: questo esercizio dovrebbe essere svolto con le sole istruzioni e caratteristiche del linguaggio imparate finora.
Soluzione: http://thinkpython2.com/code/grid.py. Fonte: Esercizio tratto da Oualline, Practical C Programming, Third Edition, O’Reilly Media, 1997. |
ContributeIf you would like to make a contribution to support my books, you can use the button below. Thank you!
Are you using one of our books in a class?We'd like to know about it. Please consider filling out this short survey.
|