[successivo] [precedente] [inizio] [fine] [indice generale] [indice analitico] [parte]
Nelle sezioni seguenti sono descritti alcuni problemi elementari attraverso cui si insegnano le tecniche di programmazione ai principianti. Assieme ai problemi vengono proposte le soluzioni in forma di programma Python. Per le soluzioni in forma di pseudocodifica si rimanda agli Appunti di informatica libera di Daniele Giacomini.
|
La moltiplicazione di due numeri positivi, può essere espressa attraverso il concetto della somma: n*m equivale a sommare m volte n, oppure n volte m. L'algoritmo risolutivo è banale, ma utile per apprendere il funzionamento dei cicli.
|
Nel listato 3.2 viene mostrata una soluzione per mezzo di un ciclo enumerativo. Il ciclo viene ripetuto y volte, incrementando la variabile z del valore di x. Alla fine, z contiene il risultato del prodotto di x per y. Il frammento seguente mostra invece la traduzione del ciclo enumerativo in un ciclo iterativo:
|
La divisione di due numeri positivi, può essere espressa attraverso la sottrazione: n/m equivale a sottrarre m da n fino a quando n diventa inferiore di m. Il numero di volte in cui tale sottrazione ha luogo, è il risultato della divisione.
|
Il listato 3.4 realizza l'algoritmo; si noti anche l'uso dell'operatore di formato.
L'elevamento a potenza, utilizzando numeri positivi, può essere espresso attraverso il concetto della moltiplicazione: n**m equivale a moltiplicare m volte n per se stesso.
|
Nel listato 3.5 viene mostrata una soluzione per mezzo di un ciclo enumerativo. Il ciclo viene ripetuto y volte; ogni volta la variabile z viene moltiplicata per il valore di x, a partire da 1. Alla fine, z contiene il risultato dell'elevamento di x a y. Il frammento seguente mostra invece la traduzione del ciclo enumerativo in un ciclo iterativo:
|
Il frammento seguente mostra una soluzione ricorsiva:
|
Il calcolo della parte intera della radice quadrata di un numero si può fare per tentativi, partendo da 1, eseguendo il quadrato fino a quando il risultato è minore o uguale al valore di partenza di cui si calcola la radice.
Il listato 3.8 realizza l'algoritmo.
|
Il fattoriale è un valore che si calcola a partire da un numero positivo. Può essere espresso come il prodotto di n per il fattoriale di n-1, quando n è maggiore di 1, mentre equivale a 1 quando n è uguale a 1. In pratica, n! = n * (n-1) * (n-2)... *1.
|
La soluzione mostrata nel listato 3.9 fa uso di un ciclo iterativo in cui l'indice i, che inizialmente contiene il valore di x-1, viene usato per essere moltiplicato al valore di x, riducendolo ogni volta di un'unità. Quando i raggiunge lo 0, il ciclo termina e x contiene il valore del fattoriale. L'esempio seguente mostra invece una soluzione ricorsiva che dovrebbe risultare più intuitiva:
|
Il massimo comune divisore tra due numeri può essere ottenuto sottraendo a quello maggiore il valore di quello minore, fino a quando i due valori sono uguali. Quel valore è il massimo comune divisore.
Il listato 3.11 realizza l'algoritmo.
|
Un numero intero è numero primo quando non può essere diviso per un altro intero diverso dal numero stesso e da 1, generando un risultato intero.
Il listato 3.12 realizza l'algoritmo.
|
Nelle sezioni seguenti sono descritti alcuni problemi legati alla scansione di array. Assieme ai problemi vengono proposte le soluzioni in forma di programmi Python.
La ricerca di un elemento all'interno di un array disordinato può avvenire solo in modo sequenziale, cioè controllando uno per uno tutti gli elementi, fino a quando si trova la corrispondenza cercata. La tabella 3.13 presenta la descrizione delle variabili più importanti che appaiono nei programmi Python successivi.
Il listato 3.14 presenta un esempio di programma Python che risolve il problema in modo iterativo.
|
L'algoritmo usato dovrebbe risultare abbastanza chiaro.
Esiste anche una soluzione ricorsiva che viene mostrata nel frammento seguente:
|
La ricerca di un elemento all'interno di un array ordinato può avvenire individuando un elemento centrale: se questo corrisponde all'elemento cercato, la ricerca è terminata, altrimenti si ripete nella parte di array precedente o successiva all'elemento, a seconda del suo valore e del tipo di ordinamento esistente.
Il problema posto in questi termini è ricorsivo. Il programma mostrato nel listato 3.16 utilizza le stesse variabili già descritte per la ricerca sequenziale.
|
Nelle sezioni seguenti sono descritti alcuni problemi classici attraverso cui si insegnano le tecniche di programmazione. Assieme ai problemi vengono proposte le soluzioni in forma di programma Python.
Il Bubblesort è un algoritmo relativamente semplice per l'ordinamento di un array, in cui ogni scansione trova il valore giusto per l'elemento iniziale dell'array stesso. Una volta trovata la collocazione di un elemento, si ripete la scansione per il segmento rimanente di array, in modo da collocare un altro valore. Il testo del programma dovrebbe chiarire il meccanismo. La tabella 3.17 presenta la descrizione delle variabili più importanti utilizzate dal programma.
Nel listato 3.18 viene mostrata una soluzione iterativa.
|
Vale la pena di osservare nelle ultime due righe alcuni aspetti idiomatici di Python: avendo a disposizione una lista è possibile utilizzare i suoi elementi come indici di un ciclo enumerativo utilizzando la consueta parola chiave in; inoltre, se si intende che l'output non vada a capo ma prosegua sulla medesima riga, si può usare il carattere vigola (,) in coda all'istruzione print. |
Segue la funzione bsort in versione ricorsiva:
|
Si tenga presente che in questa sezione, come in quelle sugli algoritmi di ricerca, l'istruzione di lettura degli argomenti della linea di comando non prevede la trasformazione da stringa a intero; questo perché, in generale, ci si può aspettare che una lista sia costituita da elementi non numerici.
Tuttavia, si consideri la seguente situazione:
$
./bsort.py 3 5 8 2 9 4512 7 67431 3 6 3
[Invio]
2 3 3 3 4512 5 6 67431 7 8 9 |
Ovviamente non è quello che ci si aspetta; in realtà, l'output è comprensibile se si tiene presente che gli argomenti vengono trattati come stringhe, perciò la lista viene ordinata secondo l'ordine lessicografico basato sul codice ASCII (in pratica l'ordine alfabetico esteso).
Se si vuole correggere il comportamanto del programma, è possibile sostituire la riga
|
con la riga
|
Con l'occasione, si noti un'ulteriore interessante aspetto idiomatico di Python: è possibile applicare una funzione per trasformare tutti i membri di una lista utilizzando il costrutto:
map(funzione, lista) |
Si invita il lettore interessato a consultare la documentazione di Python per ulteriori aspetti riguardanti la gestione delle liste.
Ecco il comportamento del programma modificato:
$
./bsort.py 3 5 8 2 9 4512 7 67431 3 6 3
[Invio]
2 3 3 3 5 6 7 8 9 4512 67431 |
Vale la pena notare che, così modificato, il programma non funziona se gli argomenti passatigli non possono essere interpretati come numeri interi.
La torre di Hanoi è un gioco antico: si compone di tre pioli identici conficcati verticalmente su una tavola e di una serie di anelli di larghezze differenti. Gli anelli sono più precisamente dei dischi con un foro centrale che permette loro di essere infilati nei pioli.
Il gioco inizia con tutti gli anelli collocati in un solo piolo, in ordine, in modo che in basso ci sia l'anello più largo e in alto quello più stretto. Si deve riuscire a spostare tutta la pila di anelli in un dato piolo muovendo un anello alla volta e senza mai collocare un anello più grande sopra uno più piccolo.
La figura 3.24 illustra la situazione iniziale della torre di Hanoi all'inizio del gioco.
|
Nella figura 3.24 gli anelli appaiono inseriti sul piolo 1; si supponga che questi debbano essere spostati sul piolo 2. Si può immaginare che tutti gli anelli, meno l'ultimo, possano essere spostati in qualche modo corretto, dal piolo 1 al piolo 3, come nella situazione della figura 3.25.
|
A questo punto si può spostare l'ultimo anello rimasto (l'n-esimo), dal piolo 1 al piolo 2; quindi, come prima, si può spostare in qualche modo il gruppo di anelli posizionati attualmente nel piolo 3, in modo che finiscano nel piolo 2 sopra l'anello più grande.
Pensando in questo modo, l'algoritmo risolutivo del problema deve essere ricorsivo e potrebbe essere gestito da un'unica funzione che può essere chiamata opportunamente hanoi, i cui parametri sono presentati nella tabella 3.26.
|
Il listato 3.27 presenta il programma Python con funzione ricorsiva per la soluzione del problema.
|
Si colga l'occasione per osservare un'ulteriore aspetto idiomatico di Python, ossia la possibilità di assegnare «in parallelo» gli elementi di una lista a più variabili (o, come si dice in gergo Python, una tupla) con una singola istruzione di assegnamento. |
Tornando al problema, ecco l'analisi dell'algoritmo risolutivo: se n, il numero degli anelli da spostare, è minore di 1, non si deve compiere alcuna azione. Se n è uguale a 1, le istruzioni controllate dalla struttura condizionale vengono eseguite, ma nessuna delle chiamate ricorsive fa alcunché, dato che n-1 è pari a 0. In questo caso, supponendo che n sia uguale a 1, che p1 sia pari a 1 e p2 pari a 2, il risultato è semplicemente:
Muovi l'anello 1 dal piolo 1 al piolo 2 |
Il risultato è quindi corretto per una pila iniziale consistente di un solo anello.
Se n è uguale a 2, la prima chiamata ricorsiva sposta un anello (n-1 = 1) dal piolo 1 al piolo 3 (ancora assumendo che i due anelli debbano essere spostati dal primo al terzo piolo) e si sa che questa è la mossa corretta. Quindi viene stampato il messaggio che dichiara lo spostamento del secondo piolo (l'n-esimo) dalla posizione 1 alla posizione 2. Infine, la seconda chiamata ricorsiva si occupa di spostare l'anello collocato precedentemente nel terzo piolo, nel secondo, sopra a quello che si trova già nella posizione finale corretta.
In pratica, nel caso di due anelli che devono essere spostati dal primo al secondo piolo, appaiono i tre messaggi seguenti:
Muovi l'anello 1 dal piolo 1 al piolo 3 Muovi l'anello 2 dal piolo 1 al piolo 2 Muovi l'anello 1 dal piolo 3 al piolo 2 |
Nello stesso modo si potrebbe dimostrare il funzionamento per un numero maggiore di anelli.
L'ordinamento degli elementi di un array è un problema tipico che si può risolvere in tanti modi. Il Quicksort è un algoritmo sofisticato, ottimo per lo studio della gestione degli array, oltre che per quello della ricorsione. Il concetto fondamentale di questo tipo di algoritmo è rappresentato dalla figura 3.30.
|
Una sola scansione dell'array è sufficiente per collocare definitivamente un elemento (per esempio il primo) nella sua destinazione finale e allo stesso tempo per lasciare tutti gli elementi con un valore inferiore a quello da una parte, anche se disordinati, e tutti quelli con un valore maggiore, dall'altra.
In questo modo, attraverso delle chiamate ricorsive, è possibile elaborare i due segmenti dell'array rimasti da riordinare.
L'algoritmo può essere descritto grossolanamente come:
localizzazione della collocazione finale del primo valore, separando in questo modo i valori;
ordinamento del segmento precedente all'elemento collocato definitivamente;
ordinamento del segmento successivo all'elemento collocato definitivamente.
Viene qui indicata con part la funzione che esegue la scansione dell'array, o di un suo segmento, per determinare la collocazione finale (indice cf) del primo elemento (dell'array o del segmento in questione).
Sia lista l'array da ordinare. Il primo elemento da collocare corrisponde inizialmente a lista[a] e il segmento di array su cui intervenire corrisponde a lista[a:z+1] (cioè a tutti gli elementi che vanno dall'indice a all'indice z).
Alla fine della prima scansione, l'indice cf rappresenta la posizione in cui occorre spostare il primo elemento, cioè lista[a]. In pratica, lista[a] e lista[cf] vengono scambiati.
Durante la scansione che serve a determinare la collocazione finale del primo elemento, part deve occuparsi di spostare gli elementi prima o dopo quella posizione, in funzione del loro valore, in modo che alla fine quelli inferiori o uguali a quello dell'elemento da collocare si trovino nella parte inferiore e gli altri dall'altra. In pratica, alla fine della prima scansione, gli elementi contenuti in lista[a:cf] devono contenere valori inferiori o uguali a lista[cf], mentre quelli contenuti in lista[cf+1:z+1] devono contenere valori superiori.
Indichiamo con qsort la funzione che esegue il compito complessivo di ordinare l'array. Il suo lavoro consisterebbe nel chiamare part per collocare il primo elemento, continuando poi con la chiamata ricorsiva di se stessa per la parte di array precedente all'elemento collocato e infine alla chiamata ricorsiva per la parte restante di array.
Assumendo che part e le chiamate ricorsive di qsort svolgano il loro compito correttamente, si potrebbe fare un'analisi informale dicendo che se l'indice z non è maggiore di a, allora c'è un elemento (o nessuno) all'interno di lista[a:z+1] e inoltre, lista[a:z+1] è già nel suo stato finale. Se z è maggiore di a, allora (per assunzione) part ripartisce correttamente lista[a:z+1]. L'ordinamento separato dei due segmenti (per assunzione eseguito correttamente dalle chiamate ricorsive) completa l'ordinamento di lista[a:z+1].
Le figure 3.31 e 3.32 mostrano due fasi della scansione effettuata da part all'interno dell'array o del segmento che gli viene fornito.
|
|
In pratica, l'indice i, iniziando dal valore a+1, viene spostato verso destra fino a che viene trovato un elemento maggiore di lista[a], quindi è l'indice cf a essere spostato verso sinistra, iniziando dalla stessa posizione di z, fino a che viene incontrato un elemento minore o uguale a lista[a]. Questi elementi vengono scambiati e lo spostamento di i e cf riprende. Ciò prosegue fino a che i e cf si incontrano, momento in cui lista[a:z+1] è stata ripartita e cf rappresenta la collocazione finale per l'elemento lista[l].
La tabella 3.33 riassume la descrizione delle variabili utilizzate.
|
Il listato 3.34 presenta il programma Python che include le due funzioni.
|
Vale la pena di osservare che l'array viene indicato nelle chiamate in modo che alla funzione sia inviato un riferimento a quello originale, perché le variazioni fatte all'interno delle funzioni devono riflettersi sull'array originale.
In Python, non è necessario alcun particolare accorgimento sintattico per garantire questo comportamento: infatti, le liste costituiscono un tipo di dato mutabile (secondo la terminologia Python), alla stessa stregua di altri tipi come i dizionari (per i quali si rinvia il lettore alla documentazione del linguaggio(1)); quando un oggetto mutabile viene passato come argomento a una funzione, avviene un assegnamento al corrispondente parametro formale; l'assegnamento di un oggetto mutabile a una variabile è realizzato in Python mediante il cosiddetto meccanismo dell'aliasing: in pratica la nuova variabile coincide in tutto e per tutto con l'oggetto assegnato(2), e in particolare se quest'ultimo cambia valore tale cambiamento si riflette sulla nuova variabile. Pertanto, le variazioni fatte sui parametri formali all'interno di funzioni che ricevono come argomenti delle liste, si riflettono sulle liste originali.
Ecco un esempio che può aiutare a chiarire la questione (si tratta di una sessione interattiva dell'interprete Python):
$
python
[Invio]
Python 2.3.4 (#2, Jul 5 2004, 09:15:05) [GCC 3.3.4 (Debian 1:3.3.4-2)] on linux2 Type "help", "copyright", "credits" or "license" for more information. |
>>>
lista = [1, 3, 7, 0, 10]
[Invio]
>>>
altra_lista = lista
[Invio]
>>>
altra_lista[3] = 1000
[Invio]
>>>
print lista
[Invio]
[1, 3, 7, 1000, 10] |
>>>
[Ctrl d]
$
La permutazione è lo scambio di un gruppo di elementi posti in sequenza. Il problema che si vuole analizzare è la ricerca di tutte le permutazioni possibili di un dato gruppo di elementi.
Se ci sono n elementi in un array, allora alcune delle permutazioni si possono ottenere bloccando l'n-esimo elemento e generando tutte le permutazioni dei primi elementi. Quindi l'n-esimo elemento può essere scambiato con uno dei primi n-1, ripetendo poi la fase precedente. Questa operazione deve essere ripetuta finché ognuno degli n elementi originali è stato usato nell'n-esima posizione.
Il listato 3.38 presenta il programma Python, le cui variabili più importanti sono descritte nella tabella 3.37.
|
Si colga l'occasione per notare un paio di aspetti idiomatici di Python. Per prima cosa, è possibile usare la funzione range per costruire un ciclo enumerativo decrescente, poiché range accetta un terzo argomento che in pratica rappresenta il passo con cui viene generata la successione di valori che popola la lista; ecco alcuni esempi:
$
python
[Invio]
Python 2.3.4 (#2, Jul 5 2004, 09:15:05) [GCC 3.3.4 (Debian 1:3.3.4-2)] on linux2 Type "help", "copyright", "credits" or "license" for more information. |
>>>
range(10)
[Invio]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] |
>>>
range(1, 11)
[Invio]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] |
>>>
range(0, 30, 5)
[Invio]
[0, 5, 10, 15, 20, 25] |
>>>
range(0, 10, 3)
[Invio]
[0, 3, 6, 9] |
>>>
range(0, -10, -1)
[Invio]
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9] |
>>>
range(0)
[Invio]
[] |
>>>
range(1, 0)
[Invio]
[] |
>>>
[Ctrl d]
$
si noti in particolare che, al solito, il secondo argomento denota il primo valore escluso dalla successione. Si noti infine l'uso dell'istruzione print isolata, allo scopo di andare a capo (ciò è necessario poiché nel ciclo enumerativo precedente si era usato il carattere vigola (,) in coda, il quale sopprime il carattere di newline).
In questa sezione viene presentato e commentato un semplice programma interattivo, numeri.py, con lo scopo di illustrare alcune ulteriori caratteristiche (sintattiche e idiomatiche) del linguaggio Python.
Prima del sorgente, è interessante vedere un esempio di esecuzione del programma, allo scopo di comprenderne meglio il funzionamento. Trattandosi di un programma interattivo, la sessione presentata dovrebbe commentarsi da sé.
$
./numeri.py
[Invio]
******** Numeri ******** Con questo programma e` possibile effettuare alcune operazioni matematiche su operandi scelti dall'utente. |
Scegliere su quanti operandi operare [2-10]:
pippo
[Invio]
Non e` un numero intero. Riprova... |
Scegliere su quanti operandi operare [2-10]:
1
[Invio]
Il numero dev'essere compreso fra 2 e 10. Riprova... |
Scegliere su quanti operandi operare [2-10]:
3
[Invio]
L'utente ha scelto di operare su 3 operandi. Inserire gli operandi su cui operare: |
Operando 0:
-7.9
[Invio]
Operando 1:
pippo
[Invio]
Non e` un numero. Riprova... |
Operando 1:
0.25
[Invio]
Operando 2:
123456
[Invio]
L'utente ha scelto di operare sui seguenti operandi: -7.9, 0.25, 123456.0 Operazioni consentite: 0 - Termina 1 - Addizione 2 - Moltiplicazione 3 - Massimo 4 - Minimo 5 - Media |
Scegliere un'operazione [1-5, 0 per terminare]:
2
[Invio]
Scegliere un'operazione [1-5, 0 per terminare]:
1
[Invio]
Scegliere un'operazione [1-5, 0 per terminare]:
9
[Invio]
Il numero dev'essere compreso fra 0 e 5. Riprova... |
Scegliere un'operazione [1-5, 0 per terminare]:
5
[Invio]
Scegliere un'operazione [1-5, 0 per terminare]:
0
[Invio]
Il prodotto degli operandi e` -243825.6 La somma degli operandi e` 123448.35 La media degli operandi e` 61726.0875 |
Desideri ricominciare da capo? [s/N]:
n
[Invio]
Grazie per aver utilizzato il programma numeri.py! |
$
Il listato 3.56 presenta il codice sorgente del programma numeri.py.
|
Commentiamo gli aspetti principali del programma. Cominciamo l'analisi dal livello più esterno:
Righe 157-176
Vengono definite alcune variabili (o nomi secondo la terminologia Python) utilizzate dal programma principale.
La variabile operazioni_possibili è un dizionario che serve a mantenere una corrispondenza fra stringhe (che contengono dei nomi convenzionali di funzioni) e funzioni (le quali possono essere incorporate oppure appartenenti a un modulo esterno oppure ancora definite altrove nel programma stesso). nomi_risultati_operazioni è un dizionario che mantiene una corrispondenza fra fra stringhe che contengono dei nomi convenzionali di funzioni e stringhe che contengono i nomi tradizionali che esprimono il risultato del calcolo delle funzioni stesse. nomi_operazioni è una lista che contiene i nomi delle funzioni che l'utente può di volta in volta scegliere di utilizzare. operazioni_n_arie è una lista che contiene i nomi delle funzioni che sono definite in modo da operare su 2 o più argomenti (le rimanenti funzioni operano esattamente su 2 argomenti).
Righe 177-205
È il programma principale. Si compone essenzialmente di un ciclo iterativo che si ripete fintantoché la variabile booleana continua risulta vera. La successione dei passi che compongono il corpo del ciclo è la seguente:
richiedere all'utente su quanti operandi operare, e assegnazione di tale valore a quanti_operandi;
richiedere all'utente su quali operandi operare, e assegnazione di tali valori alla lista operandi;
richiedere all'utente con quali operazioni operare, e assegnazione di tali valori alla lista operazioni;
calcolo e presentazione dei risultati; richiesta all'utente se vuole continuare con una nuova iterazione e assegnazione corrispondente alla variabile continua.
All'uscita del ciclo il programma termina dopo essersi accomiatato dall'utente.
I vari passi che costituiscono il ciclo principale sono realizzati mediante funzioni che restituiscono un valore adeguato alla necessità. Procediamo ad analizzare gli aspetti maggiormente qualificanti di tali funzioni.
Righe 31-39
La funzione Elenca_operandi elenca i membri della lista operandi separati da virgole.
Si noti l'idioma tipico per iterare su di una lista fino al penultimo membro:
Ciò è necessario per trattare l'ultimo membro come caso speciale. |
Righe 40-47
La funzione Elenca_operazioni_possibili emette un elenco numerato delle operazioni possibili per permettere all'utente la scelta.
Si noti il tipico idioma per iterare su di una lista attraverso gli indici invece che attraverso i membri:
|
Righe 48-71
La funzione Richiesta_numero_operandi() riceve in input il numero di operandi su cui operare e lo restituisce al chiamante; effettua anche un controllo piuttosto dettagliato dell'input, tramite i costrutti Python per la gestione delle eccezioni(3): try, except e else. In pratica la funzione esegue un ciclo infinito (while True:) all'interno del quale riceve l'input e cerca (try:) di convertirlo in un intero (...int(raw_input(...); se la conversione fallisce (except ValueError:) il ciclo continua; se la conversione ha successo ma il valore non è consentito il ciclo continua; altrimenti il ciclo termina (break(4)).
Righe 72-92
La funzione Richiesta_inserimento_operandi costruisce la lista degli operandi ricevuti in input e la restituisce al chiamante; il controllo dell'input viene effettuato con la tecnica già vista della gestione delle eccezioni.
Si noti l'idioma tipico per l'estensione di una lista con un membro in coda:
|
Righe 93-127
La funzione Richiesta_scelta_operazioni chiama a sua volta Elenca_operandi e Elenca_operazioni_possibili, dopodiché prepara e restituisce al chiamante la lista delle operazioni (ossia delle stringhe contenenti i nomi delle operazioni come da dizionario operazioni_possibili) richiesta dall'utente. L'utente sceglie le operazioni mediante il numero prograssivo mostrato da Elenca_operazioni_possibili e l'input viene via via controllato mediante una tecnica simile a quelle già incontrate. Si noti inoltre:
la struttura condizionale if not opz_n: la quale ha successo esattamente quando opz_n vale 0(5); in tal caso il ciclo termina;
l'istruzione di estensione della lista delle operazioni richieste è controllata dalla struttura condizionale if nomi_operazioni[opz_n] not in operazioni: per far sì che non venano inseriti doppioni nella lista.
Si tratta di un idioma Python tipico per controllare la presenza di un membro in una lista:
oppure:
|
Righe 128-146
La funzione Calcolo_e_presentazione_risultati calcola le operazioni richieste sugli operandi indicati, presenta i risultati e chiede all'utente se desidera ricominciare. L'applicazione delle funzioni che realizzano le operazioni agli operandi viene effettuata in due modi diversi a seconda che la funzione sia binaria (ossia lavori esattamente su 2 argomenti) oppure ternaria o più: nel primo caso su utilizza la funzione Python reduce la quale per l'appunto estende le funzioni binarie al caso di più argomenti(6).
raw_input(... chiede all'utente di inserire un'input; la stringa viene convertita in minuscole tramite il metodo lower; il metodo startswith("s") restituisce un valore booleano a seconda che la stringa cui viene applicato inizi o meno con "s".
Le variabili di cui alle righe 157-176, essendo dichiarate nel blocco più esterno del sorgente, queste potrebbero essere chiamate «variabili globali», secondo una tradizione consolidata: in effetti tali nomi sono accessibili (in lettura) in tutto il programma, mentre l'accesso in scrittura è possibile solamente nel livello più esterno.
Più precisamente, in Python è possibile accedere - a un livello più interno - a un nome dichiarato al livello più esterno, anche in scrittura, ma:
il valore associato al nome è quello del livello esterno fino alla modifica, e
la modifica perde effetto appena il controllo ritorna al livello esterno.
È possibile alterare quest'ultimo comportamento utilizzando la parola chiave global: dichiarando come global un nome, ogni modifica al livello interno si riflette al livello esterno. Ad esempio:
|
$
python tmp/global.py
[Invio]
sono il programma principale x: 42 sono funz x: 0 sono il programma principale x: 0 |
$
Senza la dichiarazione global x l'esecuzione avrebbe fornito il seguente output:
sono il programma principale x: 42 sono funz x: 0 sono il programma principale x: 42 |
Per evitare ambiguità, nel seguito chiameremo nomi o variabili global i nomi Python dichiarati con la parola chiave global.
Informatica per sopravvivere 2006.02.19 --- Copyright © 2004-2006 Massimo Piai -- <pxam67 (ad) virgilio·it>
1) Essenzialmente un dizionario è un tipo speciale di array: in pratica si tratta di una collezione di coppie chiave-valore, in cui le chiavi possono essere di qualsiasi tipo immutabile, e non solamente numeri interi; corrispondono in pratica agli array associativi del linguaggio Perl.
2) In altri termini: argomento e parametro formale sono due nomi dello stesso oggetto Python.
3) Trattasi di errori non sintattici non necessariamente fatali intercettati in fase di esecuzione.
4) Si tenga presente che la parola chiave può comparire solo nel corpo di un ciclo (enumerativo oppure iterativo) e ha l'effetto di terminare immediatamente il ciclo più interno fra quelli che la includono.
5) In Python sono considerati valori booleani falsi (False): None, lo zero numerico di qualunque tipo, la stringa vuota (""), la tupla vuota (()), la lista vuota ([]), il dizionario vuoto ({}). Tutti gli altri valori sono interpretati come veri (True).
6) reduce viene utilizzata anche nelle righe 11-18 ove viene definita la funzione med la quale calcola la media artitmetica dei membri della lista che le viene passata come argomento.
Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome ulteriori_esempi_di_programmi_python.html
[successivo] [precedente] [inizio] [fine] [indice generale] [indice analitico]