<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Informatica Etica Howto</title>
	<atom:link href="http://informaticaetica.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://informaticaetica.wordpress.com</link>
	<description>by Marco Costanzo</description>
	<lastBuildDate>Thu, 21 Apr 2011 00:46:03 +0000</lastBuildDate>
	<language>it</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='informaticaetica.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Informatica Etica Howto</title>
		<link>http://informaticaetica.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://informaticaetica.wordpress.com/osd.xml" title="Informatica Etica Howto" />
	<atom:link rel='hub' href='http://informaticaetica.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Imparare a programmare</title>
		<link>http://informaticaetica.wordpress.com/2009/06/05/imparare-a-programmare/</link>
		<comments>http://informaticaetica.wordpress.com/2009/06/05/imparare-a-programmare/#comments</comments>
		<pubDate>Fri, 05 Jun 2009 08:15:36 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[guide e manuali]]></category>
		<category><![CDATA[programmare]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://informaticaetica.wordpress.com/?p=5</guid>
		<description><![CDATA[Da http://www.python.it/doc/libri.html La versione Python del libro di Allen Downey : How to Think Like a Computer Scientist. È in assoluto il migliore libro scritto per principianti, libero, disponibile in tutti i formati. Si concentra sul linguaggio di programmazione Python &#8230; <a href="http://informaticaetica.wordpress.com/2009/06/05/imparare-a-programmare/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=5&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Da <a title="http://www.python.it/doc/libri.html" href="http://www.python.it/doc/libri.html">http://www.python.it/doc/libri.html</a></p>
<p>La versione Python del libro di Allen Downey : How to Think       Like a Computer Scientist.</p>
<p>È in assoluto il migliore libro scritto per       principianti, libero, disponibile in tutti i formati. Si       concentra sul linguaggio di programmazione Python ed affronta       praticamente tutti gli aspetti della programmazione, è usato in       alcuni istituti scolastici superiori americani. Imperdibile per       coloro che si avvicinano adesso a questa disciplina. L&#8217;unica       critica che si può rivolgere a questo testo è che non è       aggiornato con le ultime versioni del linguaggio, però, vista       l&#8217;utenza a cui si rivolge non è un problema. Chiunque       acquisisca le conoscenze tratte da questo libro non avrà alcun       problema ad &#8220;aggiornarsi&#8221;&#8230;<br />
Pensare da informatico</p>
<p>Versione Python</p>
<p>di Allen B. Downey, Jeffrey Elkner e Chris Meyers<br />
Traduzione di Alessandro Pocaterra</p>
<p>Sommario<br />
Introduzione<br />
Prefazione<br />
Lista dei collaboratori<br />
Note sulla traduzione<br />
Capitolo 1: Imparare a programmare<br />
Capitolo 2: Variabili, espressioni ed istruzioni<br />
Capitolo 3: Funzioni<br />
Capitolo 4: Istruzioni condizionali e ricorsione<br />
Capitolo 5: Funzioni produttive<br />
Capitolo 6: Iterazione<br />
Capitolo 7: Stringhe<br />
Capitolo 8: Liste<br />
Capitolo 9: Tuple<br />
Capitolo 10: Dizionari<br />
Capitolo 11: File ed eccezioni<br />
Capitolo 12: Classi e oggetti<br />
Capitolo 13: Classi e funzioni<br />
Capitolo 14: Classi e metodi<br />
Capitolo 15: Insiemi di oggetti<br />
Capitolo 16: Ereditarietà<br />
Capitolo 17: Liste linkate<br />
Capitolo 18: Pile<br />
Capitolo 19: Code<br />
Capitolo 20: Alberi<br />
Appendice A: Debug<br />
Appendice B: Creazione di un nuovo tipo di dato<br />
Appendice C: Listati dei programmi<br />
Appendice D: Altro materiale<br />
GNU Free Documentation License<br />
Indice</p>
<p>Introduzione</p>
<p>Di David Beazley</p>
<p>In qualità di educatore, ricercatore e autore di libri, sono lieto di<br />
assistere alla conclusione della stesura di questo testo. Python è un<br />
linguaggio di programmazione divertente e semplice da usare, la cui<br />
popolarità è andata via via crescendo nel corso degli ultimi anni.<br />
Python è stato sviluppato più di dieci anni fa da Guido van Rossum che<br />
ne ha derivato semplicità di sintassi e facilità d&#8217;uso in gran parte<br />
da ABC, un linguaggio dedicato all&#8217;insegnamento sviluppato negli anni<br />
&#8217;80. Oltre che per questo specifico contesto, Python è stato creato<br />
per risolvere problemi reali, dimostrando di possedere un&#8217;ampia<br />
varietà di caratteristiche tipiche di linguaggi di programmazione<br />
quali C++, Java, Modula-3 e Scheme. Questo giustifica una delle sue<br />
più rimarchevoli caratteristiche: l&#8217;ampio consenso nell&#8217;ambito degli<br />
sviluppatori professionisti di software, in ambiente scientifico e di<br />
ricerca, tra i creativi e gli educatori.<span id="more-5"></span></p>
<p>Nonostante l&#8217;interesse riscosso da Python in ambienti così disparati,<br />
potresti ancora chiederti &#8220;Perché Python?&#8221; o &#8220;Perché insegnare la<br />
programmazione con Python?&#8221;. Rispondere a queste domande non è cosa<br />
semplice, specialmente quando l&#8217;interesse generale è rivolto ad<br />
alternative più masochistiche quali C++ e Java. Penso comunque che la<br />
risposta più diretta sia che la programmazione in Python è semplice,<br />
divertente e più produttiva.</p>
<p>Quando tengo corsi di informatica, il mio intento è quello di spiegare<br />
concetti importanti interessando ed intrattenendo nel contempo gli<br />
studenti. Sfortunatamente nei corsi introduttivi c&#8217;è la tendenza a<br />
focalizzare troppo l&#8217;attenzione sull&#8217;astrazione matematica e nel caso<br />
degli studenti a sentirsi frustrati a causa di fastidiosi problemi<br />
legati a dettagli di basso livello della sintassi, della compilazione<br />
e dall&#8217;imposizione di regole poco intuitive. Sebbene questa astrazione<br />
e questo formalismo siano importanti per il progettista di software<br />
professionale e per gli studenti che hanno intenzione di proseguire i<br />
loro studi di informatica, questo approccio in un corso introduttivo<br />
porta solitamente a rendere l&#8217;informatica noiosa. Quando tengo un<br />
corso non voglio avere davanti una classe di studenti annoiati:<br />
preferirei piuttosto vederli impegnati a risolvere problemi<br />
interessanti esplorando idee diverse, approcci non convenzionali,<br />
infrangendo le regole e imparando dai loro stessi errori.</p>
<p>Inoltre non voglio sprecare mezzo semestre a risolvere oscuri problemi<br />
di sintassi, cercando di capire messaggi del compilatore generalmente<br />
incomprensibili o di far fronte al centinaio di modi in cui un<br />
programma può generare un &#8220;general protection fault&#8221;.</p>
<p>Una delle ragioni per cui mi piace Python è che esso permette un<br />
ottimo equilibrio tra l&#8217;aspetto pratico e quello concettuale. Dato che<br />
Python è interpretato, gli studenti possono fare qualcosa quasi subito<br />
senza perdersi in problemi di compilazione e link. Inoltre Python è<br />
fornito di un&#8217;ampia libreria di moduli che possono essere usati in<br />
ogni sorta di contesto, dalla programmazione web alla grafica. Questo<br />
aspetto pratico è un ottimo sistema per impegnare gli studenti e<br />
permette loro di portare a termine progetti non banali. Python può<br />
anche servire come eccellente punto di partenza per introdurre<br />
importanti concetti di informatica: dato che supporta procedure e<br />
classi, possono essere gradualmente introdotti argomenti quali<br />
l&#8217;astrazione procedurale, le strutture di dati e la programmazione ad<br />
oggetti, tutti solitamente relegati a corsi avanzati di Java o C++.<br />
Python prende a prestito un certo numero di caratteristiche da<br />
linguaggi di programmazione funzionali e può essere quindi usato per<br />
introdurre concetti che sarebbero normalmente trattati in dettaglio in<br />
corsi di Scheme o di Lisp.</p>
<p>Leggendo la prefazione di Jeffrey sono rimasto colpito da un suo<br />
commento: Python gli ha permesso di ottenere &#8220;un livello generale di<br />
successo più elevato ed un minore livello di frustrazione&#8221;, e gli è<br />
stato possibile muoversi &#8220;con maggiore velocità e con risultati<br />
migliori&#8221;. Questi commenti si riferiscono al suo corso introduttivo:<br />
io uso Python per queste stesse ragioni in corsi di informatica<br />
avanzata all&#8217;Università di Chicago. In questi corsi sono costantemente<br />
messo di fronte alla difficoltà di dover coprire molti argomenti<br />
complessi in un periodo di appena nove settimane. Sebbene sia<br />
certamente possibile per me infliggere un bel po&#8217; di sofferenza usando<br />
un linguaggio come il C++, ho spesso trovato che questo approccio è<br />
controproducente, specialmente nel caso di corsi riguardanti la<br />
semplice programmazione. Ritengo che usare Python mi permetta di<br />
focalizzare meglio l&#8217;attenzione sull&#8217;argomento reale della lezione,<br />
consentendo nel contempo agli studenti di completare progetti<br />
concreti.</p>
<p>Sebbene Python sia un linguaggio ancora giovane ed in continua<br />
evoluzione, credo che esso abbia un futuro nel campo<br />
dell&#8217;insegnamento. Questo libro è un passo importante in questa<br />
direzione.</p>
<p>David Beazley, autore di Python Essential Reference<br />
Università di Chicago</p>
<p>Prefazione</p>
<p>Di Jeff Elkner</p>
<p>Questo libro deve la sua esistenza alla collaborazione resa possibile<br />
da Internet e dal movimento free software. I suoi tre autori, un<br />
professore universitario, un docente di scuola superiore ed un<br />
programmatore professionista, non si sono ancora incontrati di<br />
persona, ma ciononostante sono riusciti a lavorare insieme a stretto<br />
contatto, aiutati da molte persone che hanno donato il proprio tempo e<br />
le loro energie per rendere questo libro migliore.</p>
<p>Noi pensiamo che questo libro rappresenti la testimonianza dei<br />
benefici e delle future possibilità di questo tipo di collaborazione,<br />
la cui struttura è stata creata da Richard Stallman e dalla Free<br />
Software Foundation.</p>
<p>Come e perché sono arrivato ad usare Python</p>
<p>Nel 1999 per la prima volta venne usato il linguaggio C++ per l&#8217;esame<br />
di informatica del College Board&#8217;s Advanced Placement (AP). Come in<br />
molte scuole secondarie della nazione, la decisione di cambiare<br />
linguaggio ebbe un impatto diretto sul curriculum del corso di<br />
informatica alla Yorktown High School di Arlington, Virginia, dove<br />
insegno. Fino a quel momento il Pascal era stato il linguaggio di<br />
insegnamento sia per il primo anno che per i corsi AP. Per continuare<br />
con la tradizione di insegnare ai nostri studenti uno stesso<br />
linguaggio per due anni, decidemmo di passare al C++ con gli studenti<br />
del primo anno nel &#8217;97/&#8217;98 così da metterli al passo con il cambio nel<br />
corso AP dell&#8217;anno successivo.</p>
<p>Due anni più tardi io ero convinto che il C++ fosse una scelta non<br />
adeguata per introdurre gli studenti all&#8217;informatica: mentre da un<br />
lato il C++ è certamente un linguaggio molto potente, esso si dimostra<br />
tuttavia essere estremamente difficile da insegnare ed imparare. Mi<br />
trovavo costantemente alle prese con la difficile sintassi del C++ e<br />
stavo inoltre inutilmente perdendo molti dei miei studenti. Convinto<br />
che ci dovesse essere un linguaggio migliore per il nostro primo anno<br />
iniziai a cercare un&#8217;alternativa al C++.</p>
<p>Avevo bisogno di un linguaggio che potesse girare tanto sulle macchine<br />
Linux del nostro laboratorio quanto sui sistemi Windows e Macintosh<br />
che la maggior parte degli studenti aveva a casa. Lo volevo<br />
open-source, così che potesse essere usato dagli studenti<br />
indipendentemente dalle loro possibilità economiche. Cercavo un<br />
linguaggio che fosse usato da programmatori professionisti e che<br />
avesse un&#8217;attiva comunità di sviluppatori. Doveva supportare tanto la<br />
programmazione procedurale che quella orientata agli oggetti. Cosa più<br />
importante, doveva essere facile da insegnare ed imparare. Dopo avere<br />
vagliato le possibili alternative con questi obiettivi in mente,<br />
Python sembrò il migliore candidato.</p>
<p>Chiesi ad uno tra gli studenti più dotati di Yorktown, Matt Ahrens, di<br />
provare Python. In due mesi egli non solo imparò il linguaggio ma<br />
scrisse un&#8217;applicazione, chiamata pyTicket, che dava la possibilità al<br />
nostro staff di stendere report concernenti problemi tecnici via Web.</p>
<p>Sapevo che Matt non avrebbe potuto realizzare un&#8217;applicazione di tale<br />
portata in un tempo così breve in C++, ed il suo successo, insieme al<br />
suo giudizio positivo sul linguaggio, suggerirono che Python era la<br />
soluzione che andavo cercando.</p>
<p>Trovare un libro di testo</p>
<p>Avendo deciso di usare Python nel corso introduttivo in entrambi i<br />
miei corsi di informatica l&#8217;anno successivo, la mancanza di un libro<br />
di testo si fece il problema più pressante.</p>
<p>Il materiale disponibile gratuitamente venne in mio aiuto. In<br />
precedenza, in quello stesso anno, Richard Stallman mi aveva fatto<br />
conoscere Allen Downey. Entrambi avevamo scritto a Richard esprimendo<br />
il nostro interesse nello sviluppare dei testi educativi gratuiti e<br />
Allen aveva già scritto un testo di informatica per il primo anno, How<br />
to Think Like a Computer Scientist. Quando lessi quel libro seppi<br />
immediatamente che volevo usarlo nelle mie lezioni. Era il testo di<br />
informatica più chiaro ed utile che avessi visto: il libro enfatizzava<br />
il processo di pensiero coinvolto nella programmazione piuttosto che<br />
le caratteristiche di un particolare linguaggio. Il solo fatto di<br />
leggerlo mi rese un insegnante migliore.</p>
<p>How to Think Like a Computer Scientist non solo era un libro<br />
eccellente, ma aveva la licenza pubblica GNU: questo significava che<br />
esso poteva essere usato liberamente e modificato per far fronte alle<br />
esigenze dei suoi utilizzatori. Deciso a usare Python, dovevo tradurre<br />
in questo linguaggio la versione originale basata su Java del testo di<br />
Allen. Mentre non sarei mai stato capace di scrivere un libro<br />
basandomi sulle mie sole forze, il fatto di avere il libro di Allen da<br />
usare come base mi rese possibile farlo, dimostrando nel contempo che<br />
il modello di sviluppo cooperativo usato così bene nel software poteva<br />
funzionare anche in ambito educativo.</p>
<p>Lavorare su questo libro negli ultimi due anni è stata una ricompensa<br />
sia per me che per i miei studenti, e proprio i miei studenti hanno<br />
giocato un ruolo importante nel processo. Dato che potevo modificare<br />
il testo non appena qualcuno trovava un errore o riteneva troppo<br />
difficile un passaggio, io li incoraggiai a cercare errori dando loro<br />
un punto aggiuntivo ogniqualvolta una loro osservazione comportava il<br />
cambiamento del testo. Questo aveva il doppio scopo di incoraggiarli a<br />
leggere il testo più attentamente e di passare il libro al vaglio dei<br />
suoi critici più severi: gli studenti impegnati ad imparare<br />
l&#8217;informatica.</p>
<p>Per la seconda parte del libro riguardante la programmazione ad<br />
oggetti, sapevo che sarebbe stato necessario trovare qualcuno con<br />
un&#8217;esperienza di programmazione reale più solida della mia. Il libro<br />
rimase incompiuto per buona parte dell&#8217;anno, finché la comunità open<br />
source ancora una volta fornì i mezzi per il suo completamento.</p>
<p>Ricevetti un&#8217;email da Chris Meyers che esprimeva interesse per il<br />
libro. Chris è un programmatore professionista che aveva iniziato a<br />
tenere un corso di programmazione con Python l&#8217;anno precedente presso<br />
il Lane Community College di Eugene, Oregon. La prospettiva di tenere<br />
il corso aveva portato il libro alla conoscenza di Chris, così che<br />
quest&#8217;ultimo cominciò ad aiutarci immediatamente. Prima della fine<br />
dell&#8217;anno aveva creato un progetto parallelo chiamato Python for Fun<br />
sul sito http://www.ibiblio.org/obp e stava lavorando con alcuni dei<br />
miei studenti più avanzati guidandoli dove io non avrei potuto<br />
portarli.</p>
<p>Introduzione alla programmazione con Python</p>
<p>Il processo di traduzione e uso di How to Think Like a Computer<br />
Scientist nei due anni scorsi ha confermato che Python è adatto<br />
all&#8217;insegnamento agli studenti del primo anno. Python semplifica<br />
enormemente gli esempi di programmazione e rende più semplici le idee<br />
importanti.</p>
<p>Il primo esempio illustra bene il punto. È il tradizionale programma<br />
&#8220;hello, world&#8221;, la cui versione C++ nel libro originale è la seguente:<br />
#include &lt;iostream.h&gt;</p>
<p>void main()<br />
{<br />
cout &lt;&lt; &#8220;Hello, World!&#8221; &lt;&lt; endl;<br />
}</p>
<p>Nella versione Python diventa:<br />
print &#8220;Hello, World!&#8221;</p>
<p>I vantaggi di Python saltano subito all&#8217;occhio anche in questo esempio<br />
banale. Il corso di informatica I a Yorktown non necessita di<br />
prerequisiti, così molti studenti vedendo questo esempio stanno in<br />
realtà guardando il loro primo programma. Qualcuno di loro è<br />
sicuramente un po&#8217; nervoso, avendo saputo che la programmazione è<br />
difficile da imparare. La versione in C++ mi ha sempre costretto a<br />
scegliere tra due opzioni ugualmente insoddisfacenti: o spiegare le<br />
istruzioni #include, void main(), { e }, rischiando di intimidire e<br />
mettere in confusione qualcuno degli studenti già dall&#8217;inizio, o dire<br />
loro &#8220;Non preoccupatevi di questa roba per adesso; ne parleremo più<br />
avanti&#8221; e rischiare di ottenere lo stesso risultato. Gli obiettivi a<br />
questo punto del corso sono quelli di introdurre gli studenti all&#8217;idea<br />
di istruzione di programma e di portarli a scrivere il loro primo<br />
programma, così da introdurli nell&#8217;ambiente della programmazione.<br />
Python ha esattamente ciò che è necessario per fare questo e niente di<br />
più.</p>
<p>Confrontare il testo esplicativo del programma in ognuna delle due<br />
versioni del libro illustra ulteriormente ciò che questo significa per<br />
lo studente alle prime armi: ci sono tredici paragrafi nella<br />
spiegazione di &#8220;Hello, world!&#8221; nella versione C++ e solo due in quella<br />
Python. Da notare che gli undici paragrafi aggiuntivi non trattano<br />
delle &#8220;grandi idee&#8221; della programmazione, ma riguardano i particolari<br />
connessi alla sintassi del C++. Ho visto questo accadere lungo tutto<br />
il corso del libro, così che interi paragrafi semplicemente sono<br />
scomparsi dalla versione Python del testo perché la sintassi del<br />
linguaggio, molto più chiara, li ha resi inutili.</p>
<p>L&#8217;uso di un linguaggio di altissimo livello come Python permette<br />
all&#8217;insegnante di posporre la trattazione di dettagli di basso livello<br />
sino al momento in cui gli studenti non sono in possesso delle basi<br />
che permettono loro di comprenderli appieno. Ciò dà la possibilità di<br />
procedere con ordine. Uno degli esempi migliori è il modo in cui<br />
Python tratta le variabili. In C++ una variabile è un nome che<br />
identifica un posto che contiene qualcosa: le variabili devono essere<br />
dichiarate anticipatamente perché la grandezza del posto cui si<br />
riferiscono deve essere predeterminata tanto che l&#8217;idea di una<br />
variabile è legata all&#8217;hardware della macchina. Il concetto potente e<br />
fondamentale di variabile è già sufficientemente difficile per<br />
studenti alle prime armi (tanto in informatica che in algebra): byte e<br />
indirizzi non aiutano certo a comprendere l&#8217;argomento. In Python una<br />
variabile è un nome che fa riferimento ad una cosa. Questo è un<br />
concetto decisamente più intuitivo per gli studenti e molto più vicino<br />
a ciò che essi hanno imparato in matematica. Ho dovuto affrontare<br />
difficoltà molto minori nell&#8217;insegnare le variabili quest&#8217;anno che in<br />
passato e ho dovuto trascorrere meno tempo aiutando gli studenti a<br />
destreggiarsi con esse.</p>
<p>Un altro esempio di come Python aiuti tanto nell&#8217;insegnamento quanto<br />
nell&#8217;apprendimento della programmazione è la sua sintassi per le<br />
funzioni. I miei studenti hanno sempre avuto difficoltà a capire le<br />
funzioni: il problema verte sulla differenza tra la definizione di una<br />
funzione e la sua chiamata e la relativa distinzione tra un parametro<br />
ed un argomento. Python viene in aiuto con una sintassi che non manca<br />
di eleganza. La definizione di una funzione inizia con def, così dico<br />
ai miei studenti: &#8220;Quando definite una funzione iniziate con def,<br />
seguito dal nome della funzione; quando volete chiamare la funzione<br />
basta inserire il suo nome.&#8221; I parametri vanno con le definizioni, gli<br />
argomenti con le chiamate. Non ci sono tipi di ritorno, tipi del<br />
parametro, o riferimenti, così posso insegnare le funzioni in minor<br />
tempo e con una migliore comprensione.</p>
<p>L&#8217;uso di Python ha migliorato l&#8217;efficacia del nostro corso di<br />
informatica. Ottengo dai miei studenti un livello generale di successo<br />
più elevato ed un minore livello di frustrazione, rispetto al biennio<br />
in cui ho insegnato il C++. Mi muovo con maggior velocità e con<br />
migliori risultati. Un maggior numero di studenti terminano il corso<br />
con la capacità di creare programmi significativi e con un&#8217;attitudine<br />
positiva verso l&#8217;esperienza della programmazione.</p>
<p>Costruire una comunità</p>
<p>Ho ricevuto email da tutto il mondo da gente che usa questo libro per<br />
imparare o insegnare la programmazione. Una comunità di utilizzatori<br />
ha iniziato ad emergere, e molte persone hanno contribuito al progetto<br />
spedendo materiale al sito http://www.thinkpython.com.</p>
<p>Con la pubblicazione del libro in forma stampata mi aspetto che la<br />
comunità di utilizzatori si espanda. La nascita di questa comunità e<br />
la possibilità che essa suggerisce riguardo collaborazioni tra<br />
insegnanti sono state le cose che più mi hanno coinvolto in questo<br />
progetto. Lavorando insieme possiamo migliorare la qualità del<br />
materiale disponibile e risparmiare tempo prezioso. Ti invito a unirti<br />
a noi e attendo di ricevere tue notizie: scrivi agli autori<br />
all&#8217;indirizzo feedback@thinkpython.com.</p>
<p>Jeffrey Elkner<br />
Yorktown High School<br />
Arlington, Virginia</p>
<p>Lista dei collaboratori</p>
<p>Questo libro è nato grazie ad una collaborazione che non sarebbe stata<br />
possibile senza la GNU Free Documentation License. Vorremmo<br />
ringraziare la Free Software Foundation per aver sviluppato questa<br />
licenza e per avercela resa disponibile.</p>
<p>Vorremmo anche ringraziare il centinaio di lettori che ci hanno<br />
spedito suggerimenti e correzioni nel corso degli ultimi due anni.<br />
Nello spirito del software libero abbiamo deciso di esprimere la<br />
nostra gratitudine aggiungendo la lista dei collaboratori.<br />
Sfortunatamente la lista non è completa, ma stiamo facendo del nostro<br />
meglio per tenerla aggiornata.</p>
<p>Se avrai modo di scorrere lungo la lista riconoscerai che ognuna di<br />
queste persone ha risparmiato a te e agli altri lettori la confusione<br />
derivante da errori tecnici o da spiegazioni non troppo chiare.</p>
<p>Anche se sembra impossibile dopo così tante correzioni, ci possono<br />
essere ancora degli errori in questo libro. Se per caso dovessi<br />
trovarne uno, speriamo tu possa spendere un minuto per farcelo sapere.<br />
L&#8217;indirizzo email al quale comunicarcelo è feedback@thinkpython.com.<br />
Se faremo qualche cambiamento a seguito del tuo suggerimento anche tu<br />
sarai inserito nella lista dei collaboratori, sempre che tu non chieda<br />
altrimenti. Grazie!</p>
<p>* Lloyd Hugh Allen, per una correzione nella sezione 8.4.<br />
* Yvon Boulianne, per una correzione di un errore di semantica al<br />
capitolo 5.<br />
* Fred Bremmer, per una correzione alla sezione 2.1.<br />
* Jonah Cohen, per lo script Perl di conversione del codice LaTeX di<br />
questo libro in HTML.<br />
* Michael Conlon, per una correzione grammaticale nel capitolo 2,<br />
per il miglioramento dello stile nel capitolo 1 e per aver<br />
iniziato la discussione sugli aspetti tecnici degli interpreti.<br />
* Benoit Girard, per la correzione di un errore nella sezione 5.6.<br />
* Courtney Gleason e Katherine Smith, per aver scritto horsebet.py,<br />
usato in una versione precedente del libro come caso di studio. Il<br />
loro programma può essere trovato sul sito.<br />
* Lee Harr, per aver sottoposto una serie di correzioni che sarebbe<br />
troppo lungo esporre qui. Dovrebbe essere citato come uno dei<br />
maggiori revisori del libro.<br />
* James Kaylin è uno studente che ha usato il libro ed ha sottoposto<br />
numerose correzioni.<br />
* David Kershaw, per aver reso funzionante del codice nella sezione<br />
3.10.<br />
* Eddie Lam, per aver spedito numerose correzioni ai primi tre<br />
capitoli, per aver sistemato il makefile così da creare un indice<br />
alla prima compilazione e per averci aiutato nella gestione delle<br />
versioni.<br />
* Man-Yong Lee, per aver spedito una correzione al codice di esempio<br />
nella sezione 2.4.<br />
* David Mayo, per una correzione grammaticale al capitolo 1.<br />
* Chris McAloon, per le correzioni nelle sezioni 3.9 e 3.10.<br />
* Matthew J. Moelter, per essere stato uno dei collaboratori al<br />
progetto, e per aver contribuito con numerose correzioni e<br />
commenti.<br />
* Simon Dicon Montford, per aver fatto notare una mancata<br />
definizione di funzione e numerosi errori di battitura nel<br />
capitolo 3 e per aver aver trovato gli errori nella funzione<br />
Incrementa nel capitolo 13.<br />
* John Ouzts, per aver corretto la definizione di &#8220;valore di<br />
ritorno&#8221; nel capitolo 3.<br />
* Kevin Parks, per aver contribuito con validi commenti e<br />
suggerimenti su come migliorare la distribuzione del libro.<br />
* David Pool, per la correzione di un errore di battitura al<br />
capitolo 1 e per averci spedito parole di incoraggiamento.<br />
* Michael Schmitt, per una correzione nel capitolo sui file e le<br />
eccezioni.<br />
* Robin Shaw, per aver trovato un errore nella sezione 13.1 dove una<br />
funzione veniva usata senza essere stata preventivamente definita.<br />
* Paul Sleigh, per aver trovato un errore nel capitolo 7, ed un<br />
altro nello script Perl per la generazione dell&#8217;HTML.<br />
* Craig T. Snydal, che sta usando il testo in un corso alla Drew<br />
University. Ha contribuito con numerosi suggerimenti e correzioni.<br />
* Ian Thomas ed i suoi studenti che hanno usato il testo in un corso<br />
di programmazione. Sono stati i primi a controllare i capitoli<br />
nella seconda parte del libro, fornendo numerose correzioni ed<br />
utili suggerimenti.<br />
* Keith Verheyden, per una correzione nel capitolo 3.<br />
* Peter Winstanley, per una correzione nel capitolo 3.<br />
* Chris Wrobel, per le correzioni al codice nel capitolo sui file e<br />
le eccezioni.<br />
* Moshe Zadka, per il suo prezioso contributo al progetto. Oltre ad<br />
aver scritto la prima stesura del capitolo sui dizionari ha<br />
fornito una continua assistenza nelle fasi iniziali del libro.<br />
* Christoph Zwerschke, per aver spedito numerose correzioni e<br />
suggerimenti, e per aver spiegato la differenza tra gleich e<br />
selbe.<br />
* James Mayer, per la lista di correzioni di errori tipografici e di<br />
spelling.<br />
* Hayden McAfee, per aver notato una potenziale incoerenza tra due<br />
esempi.<br />
* Angel Arnal, fa parte di un gruppo internazionale di traduttori<br />
che sta lavorando sulla versione in lingua spagnola del libro. Ha<br />
anche riferito di una serie di errori nella versione inglese.<br />
* Tauhidul Hoque e Lex Berezhny hanno creato le illustrazioni del<br />
capitolo 1 e migliorato molte delle altre illustrazioni.<br />
* Dr. Michele Alzetta, per aver corretto un errore nel capitolo 8 e<br />
aver inviato una serie di utili commenti e suggerimenti<br />
concernenti Fibonacci e Old Maid.<br />
* Andy Mitchell, per aver corretto un errore tipografico nel<br />
capitolo 1 ed un esempio non funzionante nel capitolo 2.<br />
* Kalin Harvey, per aver suggerito un chiarimento nel capitolo 7 e<br />
aver corretto alcuni errori di battitura.<br />
* Christopher P. Smith, per la correzione di numerosi errori di<br />
battitura e per l&#8217;aiuto nell&#8217;aggiornamento del libro alla versione<br />
2.2 di Python.<br />
* David Hutchins, per la correzione di un errore di battitura nella<br />
Prefazione.<br />
* Gregor Lingl sta insegnando Python in una scuola superiore di<br />
Vienna e lavorando alla traduzione in tedesco. Ha corretto un paio<br />
di errori nel capitolo 5.<br />
* Julie Peters, per la correzione di un errore di battitura nella<br />
prefazione.</p>
<p>Note sulla traduzione</p>
<p>Di Alessandro Pocaterra</p>
<p>Chi si trova a tradurre un testo da una lingua all&#8217;altra deve<br />
necessariamente fare delle scelte, dato che nel caso delle lingue<br />
naturali non è quasi mai possibile ottenere una perfetta<br />
corrispondenza tra testo originale e testo tradotto. Questo vale più<br />
che mai nel caso della traduzione di testi tecnici, soprattutto in<br />
campi così &#8220;giovani&#8221; come l&#8217;informatica: questo settore è nato<br />
pescando a destra e a manca termini dalla lingua inglese, e in buona<br />
parte questi sono traducibili in italiano solo in modo ridicolo (si<br />
veda il &#8220;baco&#8221; malamente ottenuto dall&#8217;originale &#8220;bug&#8221;), inadeguato o,<br />
quel che è peggio, inesatto. Partendo dal fatto che io sono un<br />
programmatore senior, il mio approccio è decisamente diverso da quello<br />
dello studente &#8220;moderno&#8221; che si appresta allo studio dell&#8217;informatica:<br />
solo dieci anni fa era praticamente impossibile trovare termini<br />
tecnici in informatica che non fossero rigorosamente in inglese e<br />
pertanto ho deciso di conservarli dove ho ritenuto fosse necessario<br />
(come nel caso di &#8220;bug&#8221;, &#8220;debug&#8221;, &#8220;parsing&#8221; per fare qualche esempio).<br />
In questa traduzione ho cercato di rispettare il più possibile il<br />
testo originale mantenendone il tono discorsivo e le frasi brevi<br />
tipiche della lingua inglese. Ho avuto il permesso degli autori a<br />
togliere (poche) frasi che avrebbero perso il loro significato perché<br />
basate su giochi di parole intraducibili e a rimaneggiare in qualche<br />
punto l&#8217;organizzazione del testo.</p>
<p>Una nota che invece riguarda la notazione numerica. Chiunque abbia mai<br />
preso in mano una calcolatrice si sarà accorto che la virgola dei<br />
decimali tanto cara alla nostra maestra delle elementari si è<br />
trasformata in un punto. Naturalmente questo cambio non è casuale: nei<br />
paesi anglosassoni l&#8217;uso di virgola e punto nei numeri è esattamente<br />
l&#8217;opposto di quello cui siamo abituati: se per noi ha senso scrivere<br />
1.234.567,89 (magari con il punto delle migliaia in alto), in inglese<br />
questo numero viene scritto come 1,234,567.89. In informatica i<br />
separatori delle migliaia sono di solito trascurati e per la maggior<br />
parte dei linguaggi di programmazione considerati illegali: per il<br />
nostro computer lo stesso numero sarà quindi 1234567.89. Un po&#8217; di<br />
pratica e ci si fa l&#8217;abitudine. In relazione al codice presente nel<br />
testo, per non uscire dai margini del documento, sono state spezzate<br />
le righe che davano problemi con l&#8217;inserimento del carattere \ come<br />
fine riga. Siete quindi fin d&#8217;ora avvertiti che, ove trovaste quel<br />
carattere, in realtà la riga andrebbe scritta comprendendo anche<br />
quella successiva. In altri casi piuttosto evidenti è stato omesso il<br />
carattere \.</p>
<p>Ringraziamenti</p>
<p>Naturalmente ringrazio i tre autori del testo originale Allen Downey,<br />
Jeffrey Elkner e Chris Meyers, senza i quali questo libro non avrebbe<br />
mai visto la luce. Devo ringraziare per l&#8217;aiuto mia moglie Sara che si<br />
è volonterosamente prestata alla rilettura e correzione del libro.<br />
Ringrazio in modo particolare Ferdinando Ferranti che si è prodigato<br />
nella revisione, ma soprattutto nel rivedere il codice LaTeX in ogni<br />
sua parte, aiutandomi a suddividere il documento così come<br />
nell&#8217;originale, correggendo gli errori di compilazione che il codice<br />
restituiva e realizzando così anche una versione HTML funzionante.<br />
Oltre a questo ha anche modificato l&#8217;impaginazione ed il Makefile<br />
ottenendo così una versione del documento la cui stampa è più<br />
funzionale rispetto all&#8217;originale, pensato per formati di carta<br />
differenti. Ringrazio anche Nicholas Wieland, &#8220;Pang&#8221; e Nicola La Rosa<br />
per il loro aiuto insostituibile in fase di revisione. Ringrazio tutti<br />
quelli, Dario Cavedon e Giovanni Panozzo in testa, che mi hanno fatto<br />
scoprire il mondo Linux, il Free Software e la Free Documentation. Un<br />
ringraziamento particolare a tutti quelli che si sono rimboccati le<br />
maniche ed hanno dato vita a quell&#8217;incredibile strumento che è LaTeX .</p>
<p>La traduzione di questo libro è stata un passatempo ed un<br />
divertimento. Dato che sicuramente qualcosa non gira ancora come<br />
dovrebbe, vi chiedo di mandarmi i vostri commenti a riguardo<br />
all&#8217;indirizzo a.pocaterra@libero.it. In caso di refusi o imprecisioni<br />
ricordate di citare sempre la pagina e la versione di questo documento<br />
(versione 1.0b).</p>
<p>Nelle versioni successive si cercherà per quanto possibile di tenere<br />
il passo con la bibliografia: qualsiasi indicazione al riguardo sarà<br />
sempre bene accetta.</p>
<p>Buona fortuna!<br />
Alessandro Pocaterra</p>
<p>Capitolo 1</p>
<p>Imparare a programmare</p>
<p>L&#8217;obiettivo di questo libro è insegnarti a pensare da informatico.<br />
Questo modo di pensare combina alcune delle migliori caratteristiche<br />
della matematica, dell&#8217;ingegneria e delle scienze naturali. Come i<br />
matematici, gli informatici usano linguaggi formali per denotare idee<br />
(nella fattispecie elaborazioni). Come gli ingegneri progettano cose,<br />
assemblano componenti in sistemi e cercano compromessi tra le varie<br />
alternative. Come gli scienziati osservano il comportamento di sistemi<br />
complessi, formulano ipotesi e verificano previsioni.</p>
<p>La più importante capacità di un informatico è quella di risolvere<br />
problemi. Risolvere problemi significa avere l&#8217;abilità di<br />
schematizzarli, pensare creativamente alle possibili soluzioni ed<br />
esprimerle in modo chiaro ed accurato. Da ciò emerge che il processo<br />
di imparare a programmare è un&#8217;eccellente opportunità di mettere in<br />
pratica l&#8217;abilità di risolvere problemi.</p>
<p>Da una parte ti sarà insegnato a programmare, già di per sé un&#8217;utile<br />
capacità. Dall&#8217;altra userai la programmazione come un mezzo rivolto ad<br />
un fine. Mentre procederemo quel fine ti diverrà più chiaro.</p>
<p>1.1 Il linguaggio di programmazione Python</p>
<p>Il linguaggio di programmazione che imparerai è il Python. Python è un<br />
esempio di linguaggio di alto livello; altri linguaggi di alto livello<br />
di cui puoi aver sentito parlare sono il C, il C++, il Perl ed il<br />
Java.</p>
<p>Come puoi immaginare sentendo la definizione &#8220;linguaggio di alto<br />
livello&#8221; esistono anche linguaggi di basso livello, talvolta chiamati<br />
&#8220;linguaggi macchina&#8221; o &#8220;linguaggi assembly&#8221;. In modo non del tutto<br />
corretto si può affermare che i computer possono eseguire soltanto<br />
programmi scritti in linguaggi di basso livello: i programmi scritti<br />
in un linguaggio di alto livello devono essere elaborati prima di<br />
poter essere eseguiti. Questo processo di elaborazione impiega del<br />
tempo e rappresenta un piccolo svantaggio dei linguaggi di alto<br />
livello.</p>
<p>I vantaggi sono d&#8217;altra parte enormi. In primo luogo è molto più<br />
facile programmare in un linguaggio ad alto livello: questi tipi di<br />
programmi sono più veloci da scrivere, più corti e facilmente<br />
leggibili, ed è più probabile che siano corretti. In secondo luogo i<br />
linguaggi di alto livello sono portabili: portabilità significa che<br />
essi possono essere eseguiti su tipi di computer diversi con poche o<br />
addirittura nessuna modifica. I programmi scritti in linguaggi di<br />
basso livello possono essere eseguiti solo su un tipo di computer e<br />
devono essere riscritti per essere trasportati su un altro sistema.</p>
<p>Questi vantaggi sono così evidenti che quasi tutti i programmi sono<br />
scritti in linguaggi di alto livello, lasciando spazio ai linguaggi di<br />
basso livello solo in poche applicazioni specializzate.</p>
<p>I programmi di alto livello vengono trasformati in programmi di basso<br />
livello eseguibili dal computer tramite due tipi di elaborazione:<br />
l&#8217;interpretazione e la compilazione. Un interprete legge il programma<br />
di alto livello e lo esegue, trasformando ogni riga di istruzioni in<br />
un&#8217;azione. L&#8217;interprete elabora il programma un po&#8217; alla volta,<br />
alternando la lettura delle istruzioni all&#8217;esecuzione dei comandi che<br />
le istruzioni descrivono:</p>
<p>[i_interpret.png]</p>
<p>Un compilatore legge il programma di alto livello e lo traduce<br />
completamente in basso livello, prima che il programma possa essere<br />
eseguito. In questo caso il programma di alto livello viene chiamato<br />
codice sorgente, ed il programma tradotto codice oggetto o eseguibile.<br />
Dopo che un programma è stato compilato può essere eseguito<br />
ripetutamente senza che si rendano necessarie ulteriori compilazioni<br />
finché non ne viene modificato il codice.</p>
<p>[i_compile.png]</p>
<p>Python è considerato un linguaggio interpretato perché i programmi<br />
Python sono eseguiti da un interprete. Ci sono due modi di usare<br />
l&#8217;interprete: a linea di comando o in modo script. In modo &#8220;linea di<br />
comando&#8221; si scrivono i programmi Python una riga alla volta: dopo<br />
avere scritto una riga di codice alla pressione di Invio (o Enter, a<br />
seconda della tastiera) l&#8217;interprete la analizza subito ed elabora<br />
immediatamente il risultato, eventualmente stampandolo a video:</p>
<p>$ python<br />
Python 1.5.2 (#1, Feb 1 2000, 16:32:16)<br />
Copyright 1991-1995 Stichting Mathematish Centrum, Amsterdam<br />
&gt;&gt;&gt; print 1 + 1<br />
2</p>
<p>La prima linea di questo esempio è il comando che fa partire<br />
l&#8217;interprete Python in ambiente Linux e può cambiare leggermente a<br />
seconda del sistema operativo utilizzato. Le due righe successive sono<br />
semplici informazioni di copyright del programma.</p>
<p>La terza riga inizia con &gt;&gt;&gt;: questa è l&#8217;indicazione (chiamata<br />
&#8220;prompt&#8221;) che l&#8217;interprete usa per indicare la sua disponibilità ad<br />
accettare comandi. Noi</p>
<p>abbiamo inserito print 1 + 1 e l&#8217;interprete ha risposto con 2.</p>
<p>In alternativa alla riga di comando si può scrivere un programma in un<br />
file (detto script) ed usare l&#8217;interprete per eseguire il contenuto<br />
del file. Nell&#8217;esempio seguente abbiamo usato un editor di testi per<br />
creare un file chiamato pippo.py:</p>
<p>print 1 + 1</p>
<p>Per convenzione, i file contenenti programmi Python hanno nomi che<br />
terminano con .py.</p>
<p>Per eseguire il programma dobbiamo dire all&#8217;interprete il nome dello<br />
script:</p>
<p>$ python pippo.py<br />
2</p>
<p>In altri ambienti di sviluppo i dettagli dell&#8217;esecuzione dei programmi<br />
possono essere diversi.</p>
<p>La gran parte degli esempi di questo libro sono eseguiti da linea di<br />
comando: lavorare da linea di comando è conveniente per lo sviluppo e<br />
per il test del programma perché si possono inserire ed eseguire<br />
immediatamente singole righe di codice. Quando si ha un programma<br />
funzionante lo si dovrebbe salvare in uno script per poterlo eseguire<br />
o modificare in futuro senza doverlo riscrivere da capo ogni volta.<br />
Tutto ciò che viene scritto in modo &#8220;linea di comando&#8221; è<br />
irrimediabilmente perso nel momento in cui usciamo dall&#8217;ambiente<br />
Python.</p>
<p>1.2 Cos&#8217;è un programma?</p>
<p>Un programma è una sequenza di istruzioni che specificano come<br />
effettuare una elaborazione. L&#8217;elaborazione può essere sia di tipo<br />
matematico (per esempio la soluzione di un sistema di equazioni o il<br />
calcolo delle radici di un polinomio) che simbolico (per esempio la<br />
ricerca e sostituzione di un testo in un documento).</p>
<p>I dettagli sono diversi per ciascun linguaggio di programmazione, ma<br />
un piccolo gruppo di istruzioni è praticamente comune a tutti:<br />
* input: ricezione di dati da tastiera, da file o da altro<br />
dispositivo.<br />
* output: scrittura di dati su video, su file o trasmissione ad<br />
altro dispositivo.<br />
* matematiche: esecuzione di semplici operazioni matematiche, quali<br />
l&#8217;addizione e la sottrazione.<br />
* condizionali: controllo di alcune condizioni ed esecuzione della<br />
sequenza di istruzioni appropriata.<br />
* ripetizione: ripetizione di un&#8217;azione, di solito con qualche<br />
variazione.</p>
<p>Che ci si creda o meno, questo è più o meno tutto quello che c&#8217;è. Ogni<br />
programma che hai usato per quanto complesso possa sembrare (anche il<br />
tuo videogioco preferito) è costituito da istruzioni che assomigliano<br />
a queste. Possiamo affermare che la programmazione altro non è che la<br />
suddivisione di un compito grande e complesso in una serie di<br />
sotto-compiti via via più piccoli, finché questi sono sufficientemente<br />
semplici da essere eseguiti da una di queste istruzioni fondamentali.</p>
<p>Questo concetto può sembrare un po&#8217; vago, ma lo riprenderemo quando<br />
parleremo di algoritmi.</p>
<p>1.3 Cos&#8217;è il debug?</p>
<p>La programmazione è un processo complesso e dato che esso è fatto da<br />
esseri umani spesso comporta errori. Per ragioni bizzarre gli errori<br />
di programmazione sono chiamati bug ed il processo della loro ricerca<br />
e correzione è chiamato debug.</p>
<p>Sono tre i tipi di errore nei quali si incorre durante la<br />
programmazione: gli errori di sintassi, gli errori in esecuzione e gli<br />
errori di semantica. È utile distinguerli per poterli individuare più<br />
velocemente.</p>
<p>Errori di sintassi</p>
<p>Python può eseguire un programma solo se il programma è<br />
sintatticamente corretto, altrimenti l&#8217;elaborazione fallisce e<br />
l&#8217;interprete ritorna un messaggio d&#8217;errore. La sintassi si riferisce<br />
alla struttura di un programma e alle regole concernenti la sua<br />
struttura. In italiano, per fare un esempio, una frase deve iniziare<br />
con una lettera maiuscola e terminare con un punto. questa frase<br />
contiene un errore di sintassi. E anche questa</p>
<p>Per la maggior parte dei lettori qualche errore di sintassi non è un<br />
problema significativo, tanto che possiamo leggere le poesie di<br />
E.E.Cummings (prive di punteggiatura) senza &#8220;messaggi d&#8217;errore&#8221;.<br />
Python non è così permissivo: se c&#8217;è un singolo errore di sintassi da<br />
qualche parte nel programma Python stamperà un messaggio d&#8217;errore e ne<br />
interromperà l&#8217;esecuzione, rendendo impossibile proseguire. Durante le<br />
prime settimane della tua carriera di programmatore probabilmente<br />
passerai molto tempo a ricercare errori di sintassi. Via via che<br />
acquisirai esperienza questi si faranno meno numerosi e sarà sempre<br />
più facile rintracciarli.</p>
<p>Errori in esecuzione</p>
<p>Il secondo tipo di errore è l&#8217;errore in esecuzione (o &#8220;runtime&#8221;), così<br />
chiamato perché l&#8217;errore non appare finché il programma non è<br />
eseguito. Questi errori sono anche chiamati eccezioni perché indicano<br />
che è accaduto qualcosa di eccezionale nel corso dell&#8217;esecuzione (per<br />
esempio si è cercato di dividere un numero per zero).</p>
<p>Gli errori in esecuzione sono rari nei semplici programmi che vedrai<br />
nei primissimi capitoli, così potrebbe passare un po&#8217; di tempo prima<br />
che tu ne incontri uno.</p>
<p>Errori di semantica</p>
<p>Il terzo tipo di errore è l&#8217;errore di semantica. Se c&#8217;è un errore di<br />
semantica il programma verrà eseguito senza problemi nel senso che il<br />
computer non genererà messaggi d&#8217;errore durante l&#8217;esecuzione, ma il<br />
risultato non sarà ciò che ci si aspettava. Sarà qualcosa di diverso,<br />
e questo qualcosa è esattamente ciò che è stato detto di fare al<br />
computer.</p>
<p>Il problema sta nel fatto che il programma che è stato scritto non è<br />
quello che si desiderava scrivere: il significato del programma (la<br />
sua semantica) è sbagliato. L&#8217;identificazione degli errori di<br />
semantica è un processo complesso perché richiede di lavorare in modo<br />
inconsueto, guardando i risultati dell&#8217;esecuzione e cercando di capire<br />
cosa il programma ha fatto di sbagliato per ottenerli.</p>
<p>Debug sperimentale</p>
<p>Una delle più importanti abilità che acquisirai è la capacità di<br />
effettuare il debug (o &#8220;rimozione degli errori&#8221;). Sebbene questo possa<br />
essere un processo frustrante è anche una delle parti più<br />
intellettualmente vivaci, stimolanti ed interessanti della<br />
programmazione.</p>
<p>In un certo senso il debug può essere paragonato al lavoro<br />
investigativo. Sei messo di fronte agli indizi e devi ricostruire i<br />
processi e gli eventi che hanno portato ai risultati che hai ottenuto.</p>
<p>Il debug è una scienza sperimentale: dopo che hai avuto un&#8217;idea di ciò<br />
che può essere andato storto, modifichi il programma e lo provi<br />
ancora. Se la tua ipotesi era corretta allora puoi predire il<br />
risultato della modifica e puoi avvicinarti di un ulteriore passo<br />
all&#8217;avere un programma funzionante. Se la tua ipotesi era sbagliata<br />
devi ricercarne un&#8217;altra. Come disse Sherlock Holmes &#8220;Quando hai<br />
eliminato l&#8217;impossibile ciò che rimane, per quanto improbabile, deve<br />
essere la verità&#8221; (A.Conan Doyle, Il segno dei quattro)</p>
<p>Per qualcuno la programmazione e il debug sono la stessa cosa,<br />
intendendo con questo che la programmazione è un processo di rimozione<br />
di errori finché il programma fa ciò che ci si aspetta. L&#8217;idea è che<br />
si dovrebbe partire da un programma che fa qualcosa e facendo piccole<br />
modifiche ed eliminando gli errori man mano che si procede si dovrebbe<br />
avere in ogni momento un programma funzionante sempre più completo.</p>
<p>Linux, per fare un esempio, è un sistema operativo che contiene<br />
migliaia di righe di codice, ma esso è nato come un semplice programma<br />
che Linus Torvalds usò per esplorare il chip 80386 Intel. Secondo<br />
Larry Greenfields, &#8220;uno dei progetti iniziali di Linus era un<br />
programma che doveva cambiare una riga di AAAA in BBBB e viceversa.<br />
Questo in seguito diventò Linux.&#8221; (The Linux Users&#8217; Guide Beta Version<br />
1)</p>
<p>I capitoli successivi ti forniranno ulteriori suggerimenti sia per<br />
quanto riguarda il debug che per altre pratiche di programmazione.</p>
<p>1.4 Linguaggi formali e naturali</p>
<p>I linguaggi naturali sono le lingue parlate, tipo l&#8217;inglese,<br />
l&#8217;italiano, lo spagnolo. Non sono stati &#8220;progettati&#8221; da qualcuno e<br />
anche se è stato imposto un certo ordine nel loro sviluppo si sono<br />
evoluti naturalmente.</p>
<p>I linguaggi formali sono linguaggi progettati per specifiche<br />
applicazioni.</p>
<p>Per fare qualche esempio, la notazione matematica è un linguaggio<br />
formale particolarmente indicato ad esprimere relazioni tra numeri e<br />
simboli; i chimici usano un linguaggio formale per rappresentare la<br />
struttura delle molecole; cosa più importante dal nostro punto di<br />
vista, i linguaggi di programmazione sono linguaggi formali che sono<br />
stati progettati per esprimere elaborazioni.</p>
<p>I linguaggi formali tendono ad essere piuttosto rigidi per quanto<br />
riguarda la sintassi: 3+3=6 è una dichiarazione matematica<br />
sintatticamente corretta, mentre 3=div6$ non lo è. H[2]O è un simbolo<br />
chimico sintatticamente corretto contrariamente a [2]Zz.</p>
<p>Le regole sintattiche si possono dividere in due categorie: la prima<br />
riguarda i token, la seconda la struttura. I token sono gli elementi<br />
di base del linguaggio (quali possono essere le parole in letteratura,<br />
i numeri in matematica e gli elementi chimici in chimica). Uno dei<br />
problemi con 3=div6$ è che $ non è un token valido in matematica;<br />
[2]Zz non è valido perché nessun elemento chimico è identificato dal<br />
simbolo Zz.</p>
<p>Il secondo tipo di regola riguarda la struttura della dichiarazione,<br />
cioè il modo in cui i token sono disposti. La dichiarazione 3=div6$ è<br />
strutturalmente non valida perché un segno div non può essere posto<br />
immediatamente dopo un segno =. Allo stesso modo l&#8217;indice nelle<br />
formule chimiche deve essere indicato dopo il simbolo dell&#8217;elementi<br />
chimico, non prima, e quindi l&#8217;espressione [2]Zz non è valida.</p>
<p>Come esercizio crea quella che può sembrare una frase in italiano<br />
con dei token non riconoscibili. Poi scrivi un&#8217;altra frase con<br />
tutti i token validi ma con una struttura non valida.</p>
<p>Quando leggi una frase in italiano o una dichiarazione in un<br />
linguaggio formale devi capire quale sia la struttura della<br />
dichiarazione. Questo processo (chiamato parsing) in un linguaggio<br />
naturale viene realizzato in modo inconscio e spesso non ci si rende<br />
conto della sua intrinseca complessità.</p>
<p>Per esempio, quando senti la frase &#8220;La scarpa è caduta&#8221;, capisci che<br />
&#8220;la scarpa&#8221; è il soggetto e che &#8220;è caduta&#8221; è il verbo. Quando hai<br />
analizzato la frase puoi capire cosa essa significa (cioè la semantica<br />
della frase). Partendo dal presupposto che tu sappia cosa sia una<br />
&#8220;scarpa&#8221; e cosa significhi &#8220;cadere&#8221; riesci a comprendere il<br />
significato generale della frase.</p>
<p>Anche se i linguaggi formali e quelli naturali condividono molte<br />
caratteristiche (token, struttura, sintassi e semantica) ci sono<br />
tuttavia molte differenze:</p>
<p>Ambiguità<br />
i linguaggi naturali ne sono pieni ed il significato viene<br />
ottenuto anche grazie ad indizi ricavati dal contesto. I<br />
linguaggi formali sono progettati per essere completamente non<br />
ambigui e ciò significa che ciascuna dichiarazione ha<br />
esattamente un significato, indipendente dal contesto.</p>
<p>Ridondanza<br />
per evitare l&#8217;ambiguità e ridurre le incomprensioni i linguaggi<br />
naturali impiegano molta ridondanza. I linguaggi formali sono<br />
meno ridondanti e più concisi.</p>
<p>Letteralità<br />
i linguaggi naturali fanno uso di paragoni e metafore, e<br />
possiamo parlare in termini astratti intuendo immediatamente<br />
che ciò che sentiamo ha un significato simbolico. I linguaggi<br />
formali invece esprimono esattamente ciò che dicono.</p>
<p>Anche se siamo cresciuti apprendendo un linguaggio naturale, la nostra<br />
lingua madre, spesso abbiamo difficoltà ad adattarci ai linguaggi<br />
formali. In un certo senso la differenza tra linguaggi naturali e<br />
formali è come quella esistente tra poesia e prosa, ma in misura<br />
decisamente più evidente:</p>
<p>Poesia<br />
le parole sono usate tanto per il loro suono che per il loro<br />
significato, e la poesia nel suo complesso crea un effetto o<br />
una risposta emotiva. L&#8217;ambiguità è non solo frequente, ma<br />
spesso addirittura cercata.</p>
<p>Prosa<br />
il significato delle parole è estremamente importante, con la<br />
struttura che contribuisce a fornire maggior significato. La<br />
prosa può essere soggetta ad analisi più facilmente della<br />
poesia, ma può risultare ancora ambigua.</p>
<p>Programmi<br />
il significato di un programma per computer è non ambiguo e<br />
assolutamente letterale, può essere compreso nella sua<br />
interezza con l&#8217;analisi dei token e della struttura.</p>
<p>Qui sono esposti alcuni suggerimenti per la lettura di programmi e di<br />
altri linguaggi formali.<br />
* Ricorda che i linguaggi formali sono molto più ricchi di<br />
significato dei linguaggi naturali, così è necessario più tempo<br />
per leggerli e comprenderli.<br />
* La struttura dei linguaggi formali è molto importante e<br />
solitamente non è una buona idea leggerli dall&#8217;alto in basso, da<br />
sinistra a destra, come avviene per un testo letterario: impara ad<br />
analizzare il programma nella tua testa, identificandone i token<br />
ed interpretandone la struttura.<br />
* I dettagli sono importanti: piccole cose come errori di ortografia<br />
e cattiva punteggiatura sono spesso trascurabili nei linguaggi<br />
naturali, ma possono fare una gran differenza in quelli formali.</p>
<p>1.5 Il primo programma</p>
<p>Per tradizione il primo programma scritto in un nuovo linguaggio è<br />
chiamato &#8220;Hello, World!&#8221; perché tutto ciò che fa è scrivere le parole<br />
Hello, World! a video e nient&#8217;altro. In Python questo programma è<br />
scritto così:</p>
<p>&gt;&gt;&gt; print &#8220;Hello, World!&#8221;</p>
<p>Questo è un esempio di istruzione di stampa, che in effetti non stampa<br />
nulla su carta limitandosi invece a scrivere un valore sullo schermo.<br />
In questo caso ciò che viene &#8220;stampato&#8221; sono le parole</p>
<p>Hello, World!</p>
<p>Le virgolette segnano l&#8217;inizio e la fine del valore da stampare ed<br />
esse non appaiono nel risultato.</p>
<p>Alcune persone giudicano la qualità di un linguaggio di programmazione<br />
dalla semplicità del programma &#8220;Hello, World!&#8221;: da questo punto di<br />
vista Python sembra essere quanto di meglio sia realizzabile.</p>
<p>1.6 Glossario</p>
<p>Soluzione di problemi<br />
il processo di formulare un problema, trovare una soluzione ed<br />
esprimerla.</p>
<p>Linguaggio ad alto livello<br />
un linguaggio di programmazione tipo Python che è progettato<br />
per essere facilmente leggibile e utilizzabile dagli esseri<br />
umani.</p>
<p>Linguaggio di basso livello<br />
un linguaggio di programmazione che è progettato per essere<br />
facilmente eseguibile da un computer; è anche chiamato<br />
&#8220;linguaggio macchina&#8221; o &#8220;linguaggio assembly&#8221;.</p>
<p>Portabilità<br />
caratteristica di un programma di poter essere eseguito su<br />
computer di tipo diverso.</p>
<p>Interpretare<br />
eseguire un programma scritto in un linguaggio di alto livello<br />
traducendolo ed eseguendolo immediatamente, una linea alla<br />
volta.</p>
<p>Compilare<br />
tradurre un programma scritto in un linguaggio di alto livello<br />
in un programma di basso livello come preparazione alla<br />
successiva esecuzione.</p>
<p>Codice sorgente<br />
un programma di alto livello prima di essere compilato.</p>
<p>Codice oggetto<br />
il risultato ottenuto da un compilatore dopo aver tradotto il<br />
codice sorgente.</p>
<p>Eseguibile<br />
altro nome per indicare il codice oggetto pronto per essere<br />
eseguito.</p>
<p>Script<br />
programma memorizzato in un file, solitamente destinato ad<br />
essere interpretato.</p>
<p>Programma<br />
serie di istruzioni che specificano come effettuare<br />
un&#8217;elaborazione.</p>
<p>Algoritmo<br />
processo generale usato per risolvere una particolare categoria<br />
di problemi.</p>
<p>Bug<br />
errore in un programma (detto anche &#8220;baco&#8221;).</p>
<p>Debug<br />
processo di ricerca e di rimozione di ciascuno dei tre tipi di<br />
errori di programmazione.</p>
<p>Sintassi<br />
struttura di un programma.</p>
<p>Errore di sintassi<br />
errore in un programma che rende impossibile la continuazione<br />
dell&#8217;analisi del codice (il programma non può quindi essere<br />
interpretato interamente o compilato).</p>
<p>Errore in esecuzione<br />
errore che non è riconoscibile finché il programma non è stato<br />
eseguito e che impedisce la continuazione della sua esecuzione.</p>
<p>Eccezione, errore runtime<br />
altri nomi per indicare un errore in esecuzione.</p>
<p>Errore di semantica<br />
errore nel programma che fa ottenere risultati diversi da<br />
quanto ci si aspettava.</p>
<p>Semantica<br />
significato di un programma.</p>
<p>Linguaggio naturale<br />
ognuno dei linguaggi parlati evoluti nel tempo.</p>
<p>Linguaggio formale<br />
ognuno dei linguaggi che sono stati progettati per scopi<br />
specifici, quali la rappresentazione di idee matematiche o<br />
programmi per computer (tutti i linguaggi per computer sono<br />
linguaggi formali).</p>
<p>Token<br />
uno degli elementi di base della struttura sintattica di un<br />
programma analogo alla parola nei linguaggi naturali.</p>
<p>Parsing<br />
esame e analisi della struttura sintattica di un programma.</p>
<p>Istruzione di stampa<br />
istruzione che ordina all&#8217;interprete Python di scrivere un<br />
valore sullo schermo.</p>
<p>Capitolo 2</p>
<p>Variabili, espressioni ed istruzioni</p>
<p>2.1 Valori e tipi</p>
<p>Un valore è una delle cose fondamentali manipolate da un<br />
programmatore, come lo sono una lettera dell&#8217;alfabeto nella scrittura<br />
o un numero in matematica. I valori che abbiamo visto finora sono<br />
&#8220;Hello, World!&#8221; e 2, quest&#8217;ultimo il risultato ottenuto quando abbiamo<br />
sommato 1+1.</p>
<p>Questi valori appartengono a tipi diversi: 2 è un intero, e &#8220;Hello,<br />
World!&#8221; è una stringa, così chiamata perché contiene una serie (o<br />
&#8220;stringa&#8221;) di caratteri. L&#8217;interprete può identificare le stringhe<br />
perché sono racchiuse da virgolette.</p>
<p>L&#8217;istruzione print funziona sia per le stringhe che per gli interi.</p>
<p>&gt;&gt;&gt; print 4<br />
4</p>
<p>Se non sei sicuro del tipo di un valore, l&#8217;interprete te lo può dire:</p>
<p>&gt;&gt;&gt; type(&#8220;Hello, World!&#8221;)<br />
&lt;type &#8216;string&#8217;&gt;<br />
&gt;&gt;&gt; type(17)<br />
&lt;type &#8216;int&#8217;&gt;</p>
<p>Ovviamente le stringhe appartengono al tipo string e gli interi al<br />
tipo int. Non è invece intuitivo il fatto che i numeri con il punto<br />
decimale appartengano al tipo float: questi numeri sono rappresentati<br />
in un formato chiamato virgola mobile o floating-point.</p>
<p>&gt;&gt;&gt; type(3.2)<br />
&lt;type &#8216;float&#8217;&gt;</p>
<p>Cosa dire di numeri come &#8220;17&#8243; e &#8220;3.2&#8243;? Sembrano effettivamente dei<br />
numeri, ma sono racchiusi tra virgolette e questo sicuramente<br />
significa qualcosa. Infatti non siamo in presenza di numeri ma di<br />
stringhe:</p>
<p>&gt;&gt;&gt; type(&#8220;17&#8243;)<br />
&lt;type &#8216;string&#8217;&gt;<br />
&gt;&gt;&gt; type(&#8220;3.2&#8243;)<br />
&lt;type &#8216;string&#8217;&gt;</p>
<p>Quando scrivi numeri grandi puoi essere tentato di usare dei punti per<br />
delimitare i gruppi di tre cifre, come in 1.000.000. Questa in effetti<br />
non è una cosa consentita in Python ed il valore numerico in questo<br />
caso non è valido. È invece corretta una scrittura del tipo</p>
<p>&gt;&gt;&gt; print 1,000,000<br />
1 0 0</p>
<p>&#8230;anche se probabilmente questo risultato non è quello che ci si<br />
aspettava! Python interpreta 1,000,000 come una lista di tre valori da<br />
stampare (1, 0 e 0). Ricordati di non inserire virgole nei tuoi<br />
interi.</p>
<p>2.2 Variabili</p>
<p>Una delle caratteristiche più potenti in un linguaggio di<br />
programmazione è la capacità di manipolare variabili. Una variabile è<br />
un nome che si riferisce ad un valore.</p>
<p>L&#8217;istruzione di assegnazione crea nuove variabili e assegna loro un<br />
valore:</p>
<p>&gt;&gt;&gt; messaggio = &#8220;Come va?&#8221;<br />
&gt;&gt;&gt; n = 17<br />
&gt;&gt;&gt; pi = 3.14159</p>
<p>Questo esempio effettua tre assegnazioni. La prima assegna la stringa<br />
&#8220;Come va?&#8221; ad una nuova variabile chiamata messaggio. La seconda<br />
assegna l&#8217;intero 17 alla variabile n e la terza assegna il valore in<br />
virgola mobile 3.14159 alla variabile pi.</p>
<p>Un modo comune di rappresentare le variabili sulla carta è scriverne<br />
il nome con una freccia che punta al valore della variabile. Questo<br />
tipo di figura è chiamato diagramma di stato perché mostra lo stato in<br />
cui si trova la variabile. Questo diagramma mostra il risultato<br />
dell&#8217;istruzione di assegnazione:</p>
<p>[i_state2.png]</p>
<p>L&#8217;istruzione print funziona anche con le variabili:</p>
<p>&gt;&gt;&gt; print messaggio<br />
Come va?<br />
&gt;&gt;&gt; print n<br />
17<br />
&gt;&gt;&gt; print pi<br />
3.14159</p>
<p>ed in ogni caso il risultato è il valore della variabile.</p>
<p>Anche le variabili hanno il tipo; ancora una volta possiamo chiedere<br />
all&#8217;interprete a quale tipo ogni variabile appartenga:</p>
<p>&gt;&gt;&gt; type(message)<br />
&lt;type &#8216;string&#8217;&gt;<br />
&gt;&gt;&gt; type(n)<br />
&lt;type &#8216;int&#8217;&gt;<br />
&gt;&gt;&gt; type(pi)<br />
&lt;type &#8216;float&#8217;&gt;</p>
<p>Il tipo di una variabile è il tipo di valore cui essa si riferisce.</p>
<p>2.3 Nomi delle variabili e parole riservate</p>
<p>I programmatori generalmente scelgono dei nomi significativi per le<br />
loro variabili, documentando così a che cosa servono.</p>
<p>I nomi delle variabili possono essere lunghi quanto si desidera e<br />
possono contenere sia lettere che numeri, ma devono sempre iniziare<br />
con una lettera. È legale usare sia lettere maiuscole che minuscole.<br />
Ricorda comunque che l&#8217;interprete le considera diverse così che<br />
Numero, NUmEro e numero sono a tutti gli effetti variabili diverse.</p>
<p>Il carattere di sottolineatura (_) può far parte di un nome ed è<br />
spesso usato in nomi di variabile composti da più parole (per esempio<br />
il_mio_nome e prezzo_del_the. In alternativa le parole possono essere<br />
composte usando l&#8217;iniziale maiuscola per ciascuna di esse, con il<br />
resto dei caratteri lasciati in minuscolo come in IlMioNome e<br />
PrezzoDelThe. Sembra che tra i due metodi quest&#8217;ultimo sia il più<br />
diffuso così lo adotteremo gradualmente nel corso delle lezioni.</p>
<p>Assegnando un nome illegale alla variabile otterrai un messaggio<br />
d&#8217;errore di sintassi:</p>
<p>&gt;&gt;&gt; 76strumenti = &#8220;grande banda&#8221;<br />
SyntaxError: invalid syntax<br />
&gt;&gt;&gt; milione$ = 1000000<br />
SyntaxError: invalid syntax<br />
&gt;&gt;&gt; class = &#8220;Computer Science 101&#8243;<br />
SyntaxError: invalid syntax</p>
<p>76strumenti è illegale perché non inizia con una lettera. milione$ è<br />
illegale perché contiene un carattere non valido (il segno di dollaro<br />
$). Ma cosa c&#8217;è di sbagliato in class?</p>
<p>class è una delle parole riservate di Python. Le parole riservate<br />
definiscono le regole del linguaggio e della struttura e non possono<br />
essere usate come nomi di variabili.</p>
<p>Python ha 28 parole riservate:</p>
<p>and      continue  else      for      import    not      raise<br />
assert   def       except    from     in        or       return<br />
break    del       exec      global   is        pass     try<br />
class    elif      finally   if       lambda    print    while</p>
<p>Sarebbe meglio tenere questa lista a portata di mano: se l&#8217;interprete<br />
ha problemi con il nome che vuoi assegnare ad una variabile e non ne<br />
capisci il motivo, prova a controllare se si trova in questa lista.</p>
<p>2.4 Istruzioni</p>
<p>Un&#8217;istruzione è un&#8217;operazione che l&#8217;interprete Python può eseguire.<br />
Abbiamo già visto due tipi di istruzioni: istruzioni di stampa * Note<br />
e di assegnazione.</p>
<p>Quando scrivi un&#8217;istruzione sulla riga di comando, Python la esegue e<br />
se previsto stampa il risultato a video. Un&#8217;istruzione di assegnazione<br />
di per sé non produce risultati visibili mentre il risultato di<br />
un&#8217;istruzione di stampa è un valore mostrato a video.</p>
<p>Uno script di solito contiene una sequenza di istruzioni: se sono<br />
presenti più istruzioni i loro risultati appariranno via via che le<br />
singole istruzioni saranno eseguite.</p>
<p>Per esempio lo script:</p>
<p>print 1<br />
x = 2<br />
print x</p>
<p>produce questa stampa:</p>
<p>1<br />
2</p>
<p>2.5 Valutazione delle espressioni</p>
<p>Un&#8217;espressione è una combinazione di valori, variabili e operatori. Se<br />
scrivi un&#8217;espressione sulla riga di comando l&#8217;interprete la valuta e<br />
mostra a video il risultato:</p>
<p>&gt;&gt;&gt; 1 + 1<br />
2</p>
<p>Sia un valore (numerico o stringa) che una variabile sono già di per<br />
sé delle espressioni:</p>
<p>&gt;&gt;&gt; 17<br />
17<br />
&gt;&gt;&gt; x<br />
2</p>
<p>La differenza tra &#8220;valutare un&#8217;espressione&#8221; e stamparne il valore è<br />
sottile ma importante:</p>
<p>&gt;&gt;&gt; messaggio = &#8220;Come va?&#8221;<br />
&gt;&gt;&gt; messaggio<br />
&#8220;Come va?&#8221;<br />
&gt;&gt;&gt; print messaggio<br />
Come va?</p>
<p>Quando Python mostra il valore di un&#8217;espressione usa lo stesso formato<br />
che si userebbe per inserirla: nel caso delle stringhe ciò significa<br />
che include le virgolette di delimitazione. L&#8217;istruzione print invece<br />
stampa il valore dell&#8217;espressione, che nel caso delle stringhe<br />
corrisponde al loro contenuto. Le virgolette sono quindi rimosse.</p>
<p>In uno script un valore preso da solo è legale, anche se non fa niente<br />
e non produce alcun risultato:</p>
<p>17<br />
3.2<br />
&#8220;Hello, World!&#8221;<br />
1 + 1</p>
<p>Lo script dell&#8217;esempio non produce alcun risultato. Come lo<br />
modificheresti per mostrare i quattro valori?</p>
<p>2.6 Operatori e operandi</p>
<p>Gli operatori sono simboli speciali che rappresentano elaborazioni di<br />
tipo matematico, quali la somma e la moltiplicazione. I valori che<br />
l&#8217;operatore usa nei calcoli sono chiamati operandi.</p>
<p>Le seguenti espressioni sono tutte legali in Python, ed il loro<br />
significato dovrebbe esserti chiaro:</p>
<p>20+32   ore-1   ore*60+minuti   minuti/60   5**2   (5+9)*(15-7)</p>
<p>L&#8217;uso dei simboli +, -, / e delle parentesi sono uguali a all&#8217;uso che<br />
se ne fa in matematica. L&#8217;asterisco (*) è il simbolo della<br />
moltiplicazione ed il doppio asterisco (**) quello dell&#8217;elevamento a<br />
potenza.</p>
<p>Quando una variabile compare al posto di un operando essa è<br />
rimpiazzata dal valore che rappresenta prima che l&#8217;operazione sia<br />
eseguita.</p>
<p>Addizione, sottrazione, moltiplicazione ed elevamento a potenza fanno<br />
tutto ciò che potresti aspettarti, ma la divisione potrebbe non<br />
sembrare così intuitiva. L&#8217;operazione seguente ha infatti un risultato<br />
inatteso:</p>
<p>&gt;&gt;&gt; minuti = 59<br />
&gt;&gt;&gt; minuti/60<br />
0</p>
<p>Il valore di minuti è 59, e 59 diviso 60 è 0.98333, non zero. La<br />
ragione di questa differenza sta nel fatto che Python sta facendo una<br />
divisione tra numeri interi.</p>
<p>Quando entrambi gli operandi sono numeri interi il risultato è sempre<br />
un numero intero e per convenzione la divisione tra numeri interi<br />
restituisce sempre un numero arrotondato all&#8217;intero inferiore<br />
(arrotondamento verso il basso), anche nel caso in cui il risultato<br />
sia molto vicino all&#8217;intero superiore.</p>
<p>Una possibile soluzione a questo problema potrebbe essere il calcolo<br />
della percentuale, piuttosto che del semplice valore decimale:</p>
<p>&gt;&gt;&gt; minuti*100/60<br />
98</p>
<p>Ancora una volta il valore è arrotondato per difetto, ma almeno la<br />
risposta è approssimativamente corretta. Un&#8217;altra alternativa è l&#8217;uso<br />
della divisione in virgola mobile che tratteremo nella sezione 3.</p>
<p>2.7 Ordine delle operazioni</p>
<p>Quando più operatori compaiono in un&#8217;espressione, l&#8217;ordine di<br />
valutazione dipende dalle regole di precedenza. Python segue le stesse<br />
regole di precedenza usate in matematica:<br />
* Parentesi: hanno il più alto livello di precedenza e possono<br />
essere usate per far valutare l&#8217;espressione in qualsiasi ordine.<br />
Dato che le espressioni tra parentesi sono valutate per prime,<br />
2*(3-1) dà come risultato 4, e (1+1)**(5-2) dà 8. Puoi usare le<br />
parentesi per rendere più leggibile un&#8217;espressione come in<br />
(minuti*100)/60, anche se questo non influisce sul risultato.<br />
* Elevamento a potenza: ha la priorità successiva così 2**1+1 fa 3 e<br />
non 4, e 3*1**3 fa 3 e non 27.<br />
* Moltiplicazione e Divisione hanno la stessa priorità, superiore a<br />
somma e sottrazione. 2*3-1 dà 5 e non 4, e 2/3-1 fa -1, e non 1<br />
(ricorda che la divisione intera 2/3 restituisce 0).<br />
* Addizione e Sottrazione, anch&#8217;esse con la stessa priorità.<br />
* Gli operatori con la stessa priorità sono valutati da sinistra<br />
verso destra, così che nell&#8217;espressione minuti*100/60, la<br />
moltiplicazione è valutata per prima, ottenendo 5900/60, che a sua<br />
volta restituisce 98. Se le operazioni fossero state valutate da<br />
destra a sinistra il risultato sarebbe stato sbagliato: 59*1=59.</p>
<p>2.8 Operazioni sulle stringhe</p>
<p>In generale non puoi effettuare operazioni matematiche sulle stringhe,<br />
anche se il loro contenuto sembra essere un numero. Se supponiamo che<br />
messaggio sia di tipo string gli esempi proposti di seguito sono<br />
illegali:</p>
<p>messaggio-1   &#8220;Ciao&#8221;/123   messaggio*&#8221;Ciao&#8221;   &#8220;15&#8243;+2</p>
<p>L&#8217;operatore + funziona con le stringhe anche se la sua funzione è<br />
diversa da quella cui siamo abituati in matematica: infatti nel caso<br />
di stringhe l&#8217;operatore + rappresenta il concatenamento, cioè<br />
l&#8217;aggiunta del secondo operando alla fine del primo. Per esempio:</p>
<p>frutta = &#8220;banana&#8221;<br />
verdura = &#8221; pomodoro&#8221;<br />
print frutta + verdura</p>
<p>Il risultato a video di questo programma è banana pomodoro. Lo spazio<br />
davanti alla parola pomodoro è parte della stringa ed è necessario per<br />
produrre lo spazio tra le due stringhe concatenate.</p>
<p>Anche l&#8217;operatore * lavora sulle stringhe pur con un significato<br />
diverso rispetto a quello matematico: infatti causa la ripetizione<br />
della stringa. Per fare un esempio, &#8220;Casa&#8221;*3 è &#8220;CasaCasaCasa&#8221;. Uno<br />
degli operandi deve essere una stringa, l&#8217;altro un numero intero.</p>
<p>Da una parte questa interpretazione di + e di * ha senso per analogia<br />
con l&#8217;addizione e la moltiplicazione in matematica. Così come 4*3 è<br />
equivalente a 4+4+4, ci aspettiamo che &#8220;Casa&#8221;*3 sia lo stesso di<br />
&#8220;Casa&#8221;+&#8221;Casa&#8221;+&#8221;Casa&#8221;, ed effettivamente è così. D&#8217;altro canto c&#8217;è un<br />
particolare sostanziale che rende diverse la somma e la<br />
moltiplicazione di numeri e di stringhe.</p>
<p>Riesci ad immaginare una proprietà che somma e moltiplicazione tra<br />
numeri non condividono con concatenamento e ripetizione di<br />
stringhe?</p>
<p>2.9 Composizione</p>
<p>Finora abbiamo guardato agli elementi di un programma (variabili,<br />
espressioni e istruzioni) prendendoli isolatamente, senza parlare di<br />
come combinarli.</p>
<p>Una delle più utili caratteristiche dei linguaggi di programmazione è<br />
la loro capacità di prendere piccoli blocchi di costruzione e di<br />
comporli.</p>
<p>Sappiamo già sommare e stampare dei numeri e possiamo fare le due<br />
operazioni nello stesso momento:</p>
<p>&gt;&gt;&gt;  print 17 + 3<br />
20</p>
<p>In realtà l&#8217;addizione è stata portata a termine prima della stampa,<br />
così che le due operazioni non stanno avvenendo contemporaneamente.<br />
Qualsiasi operazione che ha a che fare con i numeri, le stringhe e le<br />
variabili può essere usata all&#8217;interno di un&#8217;istruzione di stampa. Hai<br />
già visto un esempio a riguardo:</p>
<p>print &#8220;Numero di minuti da mezzanotte: &#8220;, ore*60+minuti</p>
<p>Puoi anche inserire espressioni arbitrarie nella parte destra di<br />
un&#8217;istruzione di assegnazione:</p>
<p>percentuale = (minuti * 100) / 60</p>
<p>Questa capacità può non sembrare particolarmente impressionante, ma<br />
vedrai presto altri esempi in cui la composizione permette di<br />
esprimere elaborazioni complesse in modo chiaro e conciso.</p>
<p>Attenzione: ci sono dei limiti su &#8220;dove&#8221; puoi usare certe espressioni.<br />
Per esempio la parte sinistra di un&#8217;istruzione di assegnazione può<br />
solo essere una variabile, e non un&#8217;espressione. minuti*60 = ore è<br />
illegale.</p>
<p>2.10 Commenti</p>
<p>Man mano che il programma cresce di dimensioni diventa sempre più<br />
difficile da leggere. I linguaggi formali sono ricchi di significato,<br />
e può risultare difficile capire a prima vista cosa fa un pezzo di<br />
codice o perché è stato scritto in un certo modo.</p>
<p>Per questa ragione è una buona idea aggiungere delle note ai tuoi<br />
programmi per spiegare con un linguaggio naturale cosa sta facendo il<br />
programma nelle sue varie parti. Queste note sono chiamate commenti, e<br />
sono marcati dal simbolo #:</p>
<p># calcola la percentuale di ore trascorse<br />
percentuale = (minuti*100)/60</p>
<p>In questo caso il commento appare come una linea a sé stante. Puoi<br />
eventualmente inserire un commento alla fine di una riga:</p>
<p>percentuale = (minuti*100)/60   # attenzione: divisione intera</p>
<p>Qualsiasi cosa scritta dopo il simbolo # e fino alla fine della riga<br />
viene trascurata nell&#8217;esecuzione del programma. Il commento serve al<br />
programmatore o ai futuri programmatori che dovranno usare questo<br />
codice. In questo ultimo esempio il commento ricorda al lettore che ci<br />
potrebbe essere un comportamento inatteso dovuto all&#8217;uso della<br />
divisione tra numeri interi.</p>
<p>2.11 Glossario</p>
<p>Valore<br />
numero o stringa (o altri tipi di dato che vedremo in seguito)<br />
che può essere memorizzato in una variabile o usato in una<br />
espressione.</p>
<p>Tipo<br />
formato di un valore che determina come esso possa essere usato<br />
nelle espressioni. Finora hai visto i numeri interi (tipo int),<br />
i numeri in virgola mobile (tipo float) e le stringhe (tipo<br />
string).</p>
<p>Virgola mobile<br />
formato di dati che rappresenta i numeri con parte decimale; è<br />
anche detto &#8220;floating-point&#8221;.</p>
<p>Variabile<br />
nome che si riferisce ad un valore.</p>
<p>Istruzione<br />
sezione di codice che rappresenta un comando o un&#8217;azione.<br />
Finora hai visto istruzioni di assegnazione e di stampa.</p>
<p>Assegnazione<br />
istruzione che assegna un valore ad una variabile.</p>
<p>Diagramma di stato<br />
rappresentazione grafica di una serie di variabili e dei valori<br />
cui esse si riferiscono.</p>
<p>Parola riservata<br />
parola che ha un significato particolare per il linguaggio e<br />
non può essere usata come nome di variabile o di funzione.</p>
<p>Operatore<br />
simbolo speciale che rappresenta un&#8217;elaborazione semplice tipo<br />
l&#8217;addizione, la moltiplicazione o il concatenamento di<br />
stringhe.</p>
<p>Operando<br />
uno dei valori sui quali agisce un operatore.</p>
<p>Espressione<br />
combinazione di variabili, operatori e valori che sono<br />
sostituibili da un unico valore equivalente.</p>
<p>Valutazione<br />
semplificazione di un&#8217;espressione seguendo una serie di<br />
operazioni per produrre un singolo valore.</p>
<p>Divisione tra numeri interi<br />
operazione che divide un numero intero per un altro intero.</p>
<p>Regole di precedenza<br />
insieme di regole che determinano l&#8217;ordine nel quale vengono<br />
analizzate espressioni complesse dove sono presenti più<br />
operandi ed operatori.</p>
<p>Concatenamento<br />
unione di due stringhe tramite l&#8217;accodamento della seconda alla<br />
prima.</p>
<p>Composizione<br />
capacità di combinare espressioni semplici in istruzioni<br />
composite in modo da rappresentare elaborazioni complesse in<br />
forma chiara e concisa.</p>
<p>Commento<br />
informazione riguardante il significato di una parte del<br />
programma; non ha alcun effetto sull&#8217;esecuzione del programma<br />
ma serve solo per facilitarne la comprensione.</p>
<p>Capitolo 3</p>
<p>Funzioni</p>
<p>3.1 Chiamate di funzioni</p>
<p>Hai già visto un esempio di chiamata di funzione:</p>
<p>&gt;&gt;&gt; type(&#8220;32&#8243;)<br />
&lt;type &#8216;string&#8217;&gt;</p>
<p>Il nome della funzione è type e mostra il tipo di valore della<br />
variabile. Il valore della variabile, che è chiamato argomento della<br />
funzione, deve essere racchiuso tra parentesi. È comune dire che una<br />
funzione &#8220;prende&#8221; o &#8220;accetta&#8221; un argomento e &#8220;ritorna&#8221; o &#8220;restituisce&#8221;<br />
un risultato. Il risultato è detto valore di ritorno.</p>
<p>Invece di stampare il valore di ritorno possiamo assegnarlo ad una<br />
variabile:</p>
<p>&gt;&gt;&gt; betty = type(&#8220;32&#8243;)<br />
&gt;&gt;&gt; print betty<br />
&lt;type &#8216;string&#8217;&gt;</p>
<p>Come esempio ulteriore, la funzione id prende un valore o una<br />
variabile e ritorna un intero che agisce come un identificatore unico<br />
del valore:</p>
<p>&gt;&gt;&gt; id(3)<br />
134882108<br />
&gt;&gt;&gt; betty = 3<br />
&gt;&gt;&gt; id(betty)<br />
134882108</p>
<p>Ogni valore ha un id unico che rappresenta dove è depositato nella<br />
memoria del computer. L&#8217;id di una variabile è l&#8217;id del valore della<br />
variabile cui essa si riferisce.</p>
<p>3.2 Conversione di tipo</p>
<p>Python fornisce una raccolta di funzioni interne che converte valori<br />
da un tipo all&#8217;altro. La funzione int prende ogni valore e lo<br />
converte, se possibile, in intero. Se la conversione non è possibile<br />
mostra un messaggio d&#8217;errore:</p>
<p>&gt;&gt;&gt; int(&#8220;32&#8243;)<br />
32<br />
&gt;&gt;&gt; int(&#8220;Hello&#8221;)<br />
ValueError: invalid literal for int(): Hello</p>
<p>int può anche convertire valori in virgola mobile in interi, ma<br />
ricorda che nel farlo tronca (cioè toglie) la parte decimale.</p>
<p>&gt;&gt;&gt; int(3.99999)<br />
3<br />
&gt;&gt;&gt; int(-2.3)<br />
-2</p>
<p>La funzione float converte interi e stringhe in numeri in virgola<br />
mobile:</p>
<p>&gt;&gt;&gt; float(32)<br />
32.0<br />
&gt;&gt;&gt; float(&#8220;3.14159&#8243;)<br />
3.14159</p>
<p>Infine str converte al tipo stringa:</p>
<p>&gt;&gt;&gt; str(32)<br />
&#8217;32&#8242;<br />
&gt;&gt;&gt; str(3.14149)<br />
&#8217;3.14149&#8242;</p>
<p>Può sembrare strano il fatto che Python distingua il valore intero 1<br />
dal corrispondente valore in virgola mobile 1.0. Questi rappresentano<br />
effettivamente uno stesso numero ma appartengono a tipi differenti<br />
(rispettivamente intero e in virgola mobile) e quindi vengono<br />
rappresentati in modo diverso all&#8217;interno della memoria del computer.</p>
<p>3.3 Forzatura di tipo</p>
<p>Per tornare ad un esempio del capitolo precedente (la divisione di<br />
minuti per 60), ora che sappiamo convertire i tipi abbiamo un modo<br />
ulteriore per gestire la divisione tra interi. Supponiamo di dover<br />
calcolare la frazione di ora che è trascorsa: l&#8217;espressione più ovvia,<br />
minuti/60, lavora con numeri interi, così il risultato è sempre 0<br />
anche se sono trascorsi 59 minuti.</p>
<p>Una delle soluzioni è quella di convertire minuti in virgola mobile e<br />
calcolare il risultato della divisione in virgola mobile:</p>
<p>&gt;&gt;&gt; minuti = 59<br />
&gt;&gt;&gt; float(minuti) / 60.0<br />
0.983333333333</p>
<p>In alternativa possiamo avvantaggiarci delle regole di conversione<br />
automatica dei tipi chiamate forzature di tipo. Nel caso di operatori<br />
matematici se uno degli operandi è float, l&#8217;altro è automaticamente<br />
convertito a float:</p>
<p>&gt;&gt;&gt; minuti = 59<br />
&gt;&gt;&gt; minuti / 60.0<br />
0.983333333333</p>
<p>Convertendo il denominatore a valore in virgola mobile forziamo Python<br />
a calcolare il risultato di una divisione in virgola mobile.</p>
<p>3.4 Funzioni matematiche</p>
<p>In matematica hai probabilmente visto funzioni del tipo sin e log, ed<br />
hai imparato a calcolare espressioni quali sin(pi/2) e log(1/x).<br />
Innanzitutto devi calcolare il valore dell&#8217;espressione tra parentesi<br />
(l&#8217;argomento). Nell&#8217;esempio pi/2 è approssimativamente 1.571 e se x<br />
vale 10.0, 1/x è 0.1.</p>
<p>Poi valuti la funzione stessa tramite calcoli o tabelle. sin di 1.571<br />
è circa 1, e log in base 10 di 0.1 è -1.</p>
<p>Questo processo può essere applicato ripetutamente per valutare<br />
espressioni complesse del tipo log(1/sin(pi/2)). In questo caso devi<br />
iniziare dall&#8217;espressione più interna pi/2, calcolando poi il seno con<br />
sin, seguito dall&#8217;inverso del seno 1/x e dal logaritmo dell&#8217;inverso<br />
log(x).</p>
<p>Python è provvisto di un modulo matematico che permette di eseguire le<br />
più comuni operazioni matematiche. Un modulo è un file che contiene<br />
una raccolta di funzioni raggruppate.</p>
<p>Prima di poter usare le funzioni di un modulo dobbiamo dire<br />
all&#8217;interprete di caricare il modulo in memoria. Questa operazione<br />
viene detta &#8220;importazione&#8221;:</p>
<p>&gt;&gt;&gt; import math</p>
<p>Per chiamare una funzione di un modulo dobbiamo specificare il nome<br />
del modulo che la contiene e il nome della funzione separati da un<br />
punto. Questo formato è chiamato notazione punto.</p>
<p>&gt;&gt;&gt; decibel = math.log10 (17.0)<br />
&gt;&gt;&gt; angolo = 1.5<br />
&gt;&gt;&gt; altezza = math.sin(angolo)</p>
<p>La prima istruzione assegna a decibel il logaritmo di 17 in base 10. È<br />
anche disponibile la funzione log che calcola il logaritmo naturale di<br />
un numero.</p>
<p>La terza istruzione trova il seno del valore della variabile angolo.<br />
sin e le altre funzioni trigonometriche (cos, tan, etc.) accettano<br />
argomenti in radianti e non i gradi. Per convertire da gradi in<br />
radianti devi dividere per 360 e moltiplicare per 2 pi. Per esempio,<br />
per calcolare il seno di 45 gradi, prima trasforma l&#8217;angolo in<br />
radianti e poi usa la funzione seno:</p>
<p>&gt;&gt;&gt; gradi = 45<br />
&gt;&gt;&gt; angolo = gradi * 2 * math.pi / 360.0<br />
&gt;&gt;&gt; math.sin(angolo)</p>
<p>La costante pi fa già parte del modulo matematico math. Se conosci un<br />
po&#8217; di geometria puoi verificare il risultato confrontandolo con</p>
<p>SQRT<br />
_________________________________________________________________</p>
<p>2</p>
<p>/2<br />
:</p>
<p>&gt;&gt;&gt; math.sqrt(2) / 2.0<br />
0.707106781187</p>
<p>3.5 Composizione</p>
<p>Così come in matematica anche in Python le funzioni possono essere<br />
composte, facendo in modo che il risultato di una possa essere usato<br />
come argomento di un&#8217;altra:</p>
<p>&gt;&gt;&gt; x = math.cos(angolo + math.pi/2)</p>
<p>Questa istruzione prende il valore di pi (math.pi), lo divide per 2 e<br />
somma il quoziente ad angolo. La somma è poi passata come argomento<br />
alla funzione cos che ne calcola il coseno.</p>
<p>&gt;&gt;&gt; x = math.exp(math.log(10.0))</p>
<p>In quest&#8217;altro esempio l&#8217;istruzione log calcola il logaritmo naturale<br />
(in base e) di 10 e poi eleva e al valore precedentemente calcolato.<br />
Il risultato viene assegnato ad x.</p>
<p>3.6 Aggiungere nuove funzioni</p>
<p>Finora abbiamo soltanto usato funzioni che fanno parte di Python, ma è<br />
possibile aggiungerne di nuove. La creazione di nuove funzioni per<br />
risolvere un particolare problema è infatti una tra le cose più utili<br />
di un linguaggio di programmazione generale, intendendo con &#8220;generale&#8221;<br />
che il linguaggio non è destinato ad un settore di applicazioni<br />
particolari, quale può essere quello scientifico o finanziario, ma che<br />
può essere usato in ogni campo).</p>
<p>Nel contesto della programmazione una funzione è una sequenza di<br />
istruzioni che esegue una determinata operazione. Questa azione è<br />
descritta in una definizione di funzione. Le funzioni che abbiamo<br />
usato finora sono state definite per noi e le loro definizioni sono<br />
rimaste nascoste: questa è una cosa positiva in quanto possiamo usarle<br />
senza doverci preoccupare di come sono state definite da chi le ha<br />
scritte.</p>
<p>La sintassi per la definizione di una funzione è:</p>
<p>def NOME( LISTA_DEI_PARAMETRI ):<br />
ISTRUZIONI</p>
<p>Puoi usare qualsiasi nome per una funzione, fatta eccezione per le<br />
parole riservate di Python. La lista dei parametri di una funzione<br />
specifica quali informazioni, sempre che ne sia prevista qualcuna,<br />
desideri fornire alla funzione per poterla usare.</p>
<p>All&#8217;interno della funzione sono naturalmente presenti delle istruzioni<br />
e queste devono essere indentate rispetto al margine sinistro. Di<br />
solito il rientro è di un paio di spazi, ma questa è solo una<br />
convenzione: per questioni puramente estetiche potresti volerne usare<br />
di più. Mentre nella maggior parte dei linguaggi il rientro è<br />
facoltativo e dipende da come il programmatore vuole organizzare<br />
visivamente il suo codice, in Python il rientro è obbligatorio. Questa<br />
scelta può sembrare un vincolo forzoso, ma ha il vantaggio di<br />
garantire una certa uniformità di stile e per quanto disordinato possa<br />
essere un programmatore il codice conserverà sempre un minimo di<br />
ordine.</p>
<p>La prima coppia di funzioni che stiamo per scrivere non ha parametri e<br />
la sintassi è:</p>
<p>def UnaRigaVuota():<br />
print</p>
<p>Questa funzione si chiama UnaRigaVuota. Le parentesi vuote stanno ad<br />
indicare che non ci sono parametri. La funzione è composta da una<br />
singola riga che stampa una riga vuota (questo è ciò che succede<br />
quando usi il comando print senza argomenti).</p>
<p>La sintassi per richiamare la funzione che hai appena definito è la<br />
stessa che hai usato per richiamare le funzioni predefinite:</p>
<p>print &#8220;Prima riga.&#8221;<br />
UnaRigaVuota()<br />
print &#8220;Seconda riga.&#8221;</p>
<p>Il risultato del programma è una scrittura a video:</p>
<p>Prima riga.<br />
Seconda riga.</p>
<p>Nota lo spazio tra le due righe. Cosa avresti dovuto fare se c&#8217;era<br />
bisogno di più spazio? Ci sono varie possibilità. Avresti potuto<br />
chiamare più volte la funzione:</p>
<p>print &#8220;Prima riga.&#8221;<br />
UnaRigaVuota()<br />
UnaRigaVuota ()<br />
UnaRigaVuota ()<br />
print &#8220;Seconda riga.&#8221;</p>
<p>o avresti potuto creare una nuova funzione chiamata TreRigheVuote che<br />
stampa tre righe vuote:</p>
<p>def TreRigheVuote():<br />
UnaRigaVuota()<br />
UnaRigaVuota()<br />
UnaRigaVuota()<br />
print &#8220;Prima riga.&#8221;<br />
TreRigheVuote()<br />
print &#8220;Seconda riga.&#8221;</p>
<p>Questa funzione contiene tre istruzioni, tutte indentate di due spazi<br />
proprio per indicare che fanno parte della definizione della funzione.<br />
Dato che dopo la definizione, alla fine del terzo UnaRigaVuota(), la<br />
riga successiva,<br />
print &#8220;Prima riga.&#8221; non ha più indentazione, ciò significa che questa<br />
non fa più parte della definizione e che la definizione deve essere<br />
considerata conclusa.</p>
<p>Puoi notare alcune cose riguardo questo programma:<br />
1. Puoi chiamare più volte la stessa procedura. È abbastanza comune e<br />
utile farlo.<br />
2. Una funzione può chiamare altre funzioni al suo interno: in questo<br />
caso TreRigheVuote chiama UnaRigaVuota.</p>
<p>Può non essere ancora chiaro perché sia il caso di creare tutte queste<br />
nuove funzioni. Effettivamente di ragioni ce ne sono tante, qui ne<br />
indichiamo due:<br />
* Creare una funzione ti dà l&#8217;opportunità di raggruppare e<br />
identificare con un nome un gruppo di istruzioni. Le funzioni<br />
possono semplificare un programma nascondendo un&#8217;elaborazione<br />
complessa dietro un singolo comando, e usando parole comprensibili<br />
per richiamarla invece di codice difficile da capire.<br />
* La creazione di funzioni rende più piccolo il programma,<br />
eliminando le parti ripetitive. Per fare un esempio, se vogliamo<br />
stampare 9 righe vuote, possiamo chiamare 9 volte la funzione<br />
UnaRigaVuota o 3 volte la funzione TreRigheVuote.</p>
<p>Esercizio: scrivi una funzione chiamata NoveRigheVuote che usa<br />
TreRigheVuote per scrivere 9 righe bianche. Cosa faresti poi per<br />
scrivere 27 righe bianche?</p>
<p>3.7 Definizioni e uso</p>
<p>Raggruppando assieme i frammenti di codice della sezione precedente il<br />
programma diventa:</p>
<p>def UnaRigaVuota():<br />
print<br />
def TreRigheVuote():<br />
UnaRigaVuota()<br />
UnaRigaVuota()<br />
UnaRigaVuota()<br />
print &#8220;Prima riga.&#8221;<br />
TreRigheVuote()<br />
print &#8220;Seconda riga.&#8221;</p>
<p>Questo programma contiene la definizione di due funzioni: UnaRigaVuota<br />
e TreRigheVuote. Le definizioni di funzione sono eseguite come le<br />
altre istruzioni ma il loro effetto è quello di creare una nuova<br />
funzione. Le istruzioni all&#8217;interno di una definizione non sono<br />
eseguite finché la funzione non è chiamata e la definizione in sé non<br />
genera alcun risultato. Come puoi facilmente immaginare, prima di<br />
poter usare una funzione devi averla definita: la definizione della<br />
funzione deve sempre precedere la sua chiamata.</p>
<p>Esercizio: sposta le ultime tre righe del programma all&#8217;inizio, per<br />
fare in modo che la chiamata alle funzioni appaia prima della loro<br />
definizione. Esegui il programma e vedi che tipo di messaggio<br />
d&#8217;errore ottieni.</p>
<p>Esercizio: inizia con il programma funzionante e sposta la<br />
definizione di UnaRigaVuota dopo la definizione di TreRigheVuote.<br />
Cosa succede quando esegui il programma?</p>
<p>3.8 Flusso di esecuzione</p>
<p>Per assicurarti che una funzione sia definita prima del suo uso devi<br />
conoscere l&#8217;ordine in cui le istruzioni sono eseguite cioè il flusso<br />
di esecuzione del programma.</p>
<p>L&#8217;esecuzione inizia sempre alla prima riga del programma e le<br />
istruzioni sono eseguite una alla volta dall&#8217;alto verso il basso.</p>
<p>La definizione di funzioni non altera il flusso di esecuzione del<br />
programma ma ricorda che le istruzioni all&#8217;interno delle funzioni non<br />
sono eseguite finché la funzione non viene chiamata. Sebbene questo<br />
non sia una cosa che avviene frequentemente, puoi anche definire una<br />
funzione all&#8217;interno di un&#8217;altra funzione. In questo caso la funzione<br />
più interna non sarà eseguita finché non viene chiamata anche quella<br />
più esterna.</p>
<p>La chiamata alle funzioni è una deviazione nel flusso di esecuzione:<br />
invece di proseguire con l&#8217;istruzione successiva, il flusso salta alla<br />
prima riga della funzione chiamata ed esegue tutte le sue istruzioni;<br />
alla fine della funzione il flusso riprende dal punto dov&#8217;era stato<br />
deviato dalla chiamata di funzione.</p>
<p>Questo è abbastanza comprensibile ma non devi dimenticare che una<br />
funzione ne può chiamare un&#8217;altra al suo interno. Può succedere che il<br />
programma principale chiami una funzione che a sua volta ne chiama<br />
un&#8217;altra: alla fine della seconda funzione il flusso torna alla prima,<br />
dov&#8217;era stato lasciato in sospeso, e quando anche la prima funzione è<br />
stata completata il flusso di esecuzione torna al programma<br />
principale.</p>
<p>Fortunatamente Python è sufficientemente intelligente da ricordare<br />
dove il flusso di esecuzione viene via via interrotto e sa dove<br />
riprendere quando una funzione è conclusa. Se il flusso di programma<br />
giunge all&#8217;ultima istruzione, dopo la sua esecuzione il programma è<br />
terminato.</p>
<p>Qual è il senso di tutto questo discorso? Quando leggi un programma<br />
non limitarti a farlo dall&#8217;alto in basso, come stessi leggendo un<br />
libro: cerca invece di seguire il flusso di esecuzione, con i suoi<br />
salti all&#8217;interno delle procedure.</p>
<p>3.9 Parametri e argomenti</p>
<p>Alcune delle funzioni che devi usare richiedono argomenti, i valori<br />
che controllano come la funzione deve portare a termine il proprio<br />
compito. Per esempio, se vuoi trovare il seno di un numero devi<br />
indicare quale sia questo numero: sin si aspetta infatti un valore<br />
numerico come argomento.</p>
<p>Alcune funzioni prendono due o più parametri: pow si aspetta due<br />
argomenti che sono la base e l&#8217;esponente in un&#8217;operazione di<br />
elevamento a potenza. Dentro la funzione i valori che sono passati<br />
vengono assegnati a variabili chiamate parametri.</p>
<p>Eccoti un esempio di definizione di una funzione con un parametro:</p>
<p>def Stampa2Volte(Valore):<br />
print Valore, Valore</p>
<p>Questa funzione si aspetta un unico argomento e lo assegna ad un<br />
parametro chiamato Valore. Il valore del parametro (a questo punto del<br />
programma non sappiamo nemmeno di che tipo sarà, se stringa, intero o<br />
di altro tipo) è stampato due volte. La stampa è poi conclusa con un<br />
ritorno a capo. Il nome Valore è stato scelto per ricordarti che sta a<br />
te sceglierne uno sufficientemente esplicativo, e di solito ne<br />
sceglierai qualcuno che ricordi l&#8217;uso della funzione o della<br />
variabile.</p>
<p>La funzione Stampa2Volte funziona per ogni tipo di dato che può essere<br />
stampato:</p>
<p>&gt;&gt;&gt; Stampa2Volte(&#8216;Pippo&#8217;)<br />
Pippo Pippo<br />
&gt;&gt;&gt; Stampa2Volte(5)<br />
5 5<br />
&gt;&gt;&gt; Stampa2Volte(3.14159)<br />
3.14159 3.14159</p>
<p>Nella prima chiamata di funzione l&#8217;argomento è una stringa, nella<br />
seconda un intero e nella terza un numero in virgola mobile (float).</p>
<p>Le stesse regole per la composizione che sono state descritte per le<br />
funzioni predefinite valgono anche per le funzioni definite da te,<br />
così che possiamo usare una qualsiasi espressione valida come<br />
argomento per Stampa2Volte:</p>
<p>&gt;&gt;&gt; Stampa2Volte(&#8220;Pippo&#8221;*4)<br />
PippoPippoPippoPippo PippoPippoPippoPippo<br />
&gt;&gt;&gt; Stampa2Volte(math.cos(math.pi))<br />
-1.0 -1.0</p>
<p>Come al solito, l&#8217;espressione passata come argomento è valutata prima<br />
dell&#8217;esecuzione della funzione, così nell&#8217;esempio appena proposto<br />
Stampa2Volte ritorna il risultato PippoPippoPippoPippo<br />
PippoPippoPippoPippo invece di &#8220;Pippo&#8221;*4 &#8220;Pippo&#8221;*4.</p>
<p>Una nota per quanto riguarda le stringhe: le stringhe possono essere<br />
racchiuse sia da virgolette &#8220;ABC&#8221; che da apici &#8216;ABC&#8217;. Il tipo di<br />
delimitatore NON usato per delimitare la stringa, l&#8217;apice se si usano<br />
le virgolette, le virgolette se si usa l&#8217;apice, può essere usato<br />
all&#8217;interno della stringa. Ad esempio sono valide le stringhe &#8220;apice &#8216;<br />
nella stringa&#8221; e &#8216;virgoletta &#8221; nella stringa&#8217;, ma non lo sono &#8216;apice &#8216;<br />
nella stringa&#8217; e &#8220;virgoletta &#8221; nella stringa&#8221;, dato che in questo caso<br />
l&#8217;interprete non riesce a stabilire quale sia il fine stringa<br />
desiderato dal programmatore.</p>
<p>Esercizio: scrivi una chiamata a Stampa2Volte che stampa a video la<br />
stringa &#8220;Pippo&#8221;*4 &#8220;Pippo&#8221;*4 così com&#8217;è scritta.</p>
<p>Naturalmente possiamo usare una variabile come argomento di una<br />
funzione:</p>
<p>&gt;&gt;&gt; Messaggio = &#8216;Come va?&#8217;<br />
&gt;&gt;&gt; Stampa2Volte(Messaggio)<br />
Come va? Come va?</p>
<p>Il nome della variabile che passiamo come argomento (Messaggio) non ha<br />
niente a che fare con il nome del parametro nella definizione della<br />
funzione (Valore). Non ha importanza conoscere il nome originale con<br />
cui sono stati identificati i parametri durante la definizione della<br />
funzione.</p>
<p>3.10 Variabili e parametri sono locali</p>
<p>Quando crei una variabile locale all&#8217;interno di una funzione, essa<br />
esiste solo all&#8217;interno della funzione e non puoi usarla all&#8217;esterno.<br />
Per esempio:</p>
<p>def StampaUnite2Volte(Parte1, Parte2):<br />
Unione = Parte1 + Parte2<br />
Stampa2Volte(Unione)</p>
<p>Questa funzione prende due argomenti, li concatena e poi ne stampa il<br />
risultato due volte. Possiamo chiamare la funzione con due stringhe:</p>
<p>&gt;&gt;&gt; Strofa1 = &#8220;Nel mezzo &#8220;<br />
&gt;&gt;&gt; Strofa2 = &#8220;del cammin&#8221;<br />
&gt;&gt;&gt; StampaUnite2Volte(Strofa1, Strofa2)<br />
Nel mezzo del cammin Nel mezzo del cammin</p>
<p>Quando StampaUnite2Volte termina, la variabile Unione è distrutta. Se<br />
proviamo a stamparla quando il flusso di esecuzione si trova<br />
all&#8217;esterno della funzione StampaUnite2Volte otterremo un messaggio<br />
d&#8217;errore:</p>
<p>&gt;&gt;&gt; print Unione<br />
NameError: Unione</p>
<p>Anche i parametri sono locali: al di fuori della funzione<br />
StampaUnite2Volte, non esiste alcuna cosa chiamata messaggio. Se<br />
proverai ad usarla al di fuori della funzione dov&#8217;è definita Python ti<br />
mostrerà ancora una volta un messaggio d&#8217;errore.</p>
<p>3.11 Diagrammi di stack</p>
<p>Per tenere traccia di quali variabili possono essere usate è talvolta<br />
utile disegnare un diagramma di stack. Come i diagrammi di stato, i<br />
diagrammi di stack mostrano il valore di ciascuna variabile e indicano<br />
a quale funzione essa appartenga.</p>
<p>Ogni funzione è rappresentata da un frame, un rettangolo con il nome<br />
della funzione a fianco e la lista dei parametri e delle variabili al<br />
suo interno. Il diagramma di stack nel caso dell&#8217;esempio precedente è:</p>
<p>[i_stack.png]</p>
<p>L&#8217;ordine dello stack mostra chiaramente il flusso di esecuzione.<br />
Possiamo vedere che Stampa2Volte è chiamata da StampaUnite2Volte e che<br />
StampaUnite2Volte è chiamata da __main__. __main__ è un nome speciale<br />
che indica il programma principale che di per sé (non essendo definito<br />
con def come si fa per le funzioni) non ha un nome vero e proprio.<br />
Quando crei una variabile all&#8217;esterno di ogni funzione, essa<br />
appartiene a __main__.</p>
<p>Ogni parametro si riferisce al valore che ha l&#8217;argomento<br />
corrispondente. Così Parte1 ha lo stesso valore di Strofa1, Parte2 ha<br />
lo stesso valore di Strofa2 e Valore lo stesso di Unione.</p>
<p>Se avviene un errore durante la chiamata di una funzione, Python<br />
mostra il nome della funzione, il nome della funzione che l&#8217;ha<br />
chiamata, il nome della funzione che ha chiamato quest&#8217;ultima e così<br />
via, fino a raggiungere il primo livello che è sempre __main__.</p>
<p>Ad esempio se cerchiamo di chiamare Unione dall&#8217;interno di<br />
Stampa2Volte, otteniamo un errore di tipo NameError:</p>
<p>Traceback (innermost last):<br />
File &#8220;test.py&#8221;, line 13, in __main__<br />
StampaUnite2Volte(Parte1, Parte2)<br />
File &#8220;test.py&#8221;, line 5, in StampaUnite2Volte<br />
Stampa2Volte(Unione)<br />
File &#8220;test.py&#8221;, line 9, in Stampa2Volte<br />
print Unione<br />
NameError: Unione</p>
<p>Questa lista temporale delle chiamate delle funzioni è detta traccia.<br />
La traccia ti dice in quale file è avvenuto l&#8217;errore, che riga<br />
all&#8217;interno del file si stava eseguendo in quel momento ed il<br />
riferimento alla funzione che ha causato l&#8217;errore.</p>
<p>Nota che c&#8217;è una notevole somiglianza tra traccia e diagramma di stack<br />
e questa somiglianza non è certamente una coincidenza.</p>
<p>3.12 Funzioni con risultati</p>
<p>Puoi notare come alcune delle funzioni che hai usato, tipo le funzioni<br />
matematiche, restituiscono dei risultati. Altre funzioni, come<br />
UnaRigaVuota, eseguono un&#8217;azione senza ritornare alcun valore. Questa<br />
differenza solleva qualche domanda:<br />
1. Cosa succede se chiami una funzione e non fai niente con il<br />
risultato che viene restituito (per esempio non lo assegni ad una<br />
variabile e non lo usi come parte di una espressione)?<br />
2. Cosa succede se usi una funzione che non produce risultato come<br />
parte di un&#8217;espressione (per esempio UnaRigaVuota() + 7)?<br />
3. Puoi scrivere funzioni che producono risultati, o sei costretto a<br />
limitarti a semplici funzioni tipo UnaRigaVuota e Stampa2Volte che<br />
eseguono azioni in questo caso piuttosto banali?</p>
<p>La risposta alla terza domanda la troveremo al capitolo 5.</p>
<p>Esercizio: trova la risposta alle altre due domande provando i due<br />
casi. Quando non hai chiaro cosa sia legale e cosa non lo sia è<br />
buona regola provare per vedere come reagisce l&#8217;interprete.</p>
<p>3.13 Glossario</p>
<p>Chiamata di funzione<br />
istruzione che esegue una funzione. Consiste di un nome di<br />
funzione seguito da una serie di argomenti racchiuso tra<br />
parentesi.</p>
<p>Argomento<br />
valore fornito alla funzione quando questa viene chiamata. Il<br />
valore è assegnato al corrispondente parametro della funzione.</p>
<p>Valore di ritorno<br />
risultato di una funzione.</p>
<p>Conversione di tipo<br />
istruzione esplicita che prende un valore di un tipo e lo<br />
converte nel corrispondente valore di un altro tipo.</p>
<p>Forzatura di tipo<br />
conversione automatica di tipo secondo le regole di forzatura<br />
di Python.</p>
<p>Modulo<br />
file che contiene una raccolta di funzioni correlate.</p>
<p>Notazione punto<br />
sintassi per la chiamata di una funzione definita in un altro<br />
modulo, specificando il nome del modulo di appartenenza,<br />
seguito da un punto e dal nome della funzione con gli eventuali<br />
argomenti tra parentesi.</p>
<p>Funzione<br />
sequenza di istruzioni identificata da un nome che svolge<br />
qualche operazione utile. Le funzioni possono avere o meno dei<br />
parametri e possono produrre o meno un risultato.</p>
<p>Definizione della funzione<br />
istruzioni che creano una nuova funzione, specificandone il<br />
nome, i parametri e le operazioni che essa deve eseguire.</p>
<p>Flusso di esecuzione<br />
ordine in cui le istruzioni sono interpretate quando il<br />
programma viene eseguito.</p>
<p>Parametro<br />
nome usato all&#8217;interno della funzione per riferirsi al valore<br />
passato come argomento.</p>
<p>Variabile locale<br />
variabile definita all&#8217;interno di una funzione. Una variabile<br />
locale può essere usata unicamente all&#8217;interno della funzione<br />
dov&#8217;è definita.</p>
<p>Diagramma di stack<br />
rappresentazione grafica delle funzioni, delle loro variabili e<br />
dei valori cui esse si riferiscono.</p>
<p>Frame<br />
rettangolo che in un diagramma di stack rappresenta una<br />
chiamata di funzione. Indica le variabili locali e i parametri<br />
della funzione.</p>
<p>Traccia<br />
lista delle funzioni in corso di esecuzione stampata in caso di<br />
errore in esecuzione.</p>
<p>Capitolo 4</p>
<p>Istruzioni condizionali e ricorsione</p>
<p>4.1 L&#8217;operatore modulo</p>
<p>L&#8217;operatore modulo opera sugli interi (e sulle espressioni intere) e<br />
produce il resto della divisione del primo operando diviso per il<br />
secondo. In Python l&#8217;operatore modulo è rappresentato dal segno<br />
percentuale (%). La sintassi è la stessa degli altri operatori<br />
matematici:</p>
<p>&gt;&gt;&gt; Quoziente = 7 / 3<br />
&gt;&gt;&gt; print Quoziente<br />
2<br />
&gt;&gt;&gt; Resto = 7 % 3<br />
&gt;&gt;&gt; print Resto<br />
1</p>
<p>Così 7 diviso 3 dà 2, con il resto di 1.</p>
<p>L&#8217;operatore modulo è molto utile in quanto ti permette di controllare<br />
se un numero è divisibile per un altro: se x % y è 0, allora x è<br />
divisibile per y.</p>
<p>Inoltre può essere usato per estrarre la cifra più a destra di un<br />
numero: x%10 restituisce la cifra più a destra in base 10. Allo stesso<br />
modo x%100 restituisce le ultime due cifre.</p>
<p>4.2 Espressioni booleane</p>
<p>Un&#8217;espressione booleana è un&#8217;espressione che è o vera o falsa. In<br />
Python un&#8217;espressione che è vera ha valore 1, un&#8217;espressione falsa ha<br />
valore 0.</p>
<p>L&#8217;operatore == confronta due valori e produce un risultato di tipo<br />
booleano:</p>
<p>&gt;&gt;&gt; 5 == 5<br />
1<br />
&gt;&gt;&gt; 5 == 6<br />
0</p>
<p>Nella prima riga i due operandi sono uguali, così l&#8217;espressione vale 1<br />
(vero); nella seconda riga 5 e 6 non sono uguali, così otteniamo 0<br />
(falso).</p>
<p>L&#8217;operatore == è uno degli operatori di confronto; gli altri sono:</p>
<p>x != y       # x è diverso da y?<br />
x &gt; y        # x è maggiore di y?<br />
x &lt; y        # x è minore di y?<br />
x &gt;= y       # x è maggiore o uguale a y?<br />
x &lt;= y       # x è minore o uguale a y?</p>
<p>Sebbene queste operazioni ti possano sembrare familiari, i simboli<br />
Python sono diversi da quelli usati comunemente in matematica. \ Un<br />
errore comune è quello di usare il simbolo di uguale (=) invece del<br />
doppio uguale (==): ricorda che = è un operatore di assegnazione e ==<br />
un operatore di confronto. Inoltre in Python non esistono simboli del<br />
tipo =&lt; e =&gt;, ma solo gli equivalenti &lt;= e &gt;=.</p>
<p>4.3 Operatori logici</p>
<p>Ci sono tre operatori logici: and, or e not. Il significato di questi<br />
operatori è simile al loro significato in italiano: per esempio, (x&gt;0)<br />
and (x&lt;10) è vera solo se x è più grande di 0 e meno di 10.</p>
<p>(n%2==0) or (n%3==0) è vera se si verifica almeno una delle due<br />
condizioni e cioè se il numero è divisibile per 2 o per 3.</p>
<p>Infine, l&#8217;operatore not nega il valore di un&#8217;espressione booleana,<br />
trasformando in falsa un&#8217;espressione vera e viceversa. Così se x&gt;y è<br />
vera (x è maggiore di y), not(x&gt;y) è falsa.</p>
<p>A dire il vero gli operatori booleani dovrebbero restituire un valore<br />
vero o falso, ma da questo punto di vista Python (come parte dei<br />
linguaggi di programmazione) non sembra essere troppo fiscale: infatti<br />
ogni valore diverso da zero viene considerato vero e lo zero è<br />
considerato falso.</p>
<p>&gt;&gt;&gt;  x = 5<br />
&gt;&gt;&gt;  x and 1<br />
1<br />
&gt;&gt;&gt;  y = 0<br />
&gt;&gt;&gt;  y and 1<br />
0</p>
<p>In generale, le righe appena viste pur essendo lecite non sono<br />
considerate un buon esempio di programmazione: se vuoi confrontare un<br />
valore con zero è sempre meglio farlo in modo esplicito, con<br />
un&#8217;espressione del tipo</p>
<p>&gt;&gt;&gt;  x != 0</p>
<p>4.4 Esecuzione condizionale</p>
<p>Per poter scrivere programmi di una certa utilità dobbiamo essere<br />
messi in grado di valutare delle condizioni e di far seguire<br />
differenti percorsi al flusso di esecuzione a seconda del risultato<br />
della valutazione. Le istruzioni condizionali ci offrono questa<br />
possibilità. La forma più semplice di istruzione if è la seguente:</p>
<p>if x &gt; 0:<br />
print &#8220;x e&#8217; positivo&#8221;</p>
<p>L&#8217;espressione booleana dopo l&#8217;istruzione if è chiamata condizione.<br />
L&#8217;istruzione indentata che segue i due punti della riga if viene<br />
eseguita solo se la condizione è vera. Se la condizione è falsa non<br />
viene eseguito alcunché.</p>
<p>Come nel caso di altre istruzioni composte, l&#8217;istruzione if è<br />
costituita da un&#8217;intestazione e da un blocco di istruzioni:</p>
<p>INTESTAZIONE:<br />
PRIMA RIGA DI ISTRUZIONI<br />
&#8230;<br />
ULTIMA RIGA DI ISTRUZIONI</p>
<p>L&#8217;intestazione inizia su di una nuova riga e termina con il segno di<br />
due punti. La serie di istruzioni indentate che seguono sono chiamate<br />
blocco di istruzioni. La prima riga di istruzioni non indentata marca<br />
la fine del blocco di istruzioni e non ne fa parte. Un blocco di<br />
istruzioni all&#8217;interno di un&#8217;istruzione composta è anche chiamato<br />
corpo dell&#8217;istruzione.</p>
<p>Non c&#8217;è un limite al numero di istruzioni che possono comparire nel<br />
corpo di un&#8217;istruzione if ma deve sempre essercene almeno una. In<br />
qualche occasione può essere utile avere un corpo vuoto, ad esempio<br />
quando il codice corrispondente non è ancora stato scritto ma si<br />
desidera ugualmente poter provare il programma. In questo caso puoi<br />
usare l&#8217;istruzione pass, che è solo un segnaposto e non fa niente:</p>
<p>if x &gt; 0:<br />
pass</p>
<p>4.5 Esecuzione alternativa</p>
<p>Una seconda forma di istruzione if è l&#8217;esecuzione alternativa, nella<br />
quale ci sono due possibilità di azione e il valore della condizione<br />
determina quale delle due debba essere scelta. La sintassi è:</p>
<p>if x%2 == 0:<br />
print x, &#8220;e&#8217; pari&#8221;<br />
else:<br />
print x, &#8220;e&#8217; dispari&#8221;</p>
<p>Se il resto della divisione intera di x per 2 è zero allora sappiamo<br />
che x è pari e il programma mostra il messaggio corrispondente. Se la<br />
condizione è falsa viene eseguita la serie di istruzioni descritta<br />
dopo la riga else (che in inglese significa &#8220;altrimenti&#8221;).</p>
<p>Le due alternative sono chiamate ramificazioni perché rappresentano<br />
delle ramificazioni nel flusso di esecuzione del programma, e solo una<br />
di esse verrà effettivamente eseguita.</p>
<p>Una nota: se hai bisogno di controllare la parità di un numero (vedere<br />
se il numero è pari o dispari), potresti desiderare di creare una<br />
funzione apposita da poter riutilizzare in seguito:</p>
<p>def StampaParita(x):<br />
if x%2 == 0:<br />
print x, &#8220;e&#8217; pari&#8221;<br />
else:<br />
print x, &#8220;e&#8217; dispari&#8221;</p>
<p>Così per ogni valore intero di x, StampaParita mostra il messaggio<br />
appropriato. Quando chiami questa funzione puoi fornire qualsiasi<br />
espressione intera come argomento.</p>
<p>&gt;&gt;&gt; StampaParita(17)<br />
&gt;&gt;&gt; StampaParita(y+1)</p>
<p>4.6 Condizioni in serie</p>
<p>Talvolta ci sono più di due possibilità per la continuazione del<br />
programma, così possiamo aver bisogno di più di due ramificazioni. Un<br />
modo per esprimere questo caso sono le condizioni in serie:</p>
<p>if x &lt; y:<br />
print x, &#8220;e&#8217; minore di&#8221;, y<br />
elif x &gt; y:<br />
print x, &#8220;e&#8217; maggiore di&#8221;, y<br />
else:<br />
print x, &#8220;e&#8221;, y, &#8220;sono uguali&#8221;</p>
<p>elif è l&#8217;abbreviazione di &#8220;else if&#8221;, che in inglese significa<br />
&#8220;altrimenti se&#8221;. Anche in questo caso solo uno dei rami verrà<br />
eseguito, a seconda del confronto tra x e y. Non c&#8217;è alcun limite al<br />
numero di istruzioni elif ma è eventualmente possibile inserire<br />
un&#8217;unica istruzione else che deve essere l&#8217;ultima dell&#8217;elenco e che<br />
rappresenta l&#8217;azione da eseguire quando nessuna delle condizioni<br />
precedenti è stata soddisfatta. La presenza di un&#8217;istruzione else è<br />
facoltativa.</p>
<p>if scelta == &#8216;A&#8217;:<br />
FunzioneA()<br />
elif scelta == &#8216;B&#8217;:<br />
FunzioneB()<br />
elif scelta == &#8216;C&#8217;:<br />
FunzioneC()<br />
else:<br />
print &#8220;Scelta non valida&#8221;</p>
<p>Le condizioni sono controllate nell&#8217;ordine in cui sono state scritte.<br />
Se la prima è falsa viene provata la seconda e così via. Non appena<br />
una è verificata viene eseguito il ramo corrispondente e l&#8217;intera<br />
istruzione if viene conclusa. In ogni caso, anche se fossero vere<br />
altre condizioni, dopo l&#8217;esecuzione della prima queste vengono<br />
trascurate. Se nessuna condizione è vera ed è presente un else verrà<br />
eseguito il codice corrispondente; se non è presente non verrà<br />
eseguito niente.</p>
<p>Esercizio: scrivi due funzioni basate sugli esempi proposti, una<br />
che confronta x e y (Confronta(x, y)) e l&#8217;altra che controlla se un<br />
valore passato come parametro appartiene ad una lista di valori<br />
validi (ElaboraScelta(scelta)).</p>
<p>4.7 Condizioni annidate</p>
<p>Un&#8217;espressione condizionale può anche essere inserita nel corpo di<br />
un&#8217;altra espressione condizionale: un&#8217;espressione di questo tipo viene<br />
detta &#8220;condizione annidata&#8221;.</p>
<p>if x == y:<br />
print x, &#8220;e&#8221;, y, &#8220;sono uguali&#8221;<br />
else:<br />
if x &lt; y:<br />
print x, &#8220;e&#8217; minore di&#8221;, y<br />
else:<br />
print x, &#8220;e&#8217; maggiore di&#8221;, y</p>
<p>La prima condizione (if x == y) contiene due rami: il primo è scelto<br />
quando x e y sono uguali, il secondo quando sono diversi. All&#8217;interno<br />
del secondo (subito sotto il primo else:) troviamo un&#8217;altra istruzione<br />
if, che a sua volta prevede un&#8217;ulteriore ramificazione. Entrambi i<br />
rami del secondo if sono istruzioni di stampa ma potrebbero contenere<br />
a loro volta ulteriori istruzioni condizionali.</p>
<p>Sebbene l&#8217;indentazione delle istruzioni renda evidente la struttura<br />
dell&#8217;esempio, le istruzioni condizionali annidate in livelli sempre<br />
più profondi diventano sempre più difficili da leggere, quindi è una<br />
buona idea evitarle quando è possibile.</p>
<p>Gli operatori logici permettono un modo molto semplice di semplificare<br />
le espressioni condizionali annidate:</p>
<p>if 0 &lt; x:<br />
if x &lt; 10:<br />
print &#8220;x e&#8217; un numero positivo.&#8221;</p>
<p>L&#8217;istruzione di stampa print è eseguita solo se entrambe le condizioni<br />
(x&gt;0 e x&lt;10) sono verificate contemporaneamente. Possiamo quindi usare<br />
l&#8217;operatore booleano and per combinarle:</p>
<p>if 0&lt;x and x&lt;10:<br />
print &#8220;x e&#8217; un numero positivo.&#8221;</p>
<p>Questo tipo di condizione è così frequente che Python permette di<br />
usare una forma semplificata che ricorda da vicino quella<br />
corrispondente usata in matematica:</p>
<p>if 0 &lt; x &lt; 10:<br />
print &#8220;x e&#8217; un numero positivo.&#8221;</p>
<p>A tutti gli effetti i tre esempi sono equivalenti per quanto riguarda<br />
la semantica (il significato) del programma.</p>
<p>4.8 L&#8217;istruzione return</p>
<p>L&#8217;istruzione return ti permette di terminare l&#8217;esecuzione di una<br />
funzione prima di raggiungerne la fine. Questo può servire quando<br />
viene riconosciuta una condizione d&#8217;errore:</p>
<p>import math<br />
def StampaLogaritmo(x):<br />
if x &lt;= 0:<br />
print &#8220;Inserire solo numeri positivi!&#8221;<br />
return<br />
risultato = math.log(x)<br />
print &#8220;Il logaritmo di&#8221;,x,&#8221;e&#8217;&#8221;, risultato</p>
<p>La funzione StampaLogaritmo accetta un parametro chiamato x. La prima<br />
operazione controlla che esso sia positivo; in caso contrario stampa<br />
un messaggio d&#8217;errore e termina prematuramente la funzione con return.</p>
<p>Ricorda che dovendo usare una funzione del modulo math è necessario<br />
importare il modulo.</p>
<p>4.9 Ricorsione</p>
<p>Abbiamo detto che è perfettamente lecito che una funzione ne chiami<br />
un&#8217;altra e di questo hai avuto modo di vedere parecchi esempi. Abbiamo<br />
invece trascurato di dirti che è anche lecito che una funzione possa<br />
chiamare sé stessa. Può non essere immediatamente ovvio il motivo per<br />
cui questo sia utile, ma questa è una delle cose più interessanti che<br />
un programma possa fare. Per fare un esempio dai un&#8217;occhiata a questa<br />
funzione:</p>
<p>def ContoAllaRovescia(n):<br />
if n == 0:<br />
print &#8220;Partenza!&#8221;<br />
else:<br />
print n<br />
ContoAllaRovescia(n-1)</p>
<p>ContoAllaRovescia si aspetta che il parametro sia un intero positivo.<br />
Se n vale 0, viene stampata la scritta Partenza!. Altrimenti stampa n<br />
e poi chiama la funzione ContoAllaRovescia (cioè sé stessa) con un<br />
argomento che vale n-1.</p>
<p>Cosa succede quando chiamiamo una funzione come questa?</p>
<p>&gt;&gt;&gt; ContoAllaRovescia(3)</p>
<p>L&#8217;esecuzione di ContoAllaRovescia inizia con n=3. Dato che n non è 0,<br />
essa stampa il valore 3, e poi richiama sé stessa&#8230;</p>
<p>L&#8217;esecuzione di ContoAllaRovescia inizia con n=2. Dato che n non è<br />
0, essa stampa il valore 2, poi richiama sé stessa&#8230;</p>
<p>L&#8217;esecuzione di ContoAllaRovescia inizia con n=1. Dato che n non è<br />
0, essa stampa il valore 1, poi richiama sé stessa&#8230;</p>
<p>L&#8217;esecuzione di ContoAllaRovescia inizia con il valore di n=0. Dal<br />
momento che n è 0, essa stampa il testo &#8220;Partenza!&#8221; e poi ritorna.</p>
<p>La funzione ContoAllaRovescia che aveva n=1; e poi ritorna.</p>
<p>La funzione ContoAllaRovescia che aveva n=2; e poi ritorna.</p>
<p>E quindi torna in __main__ (questo è un trucco). Il risultato è<br />
questo:</p>
<p>3<br />
2<br />
1<br />
Partenza!</p>
<p>Come secondo esempio torniamo alle funzioni UnaRigaVuota e<br />
TreRigheVuote:</p>
<p>def UnaRigaVuota():<br />
print<br />
def TreRigheVuote():<br />
UnaRigaVuota()<br />
UnaRigaVuota()<br />
UnaRigaVuota()</p>
<p>Sebbene funzionino correttamente non sarebbero di molto aiuto nel<br />
momento in cui vogliamo stampare due righe vuote o magari 106. Una<br />
alternativa migliore potrebbe essere questa:</p>
<p>def NRigheVuote(n):<br />
if n &gt; 0:<br />
print<br />
NRigheVuote(n-1)</p>
<p>Questo programma è simile a ContoAllaRovescia: finché n è maggiore di<br />
0, la funzione stampa una riga vuota e poi chiama sé stessa con un<br />
argomento n diminuito di 1.</p>
<p>Il processo di una funzione che richiama sé stessa è detto ricorsione,<br />
e la funzione è definita ricorsiva.</p>
<p>4.10 Diagrammi di stack per funzioni ricorsive</p>
<p>Nella sezione 3.11, abbiamo usato un diagramma di stack per<br />
rappresentare lo stato di un programma durante una chiamata di<br />
funzione. Lo stesso tipo di diagramma può aiutare a capire come lavora<br />
una funzione ricorsiva.</p>
<p>Ogni volta che una funzione viene chiamata, Python crea un nuovo frame<br />
della funzione, contenente le variabili locali definite all&#8217;interno<br />
della funzione ed i suoi parametri. Nel caso di una funzione ricorsiva<br />
possono esserci più frame riguardanti una stessa funzione allo stesso<br />
tempo.</p>
<p>La figura mostra il diagramma dello stack della funzione<br />
ContoAllaRovescia chiamata con n=3:</p>
<p>[i_stack2.png]</p>
<p>Come al solito il livello superiore dello stack è il frame per<br />
__main__. Questo frame è vuoto perché in questo caso non abbiamo<br />
creato alcuna variabile locale e non abbiamo passato alcun parametro.</p>
<p>I quattro frame di ContoAllaRovescia hanno valori diversi per il<br />
parametro n. Il livello inferiore dello stack, quando n=0, è chiamato<br />
lo stato di base. Esso non effettua ulteriori chiamate ricorsive, così<br />
non ci sono ulteriori frame.</p>
<p>Esercizio: disegna il diagramma dello stack per la funzione<br />
NRigheVuote chiamata con n=4.</p>
<p>4.11 Ricorsione infinita</p>
<p>Se una ricorsione non raggiunge mai il suo stato di base la chiamata<br />
alla funzione viene eseguita all&#8217;infinito ed in teoria il programma<br />
non giunge mai alla fine. Questa situazione è conosciuta come<br />
ricorsione infinita e non è generalmente considerata una buona cosa.<br />
Questo è un programma minimo che genera una ricorsione infinita:</p>
<p>def Ricorsione():<br />
Ricorsione()</p>
<p>Nella maggior parte degli ambienti un programma con una ricorsione<br />
infinita non viene eseguito senza fine, dato che ogni chiamata ad una<br />
funzione impegna un po&#8217; di memoria del computer e questa memoria prima<br />
o poi finisce. Python stampa un messaggio d&#8217;errore quando è stato<br />
raggiunto il massimo livello di ricorsione possibile:</p>
<p>File &#8220;&lt;stdin&gt;&#8221;, line 2, in Ricorsione<br />
&#8230;<br />
File &#8220;&lt;stdin&gt;&#8221;, line 2, in Ricorsione<br />
RuntimeError: Maximum recursion depth exceeded</p>
<p>Questa traccia è un po&#8217; più lunga di quella che abbiamo visto nel<br />
capitolo precedente. Quando è capitato l&#8217;errore c&#8217;erano moltissime<br />
ricorsioni nello stack.</p>
<p>Esercizio: scrivi una funzione con ricorsione infinita ed eseguila<br />
nell&#8217;interprete Python.</p>
<p>4.12 Inserimento da tastiera</p>
<p>I programmi che abbiamo scritto finora sono piuttosto banali, nel<br />
senso che non accettano inserimenti di dati da parte dell&#8217;operatore,<br />
limitandosi a eseguire sempre le stesse operazioni.</p>
<p>Python fornisce un insieme di funzioni predefinite che permettono di<br />
inserire dati da tastiera. La più semplice di esse è raw_input. Quando<br />
questa funzione è chiamata il programma si ferma ed attende che<br />
l&#8217;operatore inserisca qualcosa, confermando poi l&#8217;inserimento con<br />
Invio (o Enter). A quel punto il programma riprende e raw_input<br />
ritorna ciò che l&#8217;operatore ha inserito sotto forma di stringa:</p>
<p>&gt;&gt;&gt; Inserimento = raw_input ()<br />
Testo inserito<br />
&gt;&gt;&gt; print Inserimento<br />
Testo inserito</p>
<p>Prima di chiamare raw_input è una buona idea stampare un messaggio che<br />
avvisa l&#8217;operatore di ciò che deve essere inserito. Questo messaggio è<br />
chiamato prompt. L&#8217;operazione è così comune che il messaggio di prompt<br />
può essere passato come argomento a raw_input:</p>
<p>&gt;&gt;&gt; Nome = raw_input (&#8220;Qual e&#8217; il tuo nome? &#8220;)<br />
Qual e&#8217; il tuo nome? Arturo<br />
&gt;&gt;&gt; print Nome<br />
Arturo</p>
<p>Se il valore da inserire è un intero possiamo usare la funzione input:</p>
<p>Prompt = &#8220;A che velocita&#8217;viaggia il treno?\n&#8221;<br />
Velocita = input(Prompt)</p>
<p>Se l&#8217;operatore inserisce una serie di cifre questa è convertita in un<br />
intero ed assegnata a Velocita. Sfortunatamente se i caratteri<br />
inseriti dall&#8217;operatore non rappresentano un numero, il programma<br />
stampa un messaggio d&#8217;errore e si blocca:</p>
<p>&gt;&gt;&gt; Velocita = input (Prompt)<br />
A che velocita&#8217;viaggia il treno?<br />
ottanta all&#8217;ora<br />
SyntaxError: invalid syntax</p>
<p>Per evitare questo tipo di errori è generalmente meglio usare la<br />
funzione \linebreak raw_input per ottenere una stringa di caratteri e<br />
poi usare le funzioni di conversione per ottenere gli altri tipi.</p>
<p>4.13 Glossario</p>
<p>Operatore modulo<br />
operatore matematico denotato con il segno di percentuale (%)<br />
che restituisce il resto della divisione tra due operandi<br />
interi.</p>
<p>Espressione booleana<br />
espressione che è o vera o falsa.</p>
<p>Operatore di confronto<br />
uno degli operatori che confrontano due valori: ==, !=, &gt;, &lt;,<br />
&gt;= e &lt;=.</p>
<p>Operatore logico<br />
uno degli operatori che combina le espressioni booleane: and,<br />
or e not.</p>
<p>Istruzione condizionale<br />
istruzione che controlla il flusso di esecuzione del programma<br />
a seconda del verificarsi di certe condizioni.</p>
<p>Condizione<br />
espressione booleana in una istruzione condizionale che<br />
determina quale ramificazione debba essere seguita dal flusso<br />
di esecuzione.</p>
<p>Istruzione composta<br />
istruzione che consiste di un&#8217;intestazione terminante con i due<br />
punti (:) e di un corpo composto di una o più istruzioni<br />
indentate rispetto all&#8217;intestazione.</p>
<p>Blocco<br />
gruppo di istruzioni consecutive con la stessa indentazione.</p>
<p>Corpo<br />
blocco che segue l&#8217;intestazione in un&#8217;istruzione composta.</p>
<p>Annidamento<br />
particolare struttura di programma interna ad un&#8217;altra, come<br />
nel caso di una istruzione condizionale inserita all&#8217;interno di<br />
un&#8217;altra istruzione condizionale.</p>
<p>Ricorsione<br />
richiamo di una funzione che è già in esecuzione.</p>
<p>Stato di base<br />
ramificazione di un&#8217;istruzione condizionale posta in una<br />
funzione ricorsiva e che non esegue alcuna chiamata ricorsiva.</p>
<p>Ricorsione infinita<br />
funzione che chiama sé stessa ricorsivamente senza mai<br />
raggiungere lo stato di base. L&#8217;occupazione progressiva della<br />
memoria che avviene ad ogni successiva chiamata causa ad un<br />
certo punto un errore in esecuzione.</p>
<p>Prompt<br />
suggerimento visivo che specifica il tipo di dati atteso come<br />
inserimento da tastiera.</p>
<p>Capitolo 5</p>
<p>Funzioni produttive</p>
<p>5.1 Valori di ritorno</p>
<p>Alcune delle funzioni predefinite che abbiamo usato finora producono<br />
dei risultati: la chiamata della funzione con un particolare argomento<br />
genera un nuovo valore che viene in seguito assegnato ad una variabile<br />
o viene usato come parte di un&#8217;espressione.</p>
<p>e = math.exp(1.0)<br />
Altezza = Raggio * math.sin(Angolo)</p>
<p>Nessuna delle funzioni che abbiamo scritto sino a questo momento ha<br />
ritornato un valore.</p>
<p>In questo capitolo scriveremo funzioni che ritornano un valore e che<br />
chiamiamo funzioni produttive. \ Il primo esempio è AreaDelCerchio che<br />
ritorna l&#8217;area di un cerchio per un dato raggio:</p>
<p>import math<br />
def AreaDelCerchio(Raggio):<br />
temp = math.pi * Raggio**2<br />
return temp</p>
<p>Abbiamo già visto l&#8217;istruzione return, ma nel caso di una funzione<br />
produttiva questa istruzione prevede un valore di ritorno. Questa<br />
istruzione significa: &#8220;ritorna immediatamente da questa funzione a<br />
quella chiamante e usa questa espressione come valore di ritorno&#8221;.<br />
L&#8217;espressione che rappresenta il valore di ritorno può essere anche<br />
complessa, così che l&#8217;esempio visto in precedenza può essere riscritto<br />
in modo più conciso:</p>
<p>def AreaDelCerchio(raggio):<br />
return math.pi * Raggio**2</p>
<p>D&#8217;altra parte una variabile temporanea come temp spesso rende il<br />
programma più leggibile e ne semplifica il debug.</p>
<p>Talvolta è necessario prevedere delle istruzioni di ritorno multiple,<br />
ciascuna all&#8217;interno di una ramificazione di un&#8217;istruzione<br />
condizionale:</p>
<p>def ValoreAssoluto(x):<br />
if x &lt; 0:<br />
return -x<br />
else:<br />
return x</p>
<p>Dato che queste istruzioni return sono in rami diversi della<br />
condizione solo una di esse verrà effettivamente eseguita.</p>
<p>Il codice che è posto dopo un&#8217;istruzione return, o in ognuno dei posti<br />
dove non può essere raggiunto dal flusso di esecuzione, è denominato<br />
codice morto.</p>
<p>In una funzione produttiva è una buona idea assicurarci che ognuna<br />
delle ramificazioni possibili porti ad un&#8217;uscita dalla funzione con<br />
un&#8217;istruzione di return. Per esempio:</p>
<p>def ValoreAssoluto(x):<br />
if x &lt; 0:<br />
return -x<br />
elif x &gt; 0:<br />
return x</p>
<p>Questo programma non è corretto in quanto non è prevista un&#8217;uscita con<br />
return nel caso x sia 0. In questo caso il valore di ritorno è un<br />
valore speciale chiamato None:</p>
<p>&gt;&gt;&gt; print ValoreAssoluto(0)<br />
None</p>
<p>Esercizio: scrivi una funzione Confronto che ritorna 1 se x&gt;y, 0 se<br />
x==y e -1 se x&lt;y.</p>
<p>5.2 Sviluppo del programma</p>
<p>A questo punto sei già in grado di leggere funzioni complete e capire<br />
cosa fanno. Inoltre se hai fatto gli esercizi che ti ho suggerito hai<br />
già scritto qualche piccola funzione. A mano a mano che scriverai<br />
funzioni di complessità maggiore comincerai ad incontrare qualche<br />
difficoltà soprattutto con gli errori di semantica e di esecuzione.</p>
<p>Per fare fronte a questi programmi via via più complessi ti suggerisco<br />
una tecnica chiamata sviluppo incrementale. Lo scopo dello sviluppo<br />
incrementale è evitare lunghe sessioni di debug, aggiungendo e<br />
testando continuamente piccole parti di codice alla volta.</p>
<p>Come programma di esempio supponiamo che tu voglia trovare la distanza<br />
tra due punti conoscendone le coordinate (x[1], y[1]) e (x[2], y[2]).<br />
Con il teorema di Pitagora sappiamo che la distanza è</p>
<p>distanza =</p>
<p>SQRT<br />
_________________________________________________________________</p>
<p>(x[2] &#8211; x[1])2 + (y[2] &#8211; y[1])2</p>
<p>La prima cosa da considerare è l&#8217;aspetto che la funzione<br />
DistanzaTraDuePunti deve avere in Python chiarendo subito quali siano<br />
i parametri che si vogliono passare alla funzione e quale sia il<br />
risultato da ottenere: quest&#8217;ultimo può essere tanto un valore<br />
numerico da utilizzare all&#8217;interno di una espressione o da assegnare<br />
ad una variabile, tanto una stampa a video o altro.</p>
<p>Nel nostro caso è chiaro che le coordinate dei due punti sono i nostri<br />
parametri, e la distanza calcolata un valore numerico in virgola<br />
mobile.</p>
<p>Possiamo così delineare un primo abbozzo di funzione:</p>
<p>def DistanzaTraDuePunti(x1, y1, x2, y2):<br />
return 0.0</p>
<p>Ovviamente questa prima versione non calcola distanze, in quanto<br />
ritorna sempre 0. Ma è già una funzione sintatticamente corretta e può<br />
essere eseguita: è il caso di eseguire questo primo test prima di<br />
procedere a renderla più complessa.</p>
<p>Per testare la nuova funzione proviamo a chiamarla con dei semplici<br />
valori:</p>
<p>&gt;&gt;&gt; DistanzaTraDuePunti(1, 2, 4, 6)<br />
0.0</p>
<p>Abbiamo scelto questi valori così che la loro distanza orizzontale è 3<br />
e quella verticale è 4. Con il teorema di Pitagora è facile vedere che<br />
il valore atteso è pari a 5 (5 è la lunghezza dell&#8217;ipotenusa di un<br />
triangolo rettangolo i cui cateti sono 3 e 4). Quando testiamo una<br />
funzione è sempre utile conoscere il risultato di qualche caso<br />
particolare per verificare se stiamo procedendo sulla strada giusta.</p>
<p>A questo punto abbiamo verificato che la funzione è sintatticamente<br />
corretta e possiamo così cominciare ad aggiungere linee di codice.<br />
Dopo ogni aggiunta la testiamo ancora per vedere che non ci siano<br />
problemi evidenti. Dovesse presentarsi un problema almeno sapremo che<br />
questo è dovuto alle linee inserite dopo l&#8217;ultimo test che ha avuto<br />
successo.</p>
<p>Un passo logico per risolvere il nostro problema è quello di trovare<br />
le differenze x[2]-x[1] e y[2]-y[1]. Memorizzeremo queste differenze<br />
in variabili temporanee chiamate dx e dy e le stamperemo a video.</p>
<p>def DistanzaTraDuePunti(x1, y1, x2, y2):<br />
dx = x2 &#8211; x1<br />
dy = y2 &#8211; y1<br />
print &#8220;dx vale&#8221;, dx<br />
print &#8220;dy vale&#8221;, dy<br />
return 0.0</p>
<p>Se la funzione lavora correttamente, quando la richiamiamo con i<br />
valori di prima dovremmo trovare che dx e dy valgono rispettivamente 3<br />
e 4. Se i risultati coincidono siamo sicuri che la funzione carica<br />
correttamente i parametri ed elabora altrettanto correttamente le<br />
prime righe. Nel caso il risultato non fosse quello atteso, dovremo<br />
concentrarci solo sulle poche righe aggiunte dall&#8217;ultimo test e non<br />
sull&#8217;intera funzione.</p>
<p>Proseguiamo con il calcolo della somma dei quadrati di dx e dy:</p>
<p>def DistanzaTraDuePunti(x1, y1, x2, y2):<br />
dx = x2 &#8211; x1<br />
dy = y2 &#8211; y1<br />
DistQuadrata = dx**2 + dy**2<br />
print &#8220;DistQuadrata vale &#8220;, DistQuadrata<br />
return 0.0</p>
<p>Nota come i due print che avevamo usato prima siano stati rimossi in<br />
quanto ci sono serviti per testare quella parte di programma ma adesso<br />
sarebbero inutili. Un codice come questo è chiamato codice temporaneo<br />
perché è utile durante la costruzione del programma ma alla fine deve<br />
essere rimosso in quanto non fa parte delle funzioni richieste alla<br />
versione definitiva della nostra funzione.</p>
<p>Ancora una volta eseguiamo il programma. Se tutto funziona dovremmo<br />
trovare un risultato pari a 25 (la somma dei quadrati costruiti sui<br />
cateti di lato 3 e 4).</p>
<p>Non ci resta che calcolare la radice quadrata. Se abbiamo importato il<br />
modulo matematico math possiamo usare la funzione sqrt per elaborare<br />
il risultato:</p>
<p>def DistanzaTraDuePunti(x1, y1, x2, y2):<br />
dx = x2 &#8211; x1<br />
dy = y2 &#8211; y1<br />
DistQuadrata = dx**2 + dy**2<br />
Risultato = math.sqrt(DistQuadrata)<br />
return Risultato</p>
<p>Stavolta se tutto va bene abbiamo finito. Potresti anche stampare il<br />
valore di Risultato prima di uscire dalla funzione con return.</p>
<p>Soprattutto all&#8217;inizio non dovresti mai aggiungere più di poche righe<br />
di programma alla volta. Man mano che la tua esperienza di<br />
programmatore cresce ti troverai a scrivere pezzi di codice sempre più<br />
grandi. In ogni caso nelle prime fasi il processo di sviluppo<br />
incrementale ti farà risparmiare un bel po&#8217; di tempo.</p>
<p>Ecco gli aspetti chiave del processo di sviluppo incrementale:<br />
1. Inizia con un programma funzionante e fai piccoli cambiamenti:<br />
questo ti permetterà di scoprire facilmente dove siano localizzati<br />
gli eventuali errori.<br />
2. Usa variabili temporanee per memorizzare i valori intermedi, così<br />
da poterli stampare e controllare.<br />
3. Quando il programma funziona perfettamente rimuovi le istruzioni<br />
temporanee e consolida le istruzioni in espressioni composite,<br />
sempre che questo non renda il programma difficile da leggere.</p>
<p>Esercizio: usa lo sviluppo incrementale per scrivere una funzione<br />
chiamata Ipotenusa che ritorna la lunghezza dell&#8217;ipotenusa di un<br />
triangolo rettangolo, passando i due cateti come parametri.<br />
Registra ogni passo del processo di sviluppo man mano che esso<br />
procede.</p>
<p>5.3 Composizione</p>
<p>È possibile chiamare una funzione dall&#8217;interno di un&#8217;altra funzione.<br />
Questa capacità è chiamata composizione.</p>
<p>Scriveremo ora una funzione che accetta come parametri il centro ed un<br />
punto sulla circonferenza di un cerchio e calcola l&#8217;area del cerchio.</p>
<p>Il centro del cerchio è memorizzato nelle variabili xc e yc e le<br />
coordinate del punto sulla circonferenza in xp e yp. Il primo passo è<br />
trovare il raggio del cerchio, che è equivalente alla distanza tra i<br />
due punti: la funzione DistanzaTraDuePunti che abbiamo appena scritto<br />
servirà proprio a questo:</p>
<p>Raggio = DistanzaTraDuePunti(xc, yc, xp, yp)</p>
<p>Il secondo passo è trovare l&#8217;area del cerchio e restituirla:</p>
<p>Risultato = AreaDelCerchio(Raggio)<br />
return Risultato</p>
<p>Assemblando il tutto in una funzione abbiamo:</p>
<p>def AreaDelCerchio2(xc, yc, xp, yp):<br />
Raggio = DistanzaTraDuePunti(xc, yc, xp, yp)<br />
Risultato = AreaDelCerchio(Raggio)<br />
return Risultato</p>
<p>Abbiamo chiamato questa funzione AreaDelCerchio2 per distinguerla<br />
dalla funzione AreaDelCerchio definita in precedenza. Non possono<br />
esistere due funzioni con lo stesso nome all&#8217;interno di un modulo.</p>
<p>Le variabili temporanee Raggio e Risultato sono utili per lo sviluppo<br />
e il debug ma quando il programma funziona possiamo riscrivere la<br />
funzione in modo più conciso componendo le chiamate alle funzioni:</p>
<p>def AreaDelCerchio2(xc, yc, xp, yp):<br />
return AreaDelCerchio(DistanzaTraDuePunti(xc, yc, xp, yp))</p>
<p>Esercizio: scrivi una funzione Pendenza(x1, y1, x2, y2) che ritorna<br />
il valore della pendenza della retta passante per i punti (x1, y1)<br />
e (x2, y2). Poi usa questa funzione in una seconda funzione<br />
chiamata IntercettaY(x1, y1, x2, y2) che ritorna il valore delle<br />
ordinate quando la retta determinata dagli stessi punti ha X uguale<br />
a zero.</p>
<p>5.4 Funzioni booleane</p>
<p>Le funzioni possono anche ritornare valori booleani (vero o falso) e<br />
questo è molto utile per mascherare al loro interno test anche<br />
complicati.</p>
<p>def Divisibile(x, y):<br />
if x % y == 0:<br />
return 1       # x e&#8217; divisibile per y: ritorna vero<br />
else:<br />
return 0       # x non e&#8217; divisibile per y: ritorna falso</p>
<p>Il nome di questa funzione è Divisibile (sarebbe comodo poterla<br />
chiamare E`Divisibile ma purtroppo gli accenti e le lettere accentate<br />
non sono caratteri validi nei nomi di variabili e di funzioni). È<br />
consuetudine assegnare dei nomi che sembrano domande con risposta<br />
si/no alle funzioni booleane: Divisibile? Bisestile? NumeroPari? Nel<br />
nostro caso Divisibile ritorna 1 o 0 per indicare se x è divisibile o<br />
meno per y. Vale il discorso già fatto in precedenza: 0 indica falso,<br />
qualsiasi valore diverso da 0 vero.</p>
<p>Possiamo rendere le funzioni ancora più concise avvantaggiandoci del<br />
fatto che la condizione nell&#8217;istruzione if è anch&#8217;essa di tipo<br />
booleano:</p>
<p>def Divisibile(x, y):<br />
return x%y == 0</p>
<p>Questa sessione mostra la nuova funzione in azione:</p>
<p>&gt;&gt;&gt;   Divisibile(6, 4)<br />
0<br />
&gt;&gt;&gt;   Divisibile(6, 3)<br />
1</p>
<p>Le funzioni booleane sono spesso usate in istruzioni condizionali:</p>
<p>if Divisibile(x, y):<br />
print x, &#8220;e&#8217; divisibile per&#8221;, y<br />
else:<br />
print x, &#8220;non e&#8217; divisibile per&#8221;, y</p>
<p>Esercizio: scrivi una funzione CompresoTra(x,y,z) che ritorna 1 se<br />
yle xle z, altrimenti ritorna 0.</p>
<p>5.5 Ancora ricorsione</p>
<p>Finora hai imparato una piccola parte di Python, ma potrebbe<br />
interessarti sapere che questo sottoinsieme è già di per sé un<br />
linguaggio di programmazione completo: questo significa che con gli<br />
elementi che già conosci puoi esprimere qualsiasi tipo di<br />
elaborazione. Aggiungendo solo qualche comando di controllo per<br />
gestire tastiera, mouse, dischi, ecc. qualsiasi tipo di programma<br />
potrebbe già essere riscritto usando solo le caratteristiche del<br />
linguaggio che hai imparato finora.</p>
<p>La prova di questa affermazione è un esercizio non banale e fu<br />
dimostrata per la prima volta da Alan Turing, uno dei primi teorici<br />
dell&#8217;informatica (qualcuno potrebbe obiettare che in realtà era un<br />
matematico, ma molti degli informatici di allora erano dei<br />
matematici). Di conseguenza la dimostrazione è chiamata Teorema di<br />
Turing.</p>
<p>Per darti un&#8217;idea di che cosa puoi fare con ciò che hai imparato<br />
finora proveremo a valutare un po&#8217; di funzioni matematiche definite<br />
ricorsivamente. Una funzione ricorsiva è simile ad una definizione<br />
circolare, nel senso che la sua definizione contiene un riferimento<br />
alla cosa che viene definita. Una definizione circolare non è poi<br />
troppo utile, tanto che se ne trovassi una consultando un vocabolario<br />
ciò ti darebbe fastidio:</p>
<p>zurloso<br />
aggettivo usato per descrivere qualcosa di zurloso.</p>
<p>D&#8217;altra parte se guardi la definizione della funzione matematica<br />
fattoriale (indicata da un numero seguito da un punto esclamativo) ti<br />
accorgi che la somiglianza è notevole:</p>
<p>0! = 1<br />
n! = n (n-1)!</p>
<p>Questa definizione stabilisce che il fattoriale di 0 è 1 e che il<br />
fattoriale di ogni altro valore n è n moltiplicato per il fattoriale<br />
di n-1.</p>
<p>Così 3! è 3 moltiplicato 2!, che a sua volta è 2 moltiplicato 1!, che<br />
a sua volta è 1 moltiplicato 0!, che per definizione è 1. Mettendo<br />
tutto assieme 3! è uguale a 3 per 2 per 1, e cioè pari a 6.</p>
<p>Se scrivi una definizione ricorsiva, solitamente puoi anche scrivere<br />
un programma Python per valutarla. Il primo passo è quello di decidere<br />
quali siano i parametri da passare alla funzione.</p>
<p>Fattoriale ha un solo parametro:</p>
<p>def Fattoriale(n):</p>
<p>Se l&#8217;argomento è 0 dobbiamo ritornare il valore 1:</p>
<p>def Fattoriale(n):<br />
if n == 0:<br />
return 1</p>
<p>Altrimenti, e questa è la parte interessante, dobbiamo fare una<br />
chiamata ricorsiva per trovare il fattoriale di n-1 e poi moltiplicare<br />
questo valore per n:</p>
<p>def Fattoriale(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
FattorialeMenoUno = Fattoriale(n-1)<br />
Risultato = n * FattorialeMenoUno<br />
return Risultato</p>
<p>Il flusso di esecuzione del programma è simile a quello di<br />
ContoAllaRovescia nella sezione 4.9. Se chiamiamo Fattoriale con il<br />
valore 3:</p>
<p>Dato che 3 non è 0, seguiamo il ramo else e calcoliamo il fattoriale<br />
di n=3-1=2&#8230;</p>
<p>Dato che 2 non è 0, seguiamo il ramo else e calcoliamo il<br />
fattoriale di n=2-1=1&#8230;</p>
<p>Dato che 1 non è 0, seguiamo il ramo else e calcoliamo il<br />
fattoriale di n=1-1=0&#8230;</p>
<p>Dato che 0 è 0 ritorniamo 1 senza effettuare ulteriori chiamate<br />
ricorsive.</p>
<p>Il valore di ritorno (1) è moltiplicato per n (1) e il risultato<br />
(1) restituito alla funzione chiamante.</p>
<p>Il valore di ritorno (1) è moltiplicato per n (2) e il risultato<br />
(2) restituito alla funzione chiamante.</p>
<p>Il valore di ritorno (2) è moltiplicato per n (3) e il risultato (6)<br />
diventa il valore di ritorno della funzione che ha fatto partire<br />
l&#8217;intero processo.</p>
<p>Questo è il diagramma di stack per l&#8217;intera serie di funzioni:</p>
<p>[i_stack3.png]</p>
<p>I valori di ritorno sono mostrati mentre vengono passati di chiamata<br />
in chiamata verso l&#8217;alto. In ogni frame il valore di ritorno è<br />
Risultato, che è il prodotto di n per FattorialeMenoUno.</p>
<p>Nota come nell&#8217;ultimo frame le variabili locali FattorialeMenoUno e<br />
Risultato non esistono, perché il ramo che le crea non viene eseguito.</p>
<p>5.6 Accettare con fiducia</p>
<p>Seguire il flusso di esecuzione è un modo di leggere i programmi, ma<br />
può dimostrarsi piuttosto difficile da seguire man mano che le<br />
dimensioni del codice aumentano. Un modo alternativo è ciò che<br />
potremmo chiamare accettazione con fiducia: quando arrivi ad una<br />
chiamata di funzione invece di seguire il flusso di esecuzione parti<br />
dal presupposto che la funzione chiamata si comporti correttamente e<br />
che ritorni il valore che ci si attende.</p>
<p>In ogni modo stai già praticando questa accettazione con fiducia<br />
quando usi le funzioni predefinite: quando chiami math.cos o math.exp<br />
non vai a controllare l&#8217;implementazione delle funzioni, assumendo che<br />
chi le ha scritte fosse un buon programmatore e che le funzioni siano<br />
corrette.</p>
<p>Lo stesso si può dire per le funzioni che scrivi tu stesso: quando<br />
abbiamo scritto la funzione Divisibile, che controlla se un numero è<br />
divisibile per un altro, e abbiamo verificato che la funzione è<br />
corretta controllando il codice possiamo usarla senza doverla<br />
ricontrollare ancora.</p>
<p>Quando hai chiamate ricorsive invece di seguire il flusso di programma<br />
puoi partire dal presupposto che la chiamata ricorsiva funzioni<br />
(producendo il risultato corretto) chiedendoti in seguito: &#8220;Supponendo<br />
che si riesca a trovare il fattoriale di n-1, posso calcolare il<br />
fattoriale di n?&#8221; In questo caso è chiaro che puoi farlo<br />
moltiplicandolo per n. È certamente strano partire dal presupposto che<br />
una funzione lavori correttamente quando non è ancora stata finita,<br />
non è vero?</p>
<p>5.7 Un esempio ulteriore</p>
<p>Nell&#8217;esempio precedente abbiamo usato delle variabili temporanee per<br />
identificare meglio i singoli passaggi e per facilitare la lettura del<br />
codice, ma avremmo potuto risparmiare qualche riga:</p>
<p>def Fattoriale(n):<br />
if n == 0:<br />
return 1<br />
else:<br />
return n * Fattoriale(n-1)</p>
<p>D&#8217;ora in poi in questo libro tenderemo ad usare la forma più concisa,<br />
ma ti consiglio di usare quella più esplicita finché non avrai un po&#8217;<br />
di esperienza nello sviluppo del codice.</p>
<p>Dopo il Fattoriale, l&#8217;esempio di funzione ricorsiva più comune è la<br />
funzione Fibonacci che ha questa definizione:</p>
<p>fibonacci(0) = 1<br />
fibonacci(1) = 1<br />
fibonacci(n) = fibonacci(n-1) + fibonacci(n-2);</p>
<p>Tradotta in Python:</p>
<p>def Fibonacci (n):<br />
if n == 0 or n == 1:<br />
return 1<br />
else:<br />
return Fibonacci(n-1) + Fibonacci(n-2)</p>
<p>Con una funzione del genere il flusso di esecuzione diventa<br />
praticamente impossibile da seguire anche per piccoli valori di n. In<br />
questo caso ed in casi analoghi vale la pena di adottare<br />
l&#8217;accettazione con fiducia partendo dal presupposto che le due<br />
chiamate ricorsive funzionino correttamente e che quindi la somma dei<br />
loro valori di ritorno sia corretta.</p>
<p>5.8 Controllo dei tipi</p>
<p>Cosa succede se chiamiamo Fattoriale e passiamo 1.5 come argomento?</p>
<p>&gt;&gt;&gt; Fattoriale(1.5)<br />
RuntimeError: Maximum recursion depth exceeded</p>
<p>A prima vista sembra una ricorsione infinita. Ma come può accadere?<br />
C&#8217;è un caso base (quando n==0) che dovrebbe fermare la ricorsione, ma<br />
il problema è che non tutti i possibili valori di n verificano la<br />
condizione di fermata prevista dal caso base.</p>
<p>Se proviamo a seguire il flusso di esecuzione, alla prima chiamata il<br />
valore di n passa a 0.5. Alla successiva diventa -0.5. Da lì in poi,<br />
sottraendo 1 di volta in volta, il valore passato alla funzione è<br />
sempre più piccolo ma non sarà mai lo 0 che ci aspettiamo nel caso<br />
base.</p>
<p>Abbiamo due scelte: possiamo generalizzare la funzione Fattoriale per<br />
farla lavorare anche nel caso di numeri in virgola mobile, o possiamo<br />
far controllare alla funzione dopo la sua chiamata se il parametro<br />
passato è del tipo corretto. La prima possibilità è chiamata in<br />
matematica funzione gamma (il fattoriale definito nei numeri reali) ed<br />
è decisamente al di là degli scopi di questo libro, così sceglieremo<br />
la seconda alternativa.</p>
<p>Possiamo usare type per controllare se il parametro è di tipo intero.<br />
Già che ci siamo mettiamo anche un controllo per essere sicuri che il<br />
numero sia positivo:</p>
<p>def Fattoriale(n):<br />
if type(n) != type(1):<br />
print &#8220;Il fattoriale è definito solo per i valori interi.&#8221;<br />
return -1<br />
elif n &lt; 0:<br />
print &#8220;Il fattoriale è definito solo per interi positivi.&#8221;<br />
return -1<br />
elif n == 0:<br />
return 1<br />
else:<br />
return n * Fattoriale(n-1)</p>
<p>Nel primo confronto abbiamo confrontato il &#8220;tipo di n&#8221; con il &#8220;tipo<br />
del numero intero 1&#8243; per vedere se n è intero.</p>
<p>Ora abbiamo tre casi: il primo blocca i valori non interi; il secondo<br />
gli interi negativi ed il terzo calcola il fattoriale di un numero che<br />
a questo punto è sicuramente un intero positivo o uguale a zero. Nei<br />
primi due casi dato che il calcolo non è possibile viene stampato un<br />
messaggio d&#8217;errore e la funzione ritorna il valore -1, per indicare<br />
che qualcosa non ha funzionato:</p>
<p>&gt;&gt;&gt; Fattoriale(&#8220;AAA&#8221;)<br />
Il fattoriale è definito solo per i valori interi.<br />
-1<br />
&gt;&gt;&gt; Fattoriale (-2)<br />
Il fattoriale è definito solo per gli interi positivi.<br />
-1</p>
<p>Se il flusso di programma passa attraverso entrambi i controlli siamo<br />
certi che n è un intero positivo e sappiamo che la ricorsione avrà<br />
termine.</p>
<p>Questo programma mostra il funzionamento di una condizione di guardia.<br />
I primi due controlli agiscono da &#8220;guardiani&#8221;, proteggendo il codice<br />
che segue da circostanze che potrebbero causare errori. Le condizioni<br />
di guardia rendono possibile provare la correttezza del codice in modo<br />
estremamente semplice ed affidabile.</p>
<p>5.9 Glossario</p>
<p>Funzione produttiva<br />
funzione che produce un valore.</p>
<p>Valore di ritorno<br />
valore restituito da una funzione.</p>
<p>Variabile temporanea<br />
variabile usata per memorizzare un risultato intermedio durante<br />
un calcolo complesso.</p>
<p>Codice morto<br />
parte di un programma che non può mai essere eseguita, spesso<br />
perché compare dopo un&#8217;istruzione di return.</p>
<p>Valore None<br />
valore speciale ritornato da una funzione che non ha<br />
un&#8217;istruzione return, o se l&#8217;istruzione return non specifica un<br />
valore di ritorno.</p>
<p>Sviluppo incrementale<br />
sistema di sviluppo del programma inteso ad evitare lunghe<br />
sessioni di debug alla ricerca degli errori aggiungendo e<br />
testando solo piccole porzioni di codice alla volta.</p>
<p>Codice temporaneo<br />
codice inserito solo nella fase di sviluppo del programma e che<br />
non è richiesto nella versione finale.</p>
<p>Condizione di guardia<br />
condizione che controlla e gestisce le circostanze che possono<br />
causare un errore.</p>
<p>Capitolo 6</p>
<p>Iterazione</p>
<p>6.1 Assegnazione e confronto</p>
<p>Come puoi avere già scoperto è possibile assegnare più valori ad una<br />
stessa variabile, con la variabile che assume sempre l&#8217;ultimo valore<br />
assegnato:</p>
<p>Numero = 5<br />
print Numero,<br />
Numero = 7<br />
print Numero</p>
<p>La stampa di questo programma è 5 7, perché la prima volta che Numero<br />
è stampato il suo valore è 5, la seconda 7. La virgola dopo la prima<br />
istruzione print evita il ritorno a capo dopo la stampa così che<br />
entrambi i valori appaiono sulla stessa riga.</p>
<p>Questo è il diagramma di stato per quest&#8217;assegnazione:</p>
<p>[i_assign2.png]</p>
<p>Nel caso di assegnazioni ripetute è particolarmente importante<br />
distinguere tra operazioni di assegnazione e controlli di uguaglianza.<br />
Python usa (=) per l&#8217;assegnazione e si potrebbe essere tentati di<br />
interpretare l&#8217;istruzione a = b come un controllo di equivalenza, ma<br />
non lo è!</p>
<p>In primo luogo l&#8217;equivalenza è commutativa mentre l&#8217;assegnazione non<br />
lo è: in matematica se a = 7 allora 7 = a; in Python l&#8217;istruzione a=7<br />
è legale mentre 7=a produce un errore di sintassi.</p>
<p>Inoltre in matematica un&#8217;uguaglianza è sempre vera: se a = b, a sarà<br />
sempre uguale a b. In Python un&#8217;assegnazione può rendere due variabili<br />
uguali ma raramente l&#8217;uguaglianza sarà mantenuta a lungo:</p>
<p>a = 5<br />
b = a    # a e b sono uguali<br />
a = 3    # ora a e b sono diversi</p>
<p>La terza riga cambia il valore di a ma non cambia il valore di b. In<br />
qualche linguaggio di programmazione sono usati simboli diversi per<br />
l&#8217;assegnazione, tipo &lt;- o :=, per evitare ogni malinteso.</p>
<p>6.2 L&#8217;istruzione while</p>
<p>I computer sono spesso usati per automatizzare compiti ripetitivi: il<br />
noiosissimo compito di ripetere operazioni identiche o simili un gran<br />
numero di volte senza fare errori è qualcosa che riesce bene ai<br />
computer.</p>
<p>Abbiamo visto due programmi, NRigheVuote e ContoAllaRovescia, che<br />
usano la ricorsione per eseguire una ripetizione. Questa ripetizione è<br />
più comunemente chiamata iterazione. Dato che l&#8217;iterazione è così<br />
comune, Python fornisce vari sistemi per renderla più semplice da<br />
implementare. Il primo sistema è l&#8217;istruzione while.</p>
<p>Ecco come ContoAllaRovescia viene riscritto usando l&#8217;istruzione while:</p>
<p>def ContoAllaRovescia(n):<br />
while n &gt; 0:<br />
print n<br />
n = n-1<br />
print &#8220;Partenza!&#8221;</p>
<p>La chiamata ricorsiva è stata rimossa e quindi questa funzione ora non<br />
è più ricorsiva.</p>
<p>Puoi leggere il programma con l&#8217;istruzione while come fosse scritto in<br />
un linguaggio naturale: &#8220;Finché (while) n è più grande di 0 stampa il<br />
valore di n e poi diminuiscilo di 1. Quando arrivi a 0 stampa la<br />
stringa Partenza!&#8221;.</p>
<p>In modo più formale ecco il flusso di esecuzione di un&#8217;istruzione<br />
while:<br />
1. Valuta la condizione controllando se essa è vera (1) o falsa (0).<br />
2. Se la condizione è falsa esci dal ciclo while e continua<br />
l&#8217;esecuzione dalla prima istruzione che lo segue.<br />
3. Se la condizione è vera esegui tutte le istruzioni nel corpo del<br />
while e torna al passo 1.</p>
<p>Il corpo del ciclo while consiste di tutte le istruzioni che seguono<br />
l&#8217;intestazione e che hanno la stessa indentazione.</p>
<p>Questo tipo di flusso è chiamato ciclo o loop. Nota che se la<br />
condizione è falsa al primo controllo, le istruzioni del corpo non<br />
sono mai eseguite.</p>
<p>Il corpo del ciclo dovrebbe cambiare il valore di una o più variabili<br />
così che la condizione possa prima o poi diventare falsa e far così<br />
terminare il ciclo. In caso contrario il ciclo si ripeterebbe<br />
all&#8217;infinito, determinando un ciclo infinito.</p>
<p>Nel caso di ContoAllaRovescia possiamo essere certi che il ciclo è<br />
destinato a terminare visto che n è finito ed il suo valore diventa<br />
via via più piccolo così da diventare, prima o poi, pari a zero. In<br />
altri casi può non essere così facile stabilire se un ciclo avrà<br />
termine:</p>
<p>def Sequenza(n):<br />
while n != 1:<br />
print n,<br />
if n%2 == 0:        # se n e&#8217; pari<br />
n = n/2<br />
else:               # se n e&#8217; dispari<br />
n = n*3+1</p>
<p>La condizione per questo ciclo è n!=1 cosicché il ciclo si ripeterà<br />
finché n è diverso da 1.</p>
<p>Ogni volta che viene eseguito il ciclo il programma stampa il valore<br />
di n e poi controlla se è pari o dispari. Se è pari, n viene diviso<br />
per 2. Se dispari, è moltiplicato per 3 e gli viene sommato 1. Se il<br />
valore passato è 3, la sequenza risultante è 3, 10, 5, 16, 8, 4, 2, 1.</p>
<p>Dato che n a volte sale e a volte scende in modo abbastanza casuale<br />
non c&#8217;è una prova ovvia che n raggiungerà 1 in modo da far terminare<br />
il ciclo. Per qualche particolare valore di n possiamo facilmente<br />
determinare a priori il suo termine (per esempio per le potenze di 2)<br />
ma per gli altri nessuno è mai riuscito a trovare la dimostrazione che<br />
il ciclo ha termine.</p>
<p>Esercizio: riscrivi la funzione NRigheVuote della sezione 4.9<br />
usando un&#8217;iterazione invece che la ricorsione.</p>
<p>6.3 Tabelle</p>
<p>Una delle cose per cui sono particolarmente indicati i cicli è la<br />
generazione di tabulati. Prima che i computer fossero comunemente<br />
disponibili si dovevano calcolare a mano logaritmi, seni, coseni e i<br />
valori di tante altre funzioni matematiche. Per rendere più facile il<br />
compito i libri di matematica contenevano lunghe tabelle di valori la<br />
cui stesura comportava enormi quantità di lavoro molto noioso e grosse<br />
possibilità di errore.</p>
<p>Quando apparvero i computer l&#8217;idea iniziale fu quella di usarli per<br />
generare tabelle prive di errori. La cosa che non si riuscì a<br />
prevedere fu il fatto che i computer sarebbero diventati così diffusi<br />
e disponibili a tutti da rendere quei lunghi tabulati cartacei del<br />
tutto inutili. Per alcune operazioni i computer usano ancora tabelle<br />
simili in modo del tutto nascosto dall&#8217;operatore: vengono usate per<br />
ottenere risposte approssimate che poi vengono rifinite per<br />
migliorarne la precisione. In qualche caso ci sono stati degli errori<br />
in queste tabelle &#8220;interne&#8221;, il più famoso dei quali ha avuto come<br />
protagonista il Pentium Intel con un errore nel calcolo delle<br />
divisioni in virgola mobile.</p>
<p>Sebbene la tabella dei logaritmi non sia più utile come lo era in<br />
passato rimane tuttavia un buon esempio di iterazione. Il programma<br />
seguente stampa una sequenza di valori nella colonna di sinistra e il<br />
loro logaritmo in quella di destra:</p>
<p>x = 1.0<br />
while x &lt; 10.0:<br />
print x, &#8216;\t&#8217;, math.log(x)<br />
x = x + 1.0</p>
<p>La stringa &#8216;\t&#8217; rappresenta un carattere di tabulazione.</p>
<p>A mano a mano che caratteri e stringhe sono mostrati sullo schermo un<br />
marcatore invisibile chiamato cursore tiene traccia di dove andrà<br />
stampato il carattere successivo. Dopo un&#8217;istruzione print il cursore<br />
normalmente si posiziona all&#8217;inizio della riga successiva.</p>
<p>Il carattere di tabulazione sposta il cursore a destra finché<br />
quest&#8217;ultimo raggiunge una delle posizione di stop delle tabulazioni.<br />
Queste posizioni si ripetono a distanze regolari, tipicamente ogni 4 o<br />
8 caratteri. Le tabulazioni sono utili per allineare in modo semplice<br />
le colonne di testo. Ecco il prodotto del programma appena visto:</p>
<p>1.0     0.0<br />
2.0     0.69314718056<br />
3.0     1.09861228867<br />
4.0     1.38629436112<br />
5.0     1.60943791243<br />
6.0     1.79175946923<br />
7.0     1.94591014906<br />
8.0     2.07944154168<br />
9.0     2.19722457734</p>
<p>Se questi valori sembrano strani ricorda che la funzione log usa il<br />
logaritmo dei numeri naturali e. Dato che le potenze di due sono così<br />
importanti in informatica possiamo avere la necessità di calcolare il<br />
logaritmo in base 2. Per farlo usiamo questa formula:</p>
<p>log[2] x =</p>
<p>log[e] x<br />
_________________________________________________________________</p>
<p>log[e] 2</p>
<p>Modificando una sola riga di programma:</p>
<p>print x, &#8216;\t&#8217;,  math.log(x)/math.log(2.0)</p>
<p>otteniamo:</p>
<p>1.0     0.0<br />
2.0     1.0<br />
3.0     1.58496250072<br />
4.0     2.0<br />
5.0     2.32192809489<br />
6.0     2.58496250072<br />
7.0     2.80735492206<br />
8.0     3.0<br />
9.0     3.16992500144</p>
<p>Possiamo vedere che 1, 2, 4 e 8 sono potenze di due perché i loro<br />
logaritmi in base 2 sono numeri interi.</p>
<p>Per continuare con le modifiche, invece di sommare qualcosa a x ad<br />
ogni ciclo e ottenere così una serie aritmetica, possiamo moltiplicare<br />
x per qualcosa ottenendo una serie geometrica. Se vogliamo trovare il<br />
logaritmo di altre potenze di due possiamo modificare ancora il<br />
programma:</p>
<p>x = 1.0<br />
while x &lt; 100.0:<br />
print x, &#8216;\t&#8217;, math.log(x)/math.log(2.0)<br />
x = x * 2.0</p>
<p>Il risultato in questo caso è:</p>
<p>1.0     0.0<br />
2.0     1.0<br />
4.0     2.0<br />
8.0     3.0<br />
16.0    4.0<br />
32.0    5.0<br />
64.0    6.0</p>
<p>Il carattere di tabulazione fa in modo che la posizione della seconda<br />
colonna non dipenda dal numero di cifre del valore nella prima.</p>
<p>Anche se i logaritmi possono non essere più così utili per un<br />
informatico, conoscere le potenze di due è fondamentale.</p>
<p>Esercizio: modifica questo programma per fare in modo che esso<br />
produca le potenze di due fino a 65536 (cioè 2^16). Poi stampale e<br />
imparale a memoria!</p>
<p>Il carattere di backslash &#8216;\&#8217; in &#8216;\t&#8217; indica l&#8217;inizio di una sequenza<br />
di escape. Le sequenze di escape sono usate per rappresentare<br />
caratteri invisibili come la tabulazione (&#8216;\t&#8217;) e il ritorno a capo<br />
(&#8216;\n&#8217;). Può comparire in qualsiasi punto di una stringa: nell&#8217;esempio<br />
appena visto la tabulazione è l&#8217;unica cosa presente nella stringa del<br />
print.</p>
<p>Secondo te, com&#8217;è possibile rappresentare un carattere di backslash in<br />
una stringa?</p>
<p>Esercizio: scrivi una stringa singola che quando stampata</p>
<p>produca<br />
questo<br />
risultato.</p>
<p>6.4 Tabelle bidimensionali</p>
<p>Una tabella bidimensionale è una tabella dove leggi un valore<br />
all&#8217;intersezione tra una riga ed una colonna, la tabella della<br />
moltiplicazione ne è un buon esempio. Immaginiamo che tu voglia<br />
stampare la tabella della moltiplicazione per i numeri da 1 a 6.</p>
<p>Un buon modo per iniziare è scrivere un ciclo che stampa i multipli di<br />
2 tutti su di una stessa riga:</p>
<p>i = 1<br />
while i &lt;= 6:<br />
print 2*i, &#8216;   &#8216;,<br />
i = i + 1<br />
print</p>
<p>La prima riga inizializza una variabile chiamata i che agisce come<br />
contatore o indice del ciclo. Man mano che il ciclo viene eseguito i<br />
passa da 1 a 6. Quando i è 7 la condizione non è più soddisfatta ed il<br />
ciclo termina. Ad ogni ciclo viene mostrato il valore di 2*i seguito<br />
da tre spazi.</p>
<p>Ancora una volta vediamo come la virgola in print faccia in modo che<br />
il cursore rimanga sulla stessa riga evitando un ritorno a capo.<br />
Quando il ciclo sui sei valori è stato completato una seconda<br />
istruzione print ha lo scopo di portare il cursore a capo su una nuova<br />
riga.</p>
<p>Il risultato del programma è:</p>
<p>2      4      6      8      10     12</p>
<p>6.5 Incapsulamento e generalizzazione</p>
<p>L&#8217;incapsulamento è il processo di inserire un pezzo di codice<br />
all&#8217;interno di una funzione così da permetterti di godere dei vantaggi<br />
delle funzioni. Hai già visto due esempi di incapsulamento:<br />
StampaParita nella sezione 4.5 e Divisibile nella sezione 5.4.</p>
<p>Generalizzare significa prendere qualcosa di specifico per farlo<br />
diventare generale: nel nostro caso prendere il programma che calcola<br />
i multipli di 2 e fargli calcolare i multipli di un qualsiasi numero<br />
intero.</p>
<p>Questa funzione incapsula il ciclo visto in precedenza e lo<br />
generalizza per stampare i primi 6 multipli di n:</p>
<p>def StampaMultipli(n):<br />
i = 1<br />
while i &lt;= 6:<br />
print n*i, &#8216;\t&#8217;,<br />
i = i + 1<br />
print</p>
<p>Per incapsulare dobbiamo solo aggiungere la prima linea che dichiara<br />
il nome della funzione e la lista dei parametri. Per generalizzare<br />
dobbiamo sostituire il valore 2 con il parametro n.</p>
<p>Se chiamiamo la funzione con l&#8217;argomento 2 otteniamo lo stesso<br />
risultato di prima. Con l&#8217;argomento 3 il risultato è:</p>
<p>3      6      9      12     15     18</p>
<p>Con l&#8217;argomento 4:</p>
<p>4      8      12     16     20     24</p>
<p>Avrai certamente indovinato come stampare la tabella della<br />
moltiplicazione. Chiamiamo ripetutamente StampaMultipli con argomenti<br />
diversi all&#8217;interno di un secondo ciclo:</p>
<p>i = 1<br />
while i &lt;= 6:<br />
StampaMultipli(i)<br />
i = i + 1</p>
<p>Nota come siano simili questo ciclo e quello all&#8217;interno di<br />
StampaMultipli: tutto quello che abbiamo fatto è stato sostituire<br />
l&#8217;istruzione print con una chiamata di funzione.</p>
<p>Il risultato di questo programma è la tabella della moltiplicazione:</p>
<p>1      2      3      4      5      6<br />
2      4      6      8      10     12<br />
3      6      9      12     15     18<br />
4      8      12     16     20     24<br />
5      10     15     20     25     30<br />
6      12     18     24     30     36</p>
<p>6.6 Ancora incapsulamento</p>
<p>Per provare ancora con l&#8217;incapsulamento andiamo a prendere il codice<br />
della sezione precedente e inseriamolo in una funzione:</p>
<p>def TabellaMoltiplicazione6x6():<br />
i = 1<br />
while i &lt;= 6:<br />
StampaMultipli(i)<br />
i = i + 1</p>
<p>Il processo appena illustrato è un piano di sviluppo piuttosto comune:<br />
si sviluppa del codice controllando poche righe in ambiente<br />
interprete; solo quando queste righe sono perfettamente funzionanti le<br />
inseriamo in una funzione.</p>
<p>Questo modo di procedere è particolarmente utile se all&#8217;inizio della<br />
stesura del tuo programma non sai come lo dividerai in funzioni.<br />
Questo tipo di approccio ti permette di progettare il codice mentre<br />
procedi con la stesura.</p>
<p>6.7 Variabili locali</p>
<p>Potresti chiederti com&#8217;è possibile che si possa usare la stessa<br />
variabile i sia in StampaMultipli che in TabellaMoltiplicazione6x6.<br />
Non ci sono problemi quando una funzione cambia il valore della<br />
variabile?</p>
<p>La risposta è no dato che la variabile i usata in StampaMultipli e la<br />
i in TabellaMoltiplicazione6x6 non sono la stessa variabile.</p>
<p>Le variabili create all&#8217;interno della definizione di una funzione sono<br />
locali e non puoi accedere al valore di una variabile locale al di<br />
fuori della funzione che la ospita. Ciò significa che sei libero di<br />
avere variabili con lo stesso nome sempre che non si trovino<br />
all&#8217;interno di una stessa funzione.</p>
<p>Il diagramma di stack per questo programma mostra che le due variabili<br />
chiamate i non sono la stessa variabile. Si riferiscono a valori<br />
diversi e cambiandone una l&#8217;altra resta invariata.</p>
<p>[i_stack4.png]</p>
<p>Il valore di i in TabellaMoltiplicazione6x6 va da 1 a 6. Nel diagramma<br />
ha valore 3 e al prossimo ciclo varrà 4. Ad ogni ciclo<br />
TabellaMoltiplicazione6x6 chiama StampaMultipli con il valore corrente<br />
di i come argomento. Quel valore viene assegnato al parametro n.</p>
<p>All&#8217;interno di StampaMultipli il valore di i copre l&#8217;intervallo che va<br />
da 1 a 6. Nel diagramma è 2 e cambiandolo non ci sono effetti<br />
collaterali per la variabile i in TabellaMoltiplicazione6x6.</p>
<p>È comune e perfettamente legale avere variabili locali con lo stesso<br />
nome. In particolare nomi come i e j sono usati frequentemente come<br />
indici per i cicli.</p>
<p>6.8 Ancora generalizzazione</p>
<p>Se vogliamo generalizzare ulteriormente TabellaMoltiplicazione6x6<br />
potremmo estendere il risultato ad una tabella di moltiplicazione di<br />
qualsiasi grandezza, e non solo fino al 6&#215;6. A questo punto dobbiamo<br />
anche passare un argomento per stabilire la grandezza desiderata:</p>
<p>def TabellaMoltiplicazioneGenerica(Grandezza):<br />
i = 1<br />
while i &lt;= Grandezza:<br />
StampaMultipli(i)<br />
i = i + 1</p>
<p>Abbiamo sostituito il valore 6 con il parametro Grandezza. Se<br />
chiamiamo TabellaMoltiplicazioneGenerica con l&#8217;argomento 7 il<br />
risultato è:</p>
<p>1      2      3      4      5      6<br />
2      4      6      8      10     12<br />
3      6      9      12     15     18<br />
4      8      12     16     20     24<br />
5      10     15     20     25     30<br />
6      12     18     24     30     36<br />
7      14     21     28     35     42</p>
<p>Il risultato è corretto fatta eccezione per il fatto che sarebbe<br />
meglio avere lo stesso numero di righe e di colonne. Per farlo<br />
dobbiamo modificare StampaMultipli per specificare quante colonne la<br />
tabella debba avere.</p>
<p>Tanto per essere originali chiamiamo anche questo parametro Grandezza,<br />
dimostrando ancora una volta che possiamo avere parametri con lo<br />
stesso nome all&#8217;interno di funzioni diverse. Ecco l&#8217;intero programma:</p>
<p>def StampaMultipli(n, Grandezza):<br />
i = 1<br />
while i &lt;= Grandezza:<br />
print n*i, &#8216;\t&#8217;,<br />
i = i + 1<br />
print<br />
def TabellaMoltiplicazioneGenerica(Grandezza):<br />
i = 1<br />
while i &lt;= Grandezza:<br />
StampaMultipli(i, Grandezza)<br />
i = i + 1</p>
<p>Quando abbiamo aggiunto il nuovo parametro abbiamo cambiato la prima<br />
riga della funzione (l&#8217;intestazione) ed il posto dove la funzione è<br />
chiamata in TabellaMoltiplicazioneGenerica.</p>
<p>Questo programma genera correttamente la tabella 7&#215;7:</p>
<p>1      2      3      4      5      6      7<br />
2      4      6      8      10     12     14<br />
3      6      9      12     15     18     21<br />
4      8      12     16     20     24     28<br />
5      10     15     20     25     30     35<br />
6      12     18     24     30     36     42<br />
7      14     21     28     35     42     49</p>
<p>Quando generalizzi una funzione nel modo più appropriato, spesso<br />
ottieni capacità che inizialmente non erano state previste. Per<br />
esempio dato che ab = ba, tutti i numeri compresi nella tabella (fatta<br />
eccezione per quelli della diagonale) sono presenti due volte. In caso<br />
di necessità puoi modificare una linea in<br />
TabellaMoltiplicazioneGenerica per stamparne solo metà. Cambia :</p>
<p>StampaMultipli(i, Grandezza)</p>
<p>in</p>
<p>StampaMultipli(i, i)</p>
<p>per ottenere</p>
<p>1<br />
2      4<br />
3      6      9<br />
4      8      12     16<br />
5      10     15     20     25<br />
6      12     18     24     30     36<br />
7      14     21     28     35     42     49</p>
<p>Esercizio: il compito consiste nel tracciare l&#8217;esecuzione di questa<br />
versione TabellaMoltiplicazioneGenerica e cerca di capire come<br />
funziona.</p>
<p>6.9 Funzioni</p>
<p>Abbiamo già menzionato i motivi per cui è consigliato l&#8217;uso delle<br />
funzioni, senza però entrare nel merito. Adesso ti starai chiedendo a<br />
che cosa ci stessimo riferendo. Eccone qualcuno:<br />
* Dare un nome ad una sequenza di istruzioni per rendere il tuo<br />
programma più semplice da leggere e correggere.<br />
* Dividere un grosso programma in tante piccole parti che possono<br />
essere testate singolarmente e poi ricomposte in un tutto unico.<br />
* Facilitare sia la ricorsione che l&#8217;iterazione.<br />
* Riutilizzare parti di programma: quando una funzione è stata<br />
scritta e testata può essere riutilizzata anche in altri<br />
programmi.</p>
<p>6.10 Glossario</p>
<p>Assegnazione ripetuta<br />
assegnazione alla stessa variabile di più valori nel corso del<br />
programma.</p>
<p>Iterazione<br />
ripetizione di una serie di istruzioni usando una funzione<br />
ricorsiva o un ciclo.</p>
<p>Ciclo<br />
istruzione o gruppo di istruzioni che vengono eseguite<br />
ripetutamente finché è soddisfatta una condizione.</p>
<p>Ciclo infinito<br />
ciclo nel quale la condizione di terminazione non è mai<br />
soddisfatta.</p>
<p>Corpo<br />
gruppo di istruzioni all&#8217;interno di un ciclo.</p>
<p>Indice del ciclo<br />
variabile usata nella condizione di terminazione di un ciclo.</p>
<p>Tabulazione<br />
carattere speciale (&#8216;\t&#8217;) che in un&#8217;istruzione di stampa sposta<br />
il cursore alla prossima posizione di stop nella riga corrente.</p>
<p>Ritorno a capo<br />
carattere speciale (&#8216;\n&#8217;) che in un&#8217;istruzione di stampa sposta<br />
il cursore all&#8217;inizio della prossima riga.</p>
<p>Cursore<br />
marcatore non visibile che tiene traccia di dove andrà stampato<br />
il prossimo carattere.</p>
<p>Sequenza di escape<br />
carattere (\\) seguito da uno o più caratteri, usato per<br />
designare dei caratteri non stampabili.</p>
<p>Incapsulare<br />
dividere un programma complesso in componenti più semplici,<br />
tipo le funzioni, e isolarne i componenti uno dall&#8217;altro usando<br />
variabili locali.</p>
<p>Generalizzare<br />
sostituire qualcosa di specifico (come un valore costante) con<br />
qualcosa di più generale (come un parametro o una variabile).</p>
<p>Piano di sviluppo<br />
processo per lo sviluppo di un programma. In questo capitolo<br />
abbiamo mostrato uno stile di sviluppo basato sulla scrittura<br />
di un semplice programma capace di svolgere un compito<br />
specifico, per poi estenderlo con l&#8217;incapsulamento e la<br />
generalizzazione.</p>
<p>Capitolo 7</p>
<p>Stringhe</p>
<p>7.1 Tipi di dati composti</p>
<p>Finora abbiamo visto tre tipi di dati: int, float e string. Le<br />
stringhe sono qualitativamente diverse dagli altri tipi di dati poiché<br />
sono composte di unità più piccole: i caratteri.</p>
<p>I tipi di dati che sono fatti di elementi più piccoli sono detti tipi<br />
di dati composti. A seconda di ciò che stiamo facendo possiamo avere<br />
la necessità di trattare un tipo composto come fosse una singola<br />
entità o possiamo voler agire sulle sue singole parti. Questa duplice<br />
possibilità è molto utile.</p>
<p>L&#8217;operatore porzione seleziona dei caratteri da una stringa:</p>
<p>&gt;&gt;&gt; Frutto = &#8220;banana&#8221;<br />
&gt;&gt;&gt; Lettera = Frutto[1]<br />
&gt;&gt;&gt; print Lettera</p>
<p>L&#8217;espressione Frutto[1] seleziona il carattere numero 1 dalla stringa<br />
Frutto. La variabile Lettera contiene il risultato. Quando stampiamo<br />
Lettera abbiamo però una sorpresa:</p>
<p>a</p>
<p>La prima lettera di &#8220;banana&#8221; logicamente non è &#8220;a&#8221;: in informatica i<br />
conteggi partono spesso da 0 e non da 1 come potrebbe sembrare normale<br />
e per accedere al primo carattere di una stringa dobbiamo quindi<br />
richiedere il numero 0, per il secondo il numero 1 e così via. Sembra<br />
un po&#8217; illogico ma ci farai facilmente l&#8217;abitudine perché questo è il<br />
modo normale di contare in molti linguaggi di programmazione. Quindi<br />
se vogliamo sapere l&#8217;iniziale della stringa scriviamo:</p>
<p>&gt;&gt;&gt; Lettera = Frutto[0]<br />
&gt;&gt;&gt; print Lettera<br />
b</p>
<p>L&#8217;espressione tra parentesi quadrate è chiamata indice. Un indice<br />
identifica un particolare elemento di un insieme ordinato che nel<br />
nostro caso è l&#8217;insieme dei caratteri di una stringa. L&#8217;indice può<br />
essere una qualsiasi espressione intera.</p>
<p>7.2 Lunghezza</p>
<p>La funzione len ritorna il numero di caratteri di una stringa:</p>
<p>&gt;&gt;&gt; Frutto = &#8220;banana&#8221;<br />
&gt;&gt;&gt; len(Frutto)<br />
6</p>
<p>Per ottenere l&#8217;ultimo carattere di una stringa potresti essere tentato<br />
di fare qualcosa di simile a:</p>
<p>Lunghezza = len(Frutto)<br />
Ultimo = Frutto[Lunghezza]       # ERRORE!</p>
<p>ma c&#8217;è qualcosa che non va: infatti ottieni un errore IndiceError:<br />
string index out of range dato che stai facendo riferimento all&#8217;indice<br />
6 quando quelli validi vanno da 0 a 5. Per ottenere l&#8217;ultimo carattere<br />
dovrai quindi scrivere:</p>
<p>Lunghezza = len(Frutto)<br />
Ultimo = Frutto[Lunghezza-1]</p>
<p>In alternativa possiamo usare indici negativi che in casi come questo<br />
sono più comodi, contando a partire dalla fine della stringa:<br />
l&#8217;espressione Frutto[-1] ritorna l&#8217;ultimo carattere della stringa,<br />
Frutto[-2] il penultimo e così via.</p>
<p>7.3 Elaborazione trasversale e cicli for</p>
<p>Molti tipi di elaborazione comportano un&#8217;azione su una stringa un<br />
carattere per volta. Spesso queste elaborazioni iniziano dal primo<br />
carattere, selezionano un carattere per volta e continuano fino al<br />
completamento della stringa. Questo tipo di elaborazione è definita<br />
elaborazione trasversale o attraversamento, in quanto attraversa la<br />
stringa dall&#8217;inizio alla fine. Un modo per implementare<br />
un&#8217;elaborazione trasversale è quello di usare un ciclo while:</p>
<p>Indice = 0<br />
while Indice &lt; len(Frutto):<br />
Lettera = Frutto[Indice]<br />
print Lettera<br />
Indice = Indice + 1</p>
<p>Questo ciclo attraversa la stringa e ne mostra una lettera alla volta,<br />
una per riga. La condizione del ciclo è Indice &lt; len(Frutto) così che<br />
quando Indice è uguale alla lunghezza della stringa la condizione<br />
diventa falsa, il corpo del ciclo non è eseguito ed il ciclo termina.<br />
L&#8217;ultimo carattere cui si accede è quello con indice len(Frutto)-1 che<br />
è l&#8217;ultimo carattere della stringa.</p>
<p>Esercizio: scrivi una funzione che prende una stringa come<br />
argomento e la stampa un carattere per riga partendo dall&#8217;ultimo<br />
carattere.</p>
<p>Usare un indice per attraversare un insieme di valori è un&#8217;operazione<br />
così comune che Python fornisce una sintassi ancora più semplice: il<br />
ciclo for.</p>
<p>for Lettera in Frutto:<br />
print Lettera</p>
<p>Ad ogni ciclo, Lettera assume il valore del prossimo carattere della<br />
stringa Frutto, così che Frutto viene attraversata completamente<br />
finché non rimangono più caratteri da analizzare.</p>
<p>L&#8217;esempio seguente mostra come usare il concatenamento e un ciclo for<br />
per generare una serie alfabetica, e cioè una lista di valori nei<br />
quali gli elementi appaiono in ordine alfabetico. Per esempio nel<br />
libro Make Way for Ducklings di Robert McCloskey i nomi dei<br />
protagonisti sono Jack, Kack, Lack, Mack, Nack, Ouack, Pack e Quack.<br />
Questo ciclo restituisce i nomi in ordine:</p>
<p>Prefissi = &#8220;JKLMNOPQ&#8221;<br />
Suffisso = &#8220;ack&#8221;<br />
for Lettera in Prefissi:<br />
print Lettera + Suffisso</p>
<p>Il risultato del programma è:</p>
<p>Jack<br />
Kack<br />
Lack<br />
Mack<br />
Nack<br />
Oack<br />
Pack<br />
Qack</p>
<p>Non è del tutto corretto dato che Ouack e Quack sono scritti in modo<br />
errato.</p>
<p>Esercizio: modifica il programma per correggere questo errore.</p>
<p>7.4 Porzioni di stringa</p>
<p>Un segmento di stringa è chiamato porzione. La selezione di una<br />
porzione è simile alla selezione di un carattere:</p>
<p>&gt;&gt;&gt; s = &#8220;Pietro, Paolo e Maria&#8221;<br />
&gt;&gt;&gt; print s[0:6]<br />
Pietro<br />
&gt;&gt;&gt; print s[8:13]<br />
Paolo<br />
&gt;&gt;&gt; print s[16:21]<br />
Maria</p>
<p>L&#8217;operatore [n:m] ritorna la stringa a partire dall&#8217; &#8220;n-esimo&#8221;<br />
carattere incluso fino all&#8217; &#8220;m-esimo&#8221; escluso. Questo comportamento<br />
non è intuitivo, e per comprenderlo è meglio immaginare i puntatori<br />
tra i caratteri, come nel diagramma seguente:</p>
<p>[i_banana.png]</p>
<p>Se non è specificato il primo indice (prima dei due punti <img src='http://s0.wp.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  la<br />
porzione parte dall&#8217;inizio della stringa. Senza il secondo indice la<br />
porzione finisce con il termine della stringa:</p>
<p>&gt;&gt;&gt; Frutto = &#8220;banana&#8221;<br />
&gt;&gt;&gt; Frutto[:3]<br />
&#8216;ban&#8217;<br />
&gt;&gt;&gt; Frutto[3:]<br />
&#8216;ana&#8217;</p>
<p>Secondo te cosa significa Frutto[:]?</p>
<p>7.5 Confronto di stringhe</p>
<p>Gli operatori di confronto operano anche sulle stringhe. Per vedere se<br />
due stringhe sono uguali:</p>
<p>if Parola == &#8220;BANANA&#8221;:<br />
print  &#8220;stai parlando di un frutto!&#8221;</p>
<p>Altri operatori di confronto sono utili per mettere le parole in<br />
ordine alfabetico:</p>
<p>if Parola &lt; &#8220;BANANA&#8221;:<br />
print &#8220;la tua parola&#8221; + Parola + &#8220;viene prima di BANANA.&#8221;<br />
elif Parola &gt; &#8220;BANANA&#8221;:<br />
print &#8220;la tua parola&#8221; + Parola + &#8220;viene dopo BANANA.&#8221;<br />
else:<br />
print &#8220;hai inserito la parola BANANA&#8221;</p>
<p>Devi comunque fare attenzione al fatto che Python non gestisce le<br />
parole maiuscole e minuscole come facciamo noi in modo intuitivo: in<br />
un confronto le lettere maiuscole vengono sempre prima delle<br />
minuscole, così che:</p>
<p>&#8220;BANANA&#8221; &lt; &#8220;BAnana&#8221; &lt; &#8220;Banana&#8221; &lt; &#8220;bANANA&#8221; &lt; &#8220;banana&#8221;<br />
&#8220;ZEBRA&#8221; &lt; &#8220;banana&#8221;</p>
<p>Un modo pratico per aggirare il problema è quello di convertire le<br />
stringhe ad un formato standard (tutto maiuscole o tutto minuscole)<br />
prima di effettuare il confronto.</p>
<p>7.6 Le stringhe sono immutabili</p>
<p>Si può essere tentati di usare l&#8217;operatore porzione [] alla sinistra<br />
di un&#8217;assegnazione, con l&#8217;intenzione di cambiare un carattere di una<br />
stringa:</p>
<p>Saluto = &#8220;Ciao!&#8221;<br />
Saluto[0] = &#8216;M&#8217;            # ERRORE!<br />
print Saluto</p>
<p>Invece di ottenere Miao! questo codice stampa il messaggio d&#8217;errore<br />
TypeError: object doesn&#8217;t support item assignment.</p>
<p>Le stringhe sono infatti immutabili e ciò significa che non puoi<br />
cambiare una stringa esistente. L&#8217;unica cosa che puoi eventualmente<br />
fare è creare una nuova stringa come variante di quella originale:</p>
<p>Saluto = &#8220;Ciao!&#8221;<br />
NuovoSaluto = &#8216;M&#8217; + Saluto[1:]<br />
print NuovoSaluto</p>
<p>Abbiamo concatenato la nuova prima lettera ad una porzione di Saluto,<br />
e questa operazione non ha avuto alcun effetto sulla stringa<br />
originale.</p>
<p>7.7 Funzione Trova</p>
<p>Secondo te cosa fa questa funzione?</p>
<p>def Trova(Stringa, Carattere):<br />
Indice = 0<br />
while Indice &lt; len(Stringa):<br />
if Stringa[Indice] == Carattere:<br />
return Indice<br />
Indice = Indice + 1<br />
return -1</p>
<p>In un certo senso questa funzione Trova è l&#8217;opposto dell&#8217;operatore<br />
porzione []: invece di prendere un indice e trovare il carattere<br />
corrispondente cerca in una stringa la posizione dove appare un<br />
carattere e ne restituisce l&#8217;indice. Se il carattere non è presente la<br />
funzione restituisce -1.</p>
<p>Questo è il primo esempio di return all&#8217;interno di un ciclo. Se<br />
Stringa[Indice] == Carattere il ciclo viene interrotto prematuramente.<br />
Se il carattere non fa parte della stringa il programma termina<br />
normalmente e ritorna -1.</p>
<p>Esercizio: modifica la funzione Trova per accettare un terzo<br />
parametro che rappresenta la posizione dove si deve cominciare a<br />
cercare all&#8217;interno della stringa.</p>
<p>7.8 Cicli e contatori</p>
<p>Questo programma conta il numero di volte in cui la lettera &#8216;a&#8217;<br />
compare in una stringa, usando un contatore:</p>
<p>Frutto = &#8220;banana&#8221;<br />
Conteggio = 0<br />
for Carattere in Frutto:<br />
if Carattere == &#8216;a&#8217;:<br />
Conteggio = Conteggio + 1<br />
print Conteggio</p>
<p>La variabile Conteggio è inizializzata a 0 e poi incrementata ogni<br />
volta che è trovata una &#8216;a&#8217; (incrementare significa aumentare di 1; è<br />
l&#8217;opposto di decrementare). Al termine del ciclo Conteggio contiene il<br />
risultato e cioè il numero totale di lettere a nella stringa.</p>
<p>Esercizio: incapsula questo codice in una funzione ContaLettera e<br />
fai in modo che questa accetti sia la stringa che la lettera da<br />
cercare come parametri.</p>
<p>Esercizio: riscrivi la funzione ContaLettera in modo che invece di<br />
elaborare completamente la stringa faccia uso della versione a tre<br />
parametri di Trova.</p>
<p>7.9 Il modulo string</p>
<p>Il modulo string contiene funzioni molto utili per la manipolazione<br />
delle stringhe. Come abbiamo già visto prima di poter usare un modulo<br />
lo dobbiamo importare:</p>
<p>&gt;&gt;&gt; import string<br />
\fussy Il modulo string include una funzione chiamata find che fa le<br />
stesse cose della nostra funzione Trova. Per poterla usare, dopo avere<br />
importato il modulo, dobbiamo chiamarla usando la notazione punto<br />
(NomeDelModulo.NomeDellaFunzione):</p>
<p>&gt;&gt;&gt; Frutto = &#8220;banana&#8221;<br />
&gt;&gt;&gt; Posizione = string.find(Frutto, &#8220;a&#8221;)<br />
&gt;&gt;&gt; print Posizione<br />
1</p>
<p>In realtà string.find è più generale della nostra Trova. In primo<br />
luogo possiamo usarla per cercare stringhe e non soltanto caratteri:</p>
<p>&gt;&gt;&gt; string.find(&#8220;banana&#8221;, &#8220;na&#8221;)<br />
2</p>
<p>Inoltre ammette un argomento ulteriore per specificare da dove<br />
vogliamo iniziare la nostra ricerca:</p>
<p>&gt;&gt;&gt; string.find(&#8220;banana&#8221;, &#8220;na&#8221;, 3)<br />
4</p>
<p>Ancora, può prendere due argomenti che specificano il dominio di<br />
ricerca, cioè la porzione di stringa originale dove vogliamo<br />
effettuare la ricerca:</p>
<p>&gt;&gt;&gt; string.find(&#8220;bob&#8221;, &#8220;b&#8221;, 1, 2)<br />
-1</p>
<p>In questo esempio la ricerca fallisce perché la lettera &#8216;b&#8217; non appare<br />
nel dominio definito dagli indici 1 e 2 (da 1 incluso fino a 2<br />
escluso).</p>
<p>7.10 Classificazione dei caratteri</p>
<p>È spesso necessario esaminare un carattere e controllare se questo è<br />
maiuscolo, minuscolo, o se si tratta di una cifra o di uno spazio<br />
bianco. A questo scopo il modulo string fornisce parecchie costanti<br />
molto utili.</p>
<p>La stringa string.lowercase contiene tutti i caratteri che il sistema<br />
considera minuscoli. Allo stesso modo string.uppercase contiene tutti<br />
i caratteri maiuscoli. Guarda cosa contengono queste stringhe:</p>
<p>&gt;&gt;&gt; print string.lowercase<br />
&gt;&gt;&gt; print string.uppercase<br />
&gt;&gt;&gt; print string.digits</p>
<p>Possiamo usare queste costanti e la funzione find per classificare i<br />
caratteri. Per esempio se find(string.lowercase, Carattere) ritorna un<br />
valore diverso da -1 allora Carattere è minuscolo (un valore diverso<br />
da -1 indicherebbe infatti la posizione del carattere trovato):</p>
<p>def Minuscolo(Carattere):<br />
return string.find(string.lowercase, Carattere) != -1</p>
<p>In alternativa possiamo usare l&#8217;operatore in che determina se un<br />
carattere compare in una stringa:</p>
<p>def Minuscolo(Carattere):<br />
return Carattere in string.lowercase</p>
<p>o il consueto operatore di confronto:</p>
<p>def Minuscolo(Carattere):<br />
return &#8216;a&#8217; &lt;= Carattere &lt;= &#8216;z&#8217;</p>
<p>Se Carattere è compreso tra &#8216;a&#8217; e &#8216;z&#8217; deve per forza trattarsi di una<br />
lettera minuscola.</p>
<p>Esercizio: prova a determinare quale di queste versioni è la più<br />
veloce. Puoi pensare ad altre ragioni, a parte la velocità, per<br />
preferire una versione piuttosto che un&#8217;altra?</p>
<p>Un&#8217;altra costante definita nel modulo string può sorprenderti quando<br />
provi a stamparla:</p>
<p>&gt;&gt;&gt; print string.whitespace</p>
<p>I caratteri spazi bianchi infatti muovono il cursore senza stampare<br />
nulla: sono questi che creano lo spazio bianco tra i caratteri<br />
visibili. La costante string.whitespace contiene tutti gli spazi<br />
bianchi inclusi lo spazio, la tabulazione (\t) ed il ritorno a capo<br />
(\n).</p>
<p>Ci sono molte altre utili funzioni nel modulo string ma questo libro<br />
non è inteso per essere un manuale di riferimento come invece lo è la<br />
Python Library Reference, disponibile al sito ufficiale del linguaggio<br />
Python www.python.org.</p>
<p>7.11 Glossario</p>
<p>Tipo di dati composto<br />
un tipo di dati costruito con componenti che sono essi stessi<br />
dei valori.</p>
<p>Attraversare<br />
elaborare tutti gli elementi di un insieme dal primo all&#8217;ultimo<br />
effettuando su tutti la stessa operazione.</p>
<p>Indice<br />
variabile o valore usati per selezionare un elemento di un<br />
insieme ordinato come un carattere in una stringa.</p>
<p>Porzione<br />
parte di una stringa specificata da due indici.</p>
<p>Mutabile<br />
tipo di dati composto al quale possono essere assegnati nuovi<br />
valori.</p>
<p>Contatore<br />
variabile usata per contare qualcosa, di solito inizializzata a<br />
0 e successivamente incrementata.</p>
<p>Incrementare<br />
aumentare di 1 il valore di una variabile.</p>
<p>Decrementare<br />
diminuire di 1 il valore di una variabile.</p>
<p>Spazio bianco<br />
ciascuno dei caratteri che se stampato si limita a muovere il<br />
cursore senza stampare caratteri visibili. La costante<br />
string.whitespace contiene tutti gli spazi bianchi.</p>
<p>Capitolo 8</p>
<p>Liste</p>
<p>Una lista è una serie ordinata di valori, ognuno identificato da un<br />
indice. I valori che fanno parte della lista sono chiamati elementi.<br />
Le liste sono simili alle stringhe essendo insiemi ordinati di<br />
caratteri, fatta eccezione per il fatto che gli elementi di una lista<br />
possono essere di tipo qualsiasi. Liste e stringhe (e altri tipi di<br />
dati che si comportano da insiemi ordinati) sono chiamate sequenze.</p>
<p>8.1 Valori della lista</p>
<p>Ci sono parecchi modi di creare una lista nuova, e quello più semplice<br />
è racchiudere i suoi elementi tra parentesi quadrate ([ e ]):</p>
<p>[10, 20, 30, 40]<br />
["Pippo", "Pluto", "Paperino"]</p>
<p>Il primo esempio è una lista di quattro interi, il secondo una lista<br />
di tre stringhe. Gli elementi di una stessa lista non devono<br />
necessariamente essere tutti dello stesso tipo. \ Questa lista<br />
contiene una stringa, un numero in virgola mobile, un intero ed<br />
un&#8217;altra lista:</p>
<p>["ciao", 2.0, 5, [10, 20]]</p>
<p>Una lista all&#8217;interno di un&#8217;altra lista è detta lista annidata.</p>
<p>Le liste che contengono numeri interi consecutivi sono così comuni che<br />
Python fornisce un modo semplice per crearle:</p>
<p>&gt;&gt;&gt; range(1,5)<br />
[1, 2, 3, 4]</p>
<p>La funzione range prende due argomenti e ritorna una lista che<br />
contiene tutti gli interi a partire dal primo (incluso) fino al<br />
secondo (escluso).</p>
<p>Ci sono altre due forme per range. Con un solo argomento crea una<br />
lista a partire da 0:</p>
<p>&gt;&gt;&gt; range(10)<br />
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]</p>
<p>Se è presente un terzo argomento questo specifica l&#8217;intervallo tra<br />
valori successivi, chiamato passo. Questo esempio mostra come ottenere<br />
una stringa dei numeri dispari tra 1 e 10:</p>
<p>&gt;&gt;&gt; range(1, 10, 2)<br />
[1, 3, 5, 7, 9]</p>
<p>Infine esiste una lista speciale che non contiene alcun elemento: è<br />
chiamata lista vuota ed è indicata da [].</p>
<p>Con tutti questi modi di creare liste sarebbe un peccato non poter<br />
variare il contenuto di una lista o poter passare liste come parametri<br />
di funzioni. Infatti entrambe queste cose possono essere fatte:</p>
<p>&gt;&gt;&gt; Vocabolario = ["amico", "casa", "telefono"]<br />
&gt;&gt;&gt; Numeri = [17, 123]<br />
&gt;&gt;&gt; ListaVuota = []<br />
&gt;&gt;&gt; print Vocabolario, Numeri, ListaVuota<br />
['amico', 'casa', 'telefono'] [17, 123] []</p>
<p>8.2 Accesso agli elementi di una lista</p>
<p>La sintassi per l&#8217;accesso agli elementi di una lista è la stessa che<br />
abbiamo già visto per i caratteri di una stringa: anche in questo caso<br />
facciamo uso dell&#8217;operatore porzione ([]). L&#8217;espressione tra parentesi<br />
quadrate specifica l&#8217;indice dell&#8217;elemento (non dimenticare che gli<br />
indici partono da 0!):</p>
<p>&gt;&gt;&gt; print Numeri[0]<br />
17<br />
&gt;&gt;&gt; Numeri[1] = 5</p>
<p>L&#8217;operatore porzione può comparire in qualsiasi posto di<br />
un&#8217;espressione: quando è alla sinistra di un&#8217;assegnazione cambia uno<br />
degli elementi della lista (nell&#8217;esempio appena visto l&#8217;elemento 123 è<br />
diventato 5).</p>
<p>Come indice possiamo inoltre usare qualsiasi espressione che produca<br />
un intero:</p>
<p>&gt;&gt;&gt; Numeri[3-2]<br />
5<br />
&gt;&gt;&gt; Numeri[1.0]<br />
TypeError: sequence index must be integer</p>
<p>Provando a leggere o modificare un elemento che non esiste si ottiene<br />
un messaggio d&#8217;errore:</p>
<p>&gt;&gt;&gt; Numeri[2] = 5<br />
IndexError: list assignment index out of range</p>
<p>Se un indice ha valore negativo il conteggio parte dalla fine della<br />
lista:</p>
<p>&gt;&gt;&gt; Numeri[-1]<br />
5<br />
&gt;&gt;&gt; Numeri[-2]<br />
17<br />
&gt;&gt;&gt; Numeri[-3]<br />
IndexError: list index out of range</p>
<p>Numeri[-1] è quindi l&#8217;ultimo elemento della lista, Numeri[-2] il<br />
penultimo e Numeri[-3] non esiste essendo la nostra lista composta di<br />
2 soli elementi.</p>
<p>È comune usare una variabile di ciclo come indice di una lista:</p>
<p>Squadre = ["Juventus", "Inter", "Milan", "Roma"]<br />
i = 0<br />
while i &lt; 4:<br />
print Squadre[i]<br />
i = i + 1</p>
<p>Questo ciclo while conta da 0 a 4: quando l&#8217;indice del ciclo i vale 4<br />
la condizione diventa falsa e il ciclo termina. Il corpo del ciclo è<br />
eseguito solo quando i è 0, 1, 2 e 3.</p>
<p>Ad ogni ciclo la variabile i è usata come indice della lista: questo<br />
tipo di elaborazione è chiamata elaborazione trasversale di una lista<br />
o attraversamento di una lista.</p>
<p>8.3 Lunghezza di una lista</p>
<p>La funzione len ritorna la lunghezza di una lista. È sempre bene usare<br />
len per conoscere il limite superiore in un ciclo, piuttosto che usare<br />
un valore costante: in questo modo se la lunghezza della lista dovesse<br />
cambiare non dovrai scorrere il programma per modificarne i cicli, e<br />
sicuramente len funzionerà correttamente per liste di ogni lunghezza:</p>
<p>Squadre = ["Juventus", "Inter", "Milan", "Roma"]<br />
i = 0<br />
while i &lt; len(Squadre):<br />
print Squadre[i]<br />
i = i + 1</p>
<p>L&#8217;ultima volta che il ciclo è eseguito i vale len(Squadre) &#8211; 1 che è<br />
l&#8217;indice dell&#8217;ultimo elemento della lista. Quando al successivo<br />
incremento i diventa len(Squadre) la condizione diventa falsa ed il<br />
corpo non è eseguito, dato che len(Squadre) non è un indice valido.</p>
<p>Sebbene una lista possa contenere a sua volta un&#8217;altra lista questa<br />
lista annidata conta come un singolo elemento indipendentemente dalla<br />
sua lunghezza. La lunghezza della lista seguente è 4:</p>
<p>['ciao!', 1, ['mela', 'pera', 'banana'], [1, 2, 3]]</p>
<p>Esercizio: scrivi un ciclo che attraversa la lista precedente e<br />
stampa la lunghezza di ogni elemento.</p>
<p>8.4 Appartenenza ad una lista</p>
<p>in è un operatore booleano (restituisce vero o falso) che controlla se<br />
un valore è presente in una lista. L&#8217;abbiamo già usato con le stringhe<br />
nella sezione 7.10 ma funziona anche con le liste e con altri tipi di<br />
sequenze:</p>
<p>&gt;&gt;&gt; Squadre = ['Juventus', 'Inter', 'Milan', 'Roma']<br />
&gt;&gt;&gt; &#8216;Inter&#8217; in Squadre<br />
1<br />
&gt;&gt;&gt; &#8216;Arsiero&#8217; in Squadre<br />
0</p>
<p>Dato che Inter è un membro della lista Squadre l&#8217;operatore in ritorna<br />
vero; Arsiero non fa parte della lista e l&#8217;operazione in ritorna<br />
falso.</p>
<p>Possiamo usare not in combinazione con in per controllare se un<br />
elemento non fa parte di una lista:</p>
<p>&gt;&gt;&gt; &#8216;Arsiero&#8217; not in Squadre<br />
1</p>
<p>8.5 Liste e cicli for</p>
<p>Il ciclo for che abbiamo visto nella sezione 7.3 funziona anche con le<br />
liste. La sintassi generica per il ciclo for in questo caso è:</p>
<p>for VARIABILE in LISTA:<br />
CORPO</p>
<p>Questa istruzione è equivalente a:</p>
<p>i = 0<br />
while i &lt; len(LISTA):<br />
VARIABILE = LISTA[i]<br />
CORPO<br />
i = i + 1</p>
<p>Il ciclo for è più conciso perché possiamo eliminare l&#8217;indice del<br />
ciclo i. Ecco il ciclo di uno degli esempi appena visti riscritto con<br />
il for:</p>
<p>for Squadra in Squadre:<br />
print Squadra</p>
<p>Si legge quasi letteralmente: &#8220;Per (ciascuna) Squadra in (nella lista<br />
di) Squadre, stampa (il nome della) Squadra&#8221;.</p>
<p>Nel ciclo for può essere usata qualsiasi espressione che produca una<br />
lista:</p>
<p>for Numero in range(20):<br />
if Numero % 2 == 0:<br />
print Numero<br />
for Frutto in ["banana", "mela", "pera"]:<br />
print &#8220;Mi piace la&#8221; + Frutto + &#8220;!&#8221;</p>
<p>Il primo esempio stampa tutti i numeri pari tra 0 e 19. Il secondo<br />
esprime l&#8217;entusiasmo per la frutta.</p>
<p>8.6 Operazioni sulle liste</p>
<p>L&#8217;operatore + concatena le liste:</p>
<p>&gt;&gt;&gt; a = [1, 2, 3]<br />
&gt;&gt;&gt; b = [4, 5, 6]<br />
&gt;&gt;&gt; c = a + b<br />
&gt;&gt;&gt; print c<br />
[1, 2, 3, 4, 5, 6]</p>
<p>L&#8217;operatore * ripete una lista un dato numero di volte:</p>
<p>&gt;&gt;&gt; [0] * 4<br />
[0, 0, 0, 0]<br />
&gt;&gt;&gt; [1, 2, 3] * 3<br />
[1, 2, 3, 1, 2, 3, 1, 2, 3]</p>
<p>Nel primo esempio abbiamo ripetuto [0] quattro volte. Nel secondo<br />
abbiamo ripetuto la lista [1, 2, 3] tre volte.</p>
<p>8.7 Porzioni di liste</p>
<p>Le porzioni che abbiamo già visto alla sezione 7.4 lavorano anche con<br />
le liste:</p>
<p>&gt;&gt;&gt; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&gt;&gt;&gt; Lista[1:3]<br />
['b', 'c']<br />
&gt;&gt;&gt; Lista[:4]<br />
['a', 'b', 'c', 'd']<br />
&gt;&gt;&gt; Lista[3:]<br />
['d', 'e', 'f']<br />
&gt;&gt;&gt; Lista[:]<br />
['a', 'b', 'c', 'd', 'e', 'f']</p>
<p>8.8 Le liste sono mutabili</p>
<p>A differenza delle stringhe le liste sono mutabili e ciò significa che<br />
gli elementi possono essere modificati. Usando l&#8217;operatore porzione<br />
nella parte sinistra dell&#8217;assegnazione possiamo aggiornare un<br />
elemento:</p>
<p>&gt;&gt;&gt; Frutta = ["banana", "mela", "susina"]<br />
&gt;&gt;&gt; Frutta[0] = &#8220;pera&#8221;<br />
&gt;&gt;&gt; Frutta[-1] = &#8220;arancia&#8221;<br />
&gt;&gt;&gt; print Frutta<br />
['pera', 'mela', 'arancia']</p>
<p>Con l&#8217;operatore porzione possiamo modificare più elementi alla volta:</p>
<p>&gt;&gt;&gt; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&gt;&gt;&gt; Lista[1:3] = ['x', 'y']<br />
&gt;&gt;&gt; print Lista<br />
['a', 'x', 'y', 'd', 'e', 'f']</p>
<p>Possiamo rimuovere elementi da una lista assegnando loro una lista<br />
vuota:</p>
<p>&gt;&gt;&gt; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&gt;&gt;&gt; Lista[1:3] = []<br />
&gt;&gt;&gt; print Lista<br />
['a', 'd', 'e', 'f']</p>
<p>Possono essere aggiunti elementi ad una lista inserendoli in una<br />
porzione vuota nella posizione desiderata:</p>
<p>&gt;&gt;&gt; Lista = ['a', 'd', 'f']<br />
&gt;&gt;&gt; Lista[1:1] = ['b', 'c']<br />
&gt;&gt;&gt; print Lista<br />
['a', 'b', 'c', 'd', 'f']<br />
&gt;&gt;&gt; Lista[4:4] = ['e']<br />
&gt;&gt;&gt; print Lista<br />
['a', 'b', 'c', 'd', 'e', 'f']</p>
<p>8.9 Cancellazione di liste</p>
<p>Usare le porzioni per cancellare elementi delle liste non è poi così<br />
pratico ed è facile sbagliare. Python fornisce un&#8217;alternativa molto<br />
più leggibile.</p>
<p>del rimuove un elemento da una lista:</p>
<p>&gt;&gt;&gt; a = ['uno', 'due', 'tre']<br />
&gt;&gt;&gt; del a[1]<br />
&gt;&gt;&gt; a<br />
['uno', 'tre']</p>
<p>Come puoi facilmente immaginare del gestisce anche gli indici negativi<br />
e avvisa con messaggio d&#8217;errore se l&#8217;indice è al di fuori dei limiti<br />
ammessi.</p>
<p>Puoi usare una porzione come indice di del:</p>
<p>&gt;&gt;&gt; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&gt;&gt;&gt; del Lista[1:5]<br />
&gt;&gt;&gt; print Lista<br />
['a', 'f']</p>
<p>Come abbiamo già visto la porzione indica tutti gli elementi a partire<br />
dal primo indice incluso fino al secondo indice escluso.</p>
<p>8.10 Oggetti e valori</p>
<p>Se eseguiamo queste istruzioni</p>
<p>a = &#8220;banana&#8221;<br />
b = &#8220;banana&#8221;</p>
<p>sappiamo che sia a che b si riferiscono ad una stringa contenente le<br />
lettere &#8220;banana&#8221;. A prima vista non possiamo dire se puntano alla<br />
stessa stringa in memoria.</p>
<p>I possibili casi sono due:</p>
<p>[i_list1.png]</p>
<p>Nel primo caso a e b si riferiscono a due diverse &#8220;cose&#8221; che hanno lo<br />
stesso valore. Nel secondo caso si riferiscono alla stessa &#8220;cosa&#8221;.<br />
Queste &#8220;cose&#8221; hanno un nome: oggetti. Un oggetto è un qualcosa cui può<br />
far riferimento una variabile.</p>
<p>Ogni oggetto ha un identificatore unico che possiamo ricavare con la<br />
funzione id. Stampando l&#8217;identificatore di a e di b possiamo dire<br />
subito se le due variabili si riferiscono allo stesso oggetto:</p>
<p>&gt;&gt;&gt; id(a)<br />
135044008<br />
&gt;&gt;&gt; id(b)<br />
135044008</p>
<p>Otteniamo lo stesso identificatore e ciò significa che Python ha<br />
creato in memoria un&#8217;unica stringa cui fanno riferimento entrambe le<br />
variabili a e b.</p>
<p>In questo ambito le liste si comportano diversamente dalle stringhe,<br />
dato che quando creiamo due liste queste sono sempre oggetti diversi:</p>
<p>&gt;&gt;&gt; a = [1, 2, 3]<br />
&gt;&gt;&gt; b = [1, 2, 3]<br />
&gt;&gt;&gt; id(a)<br />
135045528<br />
&gt;&gt;&gt; id(b)<br />
135041704</p>
<p>Il diagramma di stato in questo caso è</p>
<p>[i_list2.png]</p>
<p>a e b hanno lo stesso valore ma non si riferiscono allo stesso<br />
oggetto.</p>
<p>8.11 Alias</p>
<p>Dato che le variabili si riferiscono ad oggetti quando assegniamo una<br />
variabile ad un&#8217;altra entrambe le variabili si riferiscono allo stesso<br />
oggetto:</p>
<p>&gt;&gt;&gt; a = [1, 2, 3]<br />
&gt;&gt;&gt; b = a</p>
<p>In questo caso il diagramma di stato è</p>
<p>[i_list3.png]</p>
<p>La stessa lista in questo caso ha due nomi differenti, a e b, e<br />
diciamo che questi sono due alias. Dato che l&#8217;oggetto cui entrambi si<br />
riferiscono è lo stesso è indifferente quale degli alias si usi per<br />
effettuare un&#8217;elaborazione:</p>
<p>&gt;&gt;&gt; b[0] = 5<br />
&gt;&gt;&gt; print a<br />
[5, 2, 3]</p>
<p>Sebbene questo comportamento possa essere desiderabile è nella maggior<br />
parte dei casi difficilmente controllabile e può portare a effetti<br />
indesiderati e inattesi. In generale è buona norma evitare gli alias<br />
in caso di oggetti mutabili, mentre per quelli immutabili non ci sono<br />
problemi. Ecco perché Python si permette di usare la stessa stringa<br />
con diversi alias quando si tratta di risparmiare memoria senza che<br />
questo fatto causi alcun problema. La stringa è un oggetto immutabile<br />
e quindi non può essere modificata: non c&#8217;è quindi il rischio di<br />
causare spiacevoli effetti collaterali.</p>
<p>8.12 Clonare le liste</p>
<p>Se vogliamo modificare una lista e mantenere una copia dell&#8217;originale<br />
dobbiamo essere in grado di copiare il contenuto della lista e non<br />
solo di creare un suo alias. Questo processo è talvolta chiamato<br />
clonazione per evitare l&#8217;ambiguità insita nella parola &#8220;copia&#8221;.</p>
<p>Il modo più semplice per clonare una lista è quello di usare<br />
l&#8217;operatore porzione:</p>
<p>&gt;&gt;&gt; a = [1, 2, 3]<br />
&gt;&gt;&gt; b = a[:]<br />
&gt;&gt;&gt; print b<br />
[1, 2, 3]</p>
<p>Il fatto di prendere una porzione di a crea una nuova lista. In questo<br />
caso la porzione consiste degli elementi dell&#8217;intera lista, dato che<br />
non sono stati specificati gli indici iniziale e finale.</p>
<p>Ora siamo liberi di modificare b senza doverci preoccupare di a:</p>
<p>&gt;&gt;&gt; b[0] = 5<br />
&gt;&gt;&gt; print a<br />
[1, 2, 3]</p>
<p>Esercizio: disegna un diagramma di stato per a e per b prima e dopo<br />
questa modifica.</p>
<p>8.13 Parametri di tipo lista</p>
<p>Se passiamo una lista come parametro di funzione in realtà passiamo un<br />
suo riferimento e non una sua copia. Per esempio la funzione Testa<br />
prende una lista come parametro e ne ritorna il primo elemento:</p>
<p>def Testa(Lista):<br />
return Lista[0]</p>
<p>Ecco com&#8217;è usata:</p>
<p>&gt;&gt;&gt; Numeri = [1, 2, 3]<br />
&gt;&gt;&gt; Testa(Numeri)<br />
1</p>
<p>Il parametro Lista e la variabile Numeri sono alias dello stesso<br />
oggetto. Il loro diagramma di stato è</p>
<p>[i_stack5.png]</p>
<p>Dato che l&#8217;oggetto lista è condiviso da due frame l&#8217;abbiamo disegnato<br />
a cavallo di entrambi.</p>
<p>Se una funzione modifica una lista passata come parametro, viene<br />
modificata la lista stessa e non una sua copia. Per esempio<br />
CancellaTesta rimuove il primo elemento da una lista:</p>
<p>def CancellaTesta(Lista):<br />
del Lista[0]</p>
<p>Ecco com&#8217;è usata CancellaTesta:</p>
<p>&gt;&gt;&gt; Numeri = [1, 2, 3]<br />
&gt;&gt;&gt; CancellaTesta(Numeri)<br />
&gt;&gt;&gt; print Numeri<br />
[2, 3]</p>
<p>Quando una funzione ritorna una lista in realtà viene ritornato un<br />
riferimento alla lista stessa. Per esempio Coda ritorna una lista che<br />
contiene tutti gli elementi di una lista a parte il primo:</p>
<p>def Coda(Lista):<br />
return Lista[1:]</p>
<p>Ecco com&#8217;è usata Coda:</p>
<p>&gt;&gt;&gt; Numeri = [1, 2, 3]<br />
&gt;&gt;&gt; Resto = Coda(Numeri)<br />
&gt;&gt;&gt; print Resto<br />
[2, 3]</p>
<p>Dato che il valore ritornato è stato creato con l&#8217;operatore porzione<br />
stiamo restituendo una nuova lista. La creazione di Resto ed ogni suo<br />
successivo cambiamento non ha alcun effetto sulla lista originale<br />
Numeri.</p>
<p>8.14 Liste annidate</p>
<p>Una lista annidata è una lista che compare come elemento di un&#8217;altra<br />
lista. Nell&#8217;esempio seguente il quarto elemento della lista (ricorda<br />
che stiamo parlando dell&#8217;elemento numero 3 dato che il primo ha indice<br />
0) è una lista:</p>
<p>&gt;&gt;&gt; Lista = ["ciao", 2.0, 5, [10, 20]]</p>
<p>Se stampiamo Lista[3] otteniamo [10, 20]. Per estrarre un elemento da<br />
una lista annidata possiamo procedere in due tempi:</p>
<p>&gt;&gt;&gt; Elemento = Lista[3]<br />
&gt;&gt;&gt; Elemento[0]<br />
10</p>
<p>O possiamo combinare i due passi in un&#8217;unica istruzione:</p>
<p>&gt;&gt;&gt; Lista[3][0]<br />
10</p>
<p>L&#8217;operatore porzione viene valutato da sinistra verso destra così<br />
questa espressione ricava il quarto elemento (indice 3) di Lista ed<br />
essendo questo una lista ne estrae il primo elemento (indice 0).</p>
<p>8.15 Matrici</p>
<p>Le liste annidate sono spesso usate per rappresentare matrici. Per<br />
esempio la matrice</p>
<p>[matrix.png]</p>
<p>può essere rappresentata come</p>
<p>&gt;&gt;&gt; Matrice = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]</p>
<p>Matrice è una lista di tre elementi dove ciascuno è una riga della<br />
matrice. Possiamo selezionare una singola riga nel solito modo:</p>
<p>&gt;&gt;&gt; Matrice[1]<br />
[4, 5, 6]</p>
<p>O estrarre una singola cella usando il doppio indice:</p>
<p>&gt;&gt;&gt; Matrice[1][1]<br />
5</p>
<p>Il primo indice seleziona la riga ed il secondo la colonna. Questo è<br />
un modo comune di rappresentare le matrici ma non è l&#8217;unico: una<br />
variante è quella di usare una lista di colonne invece che di righe.<br />
Vedremo in seguito un&#8217;alternativa completamente diversa quando avremo<br />
visto i dizionari.</p>
<p>8.16 Stringhe e liste</p>
<p>Due delle funzioni più utili nel modulo string hanno a che fare con le<br />
liste di stringhe. La funzione split spezza una stringa in una lista<br />
di parole singole, considerando un qualsiasi carattere di spazio<br />
bianco come punto di interruzione tra parole consecutive:</p>
<p>&gt;&gt;&gt; import string<br />
&gt;&gt;&gt; Verso = &#8220;Nel mezzo del cammin&#8230;&#8221;<br />
&gt;&gt;&gt; string.split(Verso)<br />
['Nel', 'mezzo', 'del', 'cammin...']</p>
<p>Può anche essere usato un argomento opzionale per specificare quale<br />
debba essere il delimitatore da considerare. In questo esempio usiamo<br />
la stringa el come delimitatore:</p>
<p>&gt;&gt;&gt; string.split(Verso, &#8216;el&#8217;)<br />
['N', ' mezzo d', ' cammin...']</p>
<p>Il delimitatore non appare nella lista.</p>
<p>La funzione join si comporta in modo inverso rispetto a split: prende<br />
una lista di stringhe e ne concatena gli elementi inserendo uno spazio<br />
tra ogni coppia:</p>
<p>&gt;&gt;&gt; Lista = ['Nel', 'mezzo', 'del', 'cammin...']<br />
&gt;&gt;&gt; string.join(Lista)<br />
&#8216;Nel mezzo del cammin&#8230;&#8217;</p>
<p>Come nel caso di split, join accetta un argomento opzionale che<br />
rappresenta il delimitatore da inserire tra gli elementi. Il<br />
delimitatore di default è uno spazio ma può essere cambiato:</p>
<p>&gt;&gt;&gt; string.join(Lista, &#8216;_&#8217;)<br />
&#8216;Nel_mezzo_del_cammin&#8230;&#8217;</p>
<p>Esercizio: descrivi la relazione tra la lista Verso e cosa ottieni<br />
da string.join(string.split(Verso)). Sono le stesse per tutte le<br />
stringhe o in qualche caso possono essere diverse?</p>
<p>8.17 Glossario</p>
<p>Lista<br />
collezione di oggetti identificata da un nome dove ogni oggetto<br />
è selezionabile grazie ad un indice.</p>
<p>Indice<br />
variabile intera o valore che indica un elemento all&#8217;interno di<br />
una lista.</p>
<p>Elemento<br />
valore in una lista (o in altri tipi di sequenza). L&#8217;operatore<br />
porzione seleziona gli elementi di una lista.</p>
<p>Sequenza<br />
ognuno dei tipi di dati che consiste in una lista ordinata di<br />
elementi identificati da un indice.</p>
<p>Lista annidata<br />
lista che è un elemento di un&#8217;altra lista.</p>
<p>Attraversamento di una lista<br />
accesso in sequenza di tutti gli elementi di una lista.</p>
<p>Oggetto<br />
zona di memoria cui si può riferire una variabile.</p>
<p>Alias<br />
più variabili che si riferiscono allo stesso oggetto con nomi<br />
diversi.</p>
<p>Clonare<br />
creare un nuovo oggetto che ha lo stesso valore di un oggetto<br />
già esistente.</p>
<p>Delimitatore<br />
carattere o stringa usati per indicare dove una stringa deve<br />
essere spezzata.</p>
<p>Capitolo 9</p>
<p>Tuple</p>
<p>9.1 Mutabilità e tuple</p>
<p>Finora hai visto due tipi composti: le stringhe (sequenze di<br />
caratteri) e le liste (sequenze di elementi di tipo qualsiasi). Una<br />
delle differenze che abbiamo notato è che le gli elementi di una lista<br />
possono essere modificati, mentre non possono essere alterati i<br />
caratteri in una stringa: le stringhe sono infatti immutabili mentre<br />
le liste sono mutabili.</p>
<p>C&#8217;è un altro tipo di dati in Python, simile alla lista eccetto per il<br />
fatto che è immutabile: la tupla. La tupla è una lista di valori<br />
separati da virgole:</p>
<p>&gt;&gt;&gt; tupla = &#8216;a&#8217;, &#8216;b&#8217;, &#8216;c&#8217;, &#8216;d&#8217;, &#8216;e&#8217;</p>
<p>Sebbene non sia necessario, è convenzione racchiudere le tuple tra<br />
parentesi tonde per ragioni di chiarezza:</p>
<p>&gt;&gt;&gt; tupla = (&#8216;a&#8217;, &#8216;b&#8217;, &#8216;c&#8217;, &#8216;d&#8217;, &#8216;e&#8217;)</p>
<p>Per creare una tupla con un singolo elemento dobbiamo aggiungere la<br />
virgola finale dopo l&#8217;elemento:</p>
<p>&gt;&gt;&gt; t1 = (&#8216;a&#8217;,)<br />
&gt;&gt;&gt; type(t1)<br />
&lt;type &#8216;tuple&#8217;&gt;</p>
<p>Senza la virgola, infatti, Python tratterebbe (&#8216;a&#8217;) come una stringa<br />
tra parentesi:</p>
<p>&gt;&gt;&gt; t2 = (&#8216;a&#8217;)<br />
&gt;&gt;&gt; type(t2)<br />
&lt;type &#8216;string&#8217;&gt;</p>
<p>Sintassi a parte le operazioni sulle tuple sono identiche a quelle<br />
sulle liste. L&#8217;operatore indice seleziona un elemento dalla tupla:</p>
<p>&gt;&gt;&gt; tupla = (&#8216;a&#8217;, &#8216;b&#8217;, &#8216;c&#8217;, &#8216;d&#8217;, &#8216;e&#8217;)<br />
&gt;&gt;&gt; tupla[0]<br />
&#8216;a&#8217;</p>
<p>e l&#8217;operatore porzione seleziona una serie di elementi consecutivi:</p>
<p>&gt;&gt;&gt; tupla[1:3]<br />
(&#8216;b&#8217;, &#8216;c&#8217;)</p>
<p>A differenza delle liste se cerchiamo di modificare gli elementi di<br />
una tupla otteniamo un messaggio d&#8217;errore:</p>
<p>&gt;&gt;&gt; tupla[0] = &#8216;A&#8217;<br />
TypeError: object doesn&#8217;t support item assignment</p>
<p>Naturalmente anche se non possiamo modificare gli elementi di una<br />
tupla possiamo sempre rimpiazzarla con una sua copia modificata:</p>
<p>&gt;&gt;&gt; tupla = (&#8216;A&#8217;,) + tupla[1:]<br />
&gt;&gt;&gt; tupla<br />
(&#8216;A&#8217;, &#8216;b&#8217;, &#8216;c&#8217;, &#8216;d&#8217;, &#8216;e&#8217;)</p>
<p>9.2 Assegnazione di tuple</p>
<p>Di tanto in tanto può essere necessario scambiare i valori di due<br />
variabili. Con le istruzioni di assegnazione convenzionali dobbiamo<br />
usare una variabile temporanea. Per esempio per scambiare a e b:</p>
<p>&gt;&gt;&gt; temp = a<br />
&gt;&gt;&gt; a = b<br />
&gt;&gt;&gt; b = temp</p>
<p>Questo approccio è poco intuitivo e l&#8217;uso dell&#8217;assegnazione di tuple<br />
lo rende decisamente più comprensibile:</p>
<p>&gt;&gt;&gt; a, b = b, a</p>
<p>La parte sinistra dell&#8217;assegnazione è una tupla di variabili; la parte<br />
destra una tupla di valori. Ogni valore è assegnato alla rispettiva<br />
variabile. Tutte le espressioni sulla destra sono valutate prima delle<br />
assegnazioni. Questa caratteristica rende le tuple estremamente<br />
versatili.</p>
<p>Ovviamente il numero di variabili sulla sinistra deve corrispondere al<br />
numero di valori sulla destra:</p>
<p>&gt;&gt;&gt; a, b, c, d = 1, 2, 3<br />
ValueError: unpack tuple of wrong size</p>
<p>9.3 Tuple come valori di ritorno</p>
<p>Le funzioni possono ritornare tuple. Per fare un esempio possiamo<br />
scrivere una funzione che scambia due valori:</p>
<p>def Scambia(x, y):<br />
return y, x</p>
<p>e in seguito possiamo assegnare il valore di ritorno della funzione ad<br />
una tupla di due variabili:</p>
<p>a, b = Scambia(a, b)</p>
<p>In questo caso non c&#8217;è una grande utilità nel rendere Scambia una<br />
funzione. Anzi occorre stare attenti ad uno dei pericoli insiti<br />
nell&#8217;incapsulamento di Scambia:</p>
<p>def Scambia(x, y):      # versione non corretta<br />
x, y = y, x</p>
<p>Se chiamiamo la funzione con:</p>
<p>Scambia(a, b)</p>
<p>apparentemente tutto sembra corretto, ma quando controlliamo i valori<br />
di a e b prima e dopo lo &#8220;scambio&#8221; in realtà ci accorgiamo che questi<br />
non sono cambiati. Perché? Perché quando chiamiamo questa funzione non<br />
vengono passate le variabili a e b come argomenti, ma i loro valori.<br />
Questi valori vengono assegnati a x e y; al termine della funzione,<br />
quando x e y vengono rimosse perché variabili locali, qualsiasi valore<br />
in esse contenuto viene irrimediabilmente perso.</p>
<p>Questa funzione non produce messaggi d&#8217;errore ma ciononostante non fa<br />
ciò che noi volevamo farle fare: questo è un esempio di errore di<br />
semantica.</p>
<p>Esercizio: disegna il diagramma di stato della funzione Scambia<br />
così da capire perché non funziona.</p>
<p>9.4 Numeri casuali</p>
<p>La maggior parte dei programmi fanno la stessa cosa ogni volta che<br />
vengono eseguiti e sono detti per questo deterministici. Di solito un<br />
programma deterministico è una cosa voluta in quanto a parità di dati<br />
in ingresso ci attendiamo lo stesso risultato. Per alcune<br />
applicazioni, invece, abbiamo bisogno che l&#8217;esecuzione sia<br />
imprevedibile: i videogiochi sono un esempio lampante, ma ce ne sono<br />
tanti altri.</p>
<p>Creare un programma realmente non deterministico (e quindi<br />
imprevedibile) è una cosa piuttosto difficile, ma ci sono dei sistemi<br />
per renderlo abbastanza casuale da soddisfare la maggior parte delle<br />
esigenze in tal senso. Uno dei sistemi è quello di generare dei numeri<br />
casuali ed usarli per determinare i risultati prodotti dal programma.<br />
Python fornisce delle funzioni di base che generano numeri<br />
pseudocasuali: questi numeri non sono realmente casuali in senso<br />
matematico ma per i nostri scopi saranno più che sufficienti.</p>
<p>Il modulo random contiene una funzione chiamata random che restituisce<br />
un numero in virgola mobile compreso tra 0.0 (compreso) e 1.0<br />
(escluso). Ad ogni chiamata di random si ottiene il numero seguente di<br />
una lunga serie di numeri pseudocasuali. Per vedere un esempio prova<br />
ad eseguire questo ciclo:</p>
<p>import random<br />
for i in range(10):<br />
x = random.random()<br />
print x</p>
<p>Per generare un numero casuale (lo chiameremo così d&#8217;ora in poi, anche<br />
se è sottinteso che la casualità ottenuta non è assoluta) compreso tra<br />
0.0 (compreso) ed un limite superiore Limite (escluso) moltiplica x<br />
per Limite.</p>
<p>Esercizio: tenta di generare un numero casuale compreso tra il<br />
\linebreak LimiteInferiore (compreso) ed il LimiteSuperiore<br />
(escluso).</p>
<p>Esercizio addizionale: genera un numero intero compreso tra il<br />
\linebreak LimiteInferiore ed il LimiteSuperiore comprendendo<br />
entrambi questi limiti.</p>
<p>9.5 Lista di numeri casuali</p>
<p>Proviamo a scrivere un programma che usa i numeri casuali, iniziando<br />
con la costruzione di una lista di questi numeri. ListaCasuale prende<br />
un parametro intero Lungh e ritorna una lista di questa lunghezza<br />
composta di numeri casuali. Iniziamo con una lista di Lungh zeri e<br />
sostituiamo in un ciclo un elemento alla volta con un numero casuale:</p>
<p>def ListaCasuale(Lungh):<br />
s = [0] * Lungh<br />
for i in range(Lungh):<br />
s[i] = random.random()<br />
return s</p>
<p>Testiamo la funzione generando una lista di otto elementi: per poter<br />
controllare i programmi è sempre bene partire con insiemi di dati<br />
molto piccoli.</p>
<p>&gt;&gt;&gt; ListaCasuale(8)<br />
[0.11421081445000203, 0.38367479346590505, 0.16056841528993915,<br />
0.29204721527340882, 0.75201663462563095, 0.31790165552578986,<br />
0.43858231029411354, 0.27749268689939965]</p>
<p>I numeri casuali generati da random si ritengono distribuiti<br />
uniformemente tanto che ogni valore è egualmente probabile.</p>
<p>Se dividiamo la gamma dei valori generati in intervalli della stessa<br />
grandezza e contiamo il numero di valori casuali che cadono in ciascun<br />
intervallo dovremmo ottenere, approssimativamente, la stessa cifra in<br />
ciascuno, sempre che l&#8217;esperimento sia effettuato un buon numero di<br />
volte.</p>
<p>Possiamo testare questa affermazione scrivendo un programma per<br />
dividere la gamma dei valori in intervalli e contare il numero di<br />
valori in ciascuno di essi.</p>
<p>9.6 Conteggio</p>
<p>Un buon approccio a questo tipo di problemi è quello di dividere il<br />
problema in sottoproblemi e applicare a ciascun sottoproblema uno<br />
schema di soluzione già visto in precedenza.</p>
<p>In questo caso vogliamo attraversare una lista di numeri e contare il<br />
numero di volte in cui un valore cade in un determinato intervallo.<br />
Questo suona familiare: nella sezione 7.8 abbiamo già scritto un<br />
programma che attraversa una stringa e conta il numero di volte in cui<br />
appare una determinata lettera. Possiamo allora copiare il vecchio<br />
programma e adattarlo al problema corrente. Il programma originale<br />
era:</p>
<p>Conteggio = 0<br />
for Carattere in Frutto:<br />
if Carattere == &#8216;a&#8217;:<br />
Conteggio = Conteggio + 1<br />
print Conteggio</p>
<p>Il primo passo è quello di sostituire Frutto con Lista e Carattere con<br />
Numero. Questo non cambia il programma ma semplicemente lo rende più<br />
leggibile.</p>
<p>Il secondo passo è quello di cambiare la condizione dato che siamo<br />
interessati a verificare se Numero cade tra LimiteInferiore e<br />
LimiteSuperiore.</p>
<p>Conteggio = 0<br />
for Numero in Lista<br />
if LimiteInferiore &lt; Numero &lt; LimiteSuperiore:<br />
Conteggio = Conteggio + 1<br />
print Conteggio</p>
<p>L&#8217;ultimo passo è quello di incapsulare questo codice in una funzione<br />
chiamata NellIntervallo. I parametri della funzione sono la lista da<br />
controllare ed i valori LimiteInferiore and LimiteSuperiore:</p>
<p>def NellIntervallo(Lista, LimiteInferiore, LimiteSuperiore):<br />
Conteggio = 0<br />
for Numero in Lista:<br />
if LimiteInferiore &lt; Numero &lt; LimiteSuperiore:<br />
Conteggio = Conteggio + 1<br />
return Conteggio</p>
<p>Copiando e modificando un programma esistente siamo stati capaci di<br />
scrivere questa funzione velocemente risparmiando un bel po&#8217; di tempo<br />
di test. Questo tipo di piano di sviluppo è chiamato pattern matching:<br />
se devi cercare una soluzione a un problema che hai già risolto riusa<br />
una soluzione che avevi già trovato, modificandola per adattarla quel<br />
tanto che serve in base alle nuove circostanze.</p>
<p>9.7 Aumentare il numero degli intervalli</p>
<p>A mano a mano che il numero degli intervalli cresce NellIntervallo<br />
diventa poco pratica da gestire. Con due soli intervalli ce la caviamo<br />
ancora bene:</p>
<p>Intervallo1 = NellIntervallo(a, 0.0, 0.5)<br />
Intervallo2 = NellIntervallo(a, 0.5, 1.0)</p>
<p>ma con quattro intervalli è facile commettere errori sia nel calcolo<br />
dei limiti sia nella battitura dei numeri:</p>
<p>Intervallo1 = NellIntervallo(a, 0.0, 0.25)<br />
Intervallo2 = NellIntervallo(a, 0.25, 0.5)<br />
Intervallo3 = NellIntervallo(a, 0.5, 0.75)<br />
Intervallo4 = NellIntervallo(a, 0.75, 1.0)</p>
<p>Ci sono due ordini di problemi: il primo è che dobbiamo creare un nome<br />
di variabile per ciascun risultato; il secondo è che dobbiamo<br />
calcolare a mano i limiti inferiore e superiore per ciascun intervallo<br />
prima di chiamare la funzione.</p>
<p>Risolveremo innanzitutto questo secondo problema: se il numero degli<br />
intervalli che vogliamo considerare è NumIntervalli allora l&#8217;ampiezza<br />
di ogni intervallo è 1.0 / NumIntervalli.</p>
<p>Possiamo usare un ciclo per calcolare i limiti inferiore e superiore<br />
per ciascun intervallo, usando i come indice del ciclo da 0 a<br />
NumIntervalli-1:</p>
<p>AmpiezzaIntervallo = 1.0 / NumIntervalli<br />
for i in range(NumIntervalli):<br />
LimiteInferiore = i * AmpiezzaIntervallo<br />
LimiteSuperiore = LimiteInferiore + AmpiezzaIntervallo<br />
print &#8220;da&#8221;, LimiteInferiore, &#8220;a&#8221;, LimiteSuperiore</p>
<p>Per calcolare il limite inferiore di ogni intervallo abbiamo<br />
moltiplicato l&#8217;indice del ciclo per l&#8217;ampiezza di ciascun intervallo;<br />
per ottenere il limite superiore abbiamo sommato al limite inferiore<br />
la stessa ampiezza.</p>
<p>Con NumIntervalli = 8 il risultato è:</p>
<p>da 0.0 a 0.125<br />
da 0.125 a 0.25<br />
da 0.25 a 0.375<br />
da 0.375 a 0.5<br />
da 0.5 a 0.625<br />
da 0.625 a 0.75<br />
da 0.75 a 0.875<br />
da 0.875 a 1.0</p>
<p>Puoi vedere come ogni intervallo sia della stessa ampiezza, come tutta<br />
la gamma da 0.0 a 1.0 sia presente e come non ci siano intervalli che<br />
si sovrappongono.</p>
<p>Ora torniamo al primo problema: abbiamo bisogno di memorizzare 8<br />
interi senza dover creare variabili distinte. Le liste ci vengono in<br />
aiuto e l&#8217;indice del ciclo sembra essere un ottimo sistema per<br />
selezionare di volta in volta un elemento della lista.</p>
<p>Creiamo la lista dei conteggi all&#8217;esterno del ciclo dato che la<br />
dobbiamo creare una sola volta (e non ad ogni ciclo). All&#8217;interno del<br />
ciclo chiameremo ripetutamente NellIntervallo e aggiorneremo l&#8217;i-esimo<br />
elemento della lista dei conteggi:</p>
<p>NumIntervalli = 8<br />
Conteggio = [0] * NumIntervalli<br />
AmpiezzaIntervallo = 1.0 / NumIntervalli<br />
for i in range(NumIntervalli):<br />
LimiteInferiore = i * AmpiezzaIntervallo<br />
LimiteSuperiore = LimiteInferiore + AmpiezzaIntervallo<br />
Conteggio[i] = NellIntervallo(Lista, LimiteInferiore, \<br />
LimiteSuperiore)<br />
print Conteggio</p>
<p>Con una lista di 1000 valori questo programma produce una lista di<br />
conteggi di questo tipo:</p>
<p>[138, 124, 128, 118, 130, 117, 114, 131]</p>
<p>Ci aspettavamo per ogni intervallo un valore medio di 125 (1000 numeri<br />
divisi per 8 intervalli) ed in effetti ci siamo andati abbastanza<br />
vicini da poter affermare che il generatore di numeri casuali si<br />
comporta in modo sufficientemente realistico.</p>
<p>Esercizio: prova questa funzione con liste più lunghe per vedere se<br />
il conteggio di valori in ogni intervallo tende o meno a livellarsi<br />
(maggiore è il numero di prove più i valori dovrebbero diventare<br />
simili).</p>
<p>9.8 Una soluzione in una sola passata</p>
<p>Anche se il programma funziona correttamente non è ancora<br />
sufficientemente efficiente. Ogni volta che il ciclo chiama<br />
NellIntervallo viene attraversata l&#8217;intera lista. A mano a mano che il<br />
numero di intervalli cresce questo implica un gran numero di<br />
attraversamenti di liste.</p>
<p>Sarebbe meglio riuscire a fare un singolo attraversamento della lista<br />
ed elaborare direttamente in quale intervallo cade ogni elemento,<br />
incrementando il contatore opportuno.</p>
<p>Nella sezione precedente abbiamo preso un indice i e lo abbiamo<br />
moltiplicato per AmpiezzaIntervallo per trovare il limite inferiore di<br />
un determinato intervallo. Quello che vogliamo fare ora è ricavare<br />
direttamente l&#8217;indice dell&#8217;intervallo cui un valore appartiene.</p>
<p>Questo problema è esattamente l&#8217;inverso del precedente: dobbiamo<br />
indovinare in quale intervallo cade un valore dividendo quest&#8217;ultimo<br />
per AmpiezzaIntervallo invece di moltiplicare un indice per<br />
AmpiezzaIntervallo.</p>
<p>Dal momento che AmpiezzaIntervallo = 1.0 / NumIntervalli, dividere per<br />
AmpiezzaIntervallo è lo stesso di moltiplicare per NumIntervalli. Se<br />
moltiplichiamo un numero nella gamma da 0.0 a 1.0 per NumIntervalli<br />
otteniamo un numero compreso tra 0.0 e NumIntervalli. Se arrotondiamo<br />
questo risultato all&#8217;intero inferiore otteniamo proprio quello che<br />
stavamo cercando: l&#8217;indice dell&#8217;intervallo dove cade il valore.</p>
<p>NumIntervalli = 8<br />
Conteggio = [0] * NumIntervalli<br />
for i in Lista:<br />
Indice = int(i * NumIntervalli)<br />
Conteggio[Indice] = Conteggio[Indice] + 1</p>
<p>Abbiamo usato la funzione int per convertire un numero in virgola<br />
mobile in un intero.</p>
<p>Esercizio: È possibile per questo calcolo produrre un indice che<br />
sia fuori dalla gamma di numeri ammessa (negativo o più grande di<br />
len(Conteggio)-1)?</p>
<p>Una lista come Conteggi che contiene il numero dei valori per una<br />
serie di intervalli è chiamata istogramma.</p>
<p>Esercizio: scrivi una funzione chiamata Istogramma che prende una<br />
lista ed il numero di intervalli da considerare e ritorna<br />
l&#8217;istogramma della distribuzione dei valori per ciascun intervallo.</p>
<p>9.9 Glossario</p>
<p>Tipo immutabile<br />
tipo in cui i singoli elementi non possono essere modificati.<br />
L&#8217;operazione di assegnazione ad elementi o porzioni produce un<br />
errore.</p>
<p>Tipo mutabile<br />
tipo di dati in cui gli elementi possono essere modificati.<br />
Liste e dizionari sono mutabili; stringhe e tuple non lo sono.</p>
<p>Tupla<br />
tipo di sequenza simile alla lista con la differenza di essere<br />
immutabile. Le tuple possono essere usate dovunque serva un<br />
tipo immutabile, per esempio come chiave in un dizionario.</p>
<p>Assegnazione ad una tupla<br />
assegnazione di tutti gli elementi della tupla usando un&#8217;unica<br />
istruzione di assegnazione.</p>
<p>Programma deterministico<br />
programma che esegue le stesse operazioni ogni volta che è<br />
eseguito.</p>
<p>Pseudocasuale<br />
sequenza di numeri che sembra essere casuale ma in realtà è il<br />
risultato di un&#8217;elaborazione deterministica.</p>
<p>Istogramma<br />
lista di interi in cui ciascun elemento conta il numero di<br />
volte in cui una determinata condizione si verifica.</p>
<p>Pattern matching<br />
piano di sviluppo del programma che consiste nell&#8217;identificare<br />
un tracciato di elaborazione già visto e modificarlo per<br />
ottenere la soluzione di un problema simile.</p>
<p>Capitolo 10</p>
<p>Dizionari</p>
<p>I tipi di dati composti che abbiamo visto finora (stringhe, liste e<br />
tuple) usano gli interi come indici. Qualsiasi tentativo di usare<br />
altri tipi di dati produce un errore.</p>
<p>I dizionari sono simili agli altri tipi composti ma si differenziano<br />
per il fatto di poter usare qualsiasi tipo di dato immutabile come<br />
indice. Se desideriamo creare un dizionario per la traduzione di<br />
parole dall&#8217;inglese all&#8217;italiano è utile poter usare la parola inglese<br />
come indice di ricerca della corrispondente italiana. Gli indici usati<br />
sono in questo caso delle stringhe.</p>
<p>Un modo per creare un dizionario è partire con un dizionario vuoto e<br />
aggiungere via via gli elementi. Il dizionario vuoto è indicato da {}:</p>
<p>&gt;&gt;&gt; Eng2Ita = {}<br />
&gt;&gt;&gt; Eng2Ita['one'] = &#8216;uno&#8217;<br />
&gt;&gt;&gt; Eng2Ita['two'] = &#8216;due&#8217;</p>
<p>La prima assegnazione crea un dizionario chiamato Eng2Ita; le altre<br />
istruzioni aggiungono elementi al dizionario. Possiamo stampare il<br />
valore del dizionario nel solito modo:</p>
<p>&gt;&gt;&gt; print Eng2Ita<br />
{&#8216;one&#8217;: &#8216;uno&#8217;, &#8216;two&#8217;: &#8216;due&#8217;}</p>
<p>Gli elementi di un dizionario appaiono in una sequenza separata da<br />
virgole. Ogni voce contiene un indice ed il corrispondente valore<br />
separati da due punti. In un dizionario gli indici sono chiamati<br />
chiavi e un elemento è detto coppia chiave-valore.</p>
<p>Un altro modo di creare un dizionario è quello di fornire direttamente<br />
una serie di coppie chiave-valore:</p>
<p>&gt;&gt;&gt; Eng2Ita = {&#8216;one&#8217;: &#8216;uno&#8217;, &#8216;two&#8217;: &#8216;due&#8217;, &#8216;three&#8217;: &#8216;tre&#8217;}</p>
<p>Se stampiamo ancora una volta il valore di Eng2Ita abbiamo una<br />
sorpresa:</p>
<p>&gt;&gt;&gt; print Eng2Ita<br />
{&#8216;one&#8217;: &#8216;uno&#8217;, &#8216;three&#8217;: &#8216;tre&#8217;, &#8216;two&#8217;: &#8216;due&#8217;}</p>
<p>Le coppie chiave-valore non sono in ordine! Per fortuna non c&#8217;è<br />
ragione di conservare l&#8217;ordine di inserimento dato che il dizionario<br />
non fa uso di indici numerici. Per cercare un valore usiamo infatti<br />
una chiave:</p>
<p>&gt;&gt;&gt; print Eng2Ita['two']<br />
&#8216;due&#8217;</p>
<p>La chiave &#8216;two&#8217; produce correttamente &#8216;due&#8217; anche se appare in terza<br />
posizione nella stampa del dizionario.</p>
<p>10.1 Operazioni sui dizionari</p>
<p>L&#8217;istruzione del rimuove una coppia chiave-valore da un dizionario.<br />
Vediamo di fare un esempio pratico creando un dizionario che contiene<br />
il nome di vari tipi di frutta (la chiave) ed il numero di frutti<br />
corrispondenti in magazzino (il valore):</p>
<p>&gt;&gt;&gt; Magazzino = {&#8216;mele&#8217;: 430, &#8216;banane&#8217;: 312, &#8216;arance&#8217;: 525,<br />
&#8216;pere&#8217;: 217}<br />
&gt;&gt;&gt; print Magazzino<br />
{&#8216;banane&#8217;: 312, &#8216;arance&#8217;: 525, &#8216;pere&#8217;: 217, &#8216;mele&#8217;: 430}</p>
<p>Dovessimo togliere la scorta di pere dal magazzino possiamo<br />
direttamente rimuovere la voce dal dizionario:</p>
<p>&gt;&gt;&gt; del Magazzino['pere']<br />
&gt;&gt;&gt; print Magazzino<br />
{&#8216;banane&#8217;: 312, &#8216;arance&#8217;: 525, &#8216;mele&#8217;: 430}</p>
<p>o se intendiamo solo cambiare il numero di pere senza rimuoverne la<br />
voce dal dizionario possiamo cambiare il valore associato:</p>
<p>&gt;&gt;&gt; Magazzino['pere'] = 0<br />
&gt;&gt;&gt; print Magazzino<br />
{&#8216;banane&#8217;: 312, &#8216;arance&#8217;: 525, &#8216;pere&#8217;: 0, &#8216;mele&#8217;: 430}</p>
<p>La funzione len opera anche sui dizionari ritornando il numero di<br />
coppie chiave-valore:</p>
<p>&gt;&gt;&gt; len(Magazzino)<br />
4</p>
<p>10.2 Metodi dei dizionari</p>
<p>Un metodo è simile ad una funzione, visto che prende parametri e<br />
ritorna valori, ma la sintassi di chiamata è diversa. Il metodo keys<br />
prende un dizionario e ritorna la lista delle sue chiavi: invece di<br />
invocarlo con la sintassi delle funzioni keys(Eng2Ita) usiamo la<br />
sintassi dei metodi Eng2Ita.keys():</p>
<p>&gt;&gt;&gt; Eng2Ita.keys()<br />
['one', 'three', 'two']</p>
<p>Questa forma di notazione punto specifica il nome della funzione keys<br />
ed il nome dell&#8217;oggetto cui applicare la funzione Eng2Ita. Le<br />
parentesi vuote indicano che questo metodo non prende parametri.</p>
<p>Una chiamata ad un metodo è detta invocazione; in questo caso diciamo<br />
che stiamo invocando keys sull&#8217;oggetto Eng2Ita.</p>
<p>Il metodo values funziona in modo simile: ritorna la lista dei valori<br />
in un dizionario:</p>
<p>&gt;&gt;&gt; Eng2Ita.values()<br />
['uno', 'tre', 'due']</p>
<p>Il metodo items ritorna entrambi nella forma di una lista di tuple,<br />
una per ogni coppia chiave-valore:</p>
<p>&gt;&gt;&gt; Eng2Ita.items()<br />
[('one','uno'), ('three', 'tre'), ('two', 'due')]</p>
<p>La sintassi fornisce utili informazioni sul tipo ottenuto invocando<br />
items: le parentesi quadrate indicano che si tratta di una lista; le<br />
parentesi tonde che gli elementi della lista sono tuple.</p>
<p>Se un metodo prende un argomento usa la stessa sintassi delle chiamate<br />
di funzioni. Il metodo has_key prende come argomento una chiave e<br />
ritorna vero (1) se la chiave è presente nel dizionario:</p>
<p>&gt;&gt;&gt; Eng2Ita.has_key(&#8216;one&#8217;)<br />
1<br />
&gt;&gt;&gt; End2Ita.has_key(&#8216;deux&#8217;)<br />
0</p>
<p>Se provi a invocare un metodo senza specificare l&#8217;oggetto cui si fa<br />
riferimento ottieni un errore:</p>
<p>&gt;&gt;&gt; has_key(&#8216;one&#8217;)<br />
NameError: has_key</p>
<p>Purtroppo il messaggio d&#8217;errore a volte, come in questo caso, non è<br />
del tutto chiaro: Python cerca di dirci che la funzione has_key non<br />
esiste, dato che con questa sintassi abbiamo chiamato la funzione<br />
has_key e non invocato il metodo has_key dell&#8217;oggetto.</p>
<p>10.3 Alias e copia</p>
<p>Visto che i dizionari sono mutabili devi stare molto attento agli<br />
alias: quando due variabili si riferiscono allo stesso oggetto un<br />
cambio effettuato su una influenza immediatamente il contenuto<br />
dell&#8217;altra.</p>
<p>Se desideri poter modificare un dizionario e mantenere una copia<br />
dell&#8217;originale usa il metodo copy. Per fare un esempio costruiamo un<br />
dizionario Opposti che contiene coppie di parole dal significato<br />
opposto:</p>
<p>&gt;&gt;&gt; Opposti = {&#8216;alto&#8217;: &#8216;basso&#8217;, &#8216;giusto&#8217;: &#8216;sbagliato&#8217;,<br />
&#8216;vero&#8217;: &#8216;falso&#8217;}<br />
&gt;&gt;&gt; Alias = Opposti<br />
&gt;&gt;&gt; Copia = Opposti.copy()</p>
<p>Alias e Opposti si riferiscono allo stesso oggetto; Copia si riferisce<br />
ad una copia del dizionario nuova di zecca. Se modifichiamo Alias,<br />
Opposti viene modificato:</p>
<p>&gt;&gt;&gt; Alias['giusto'] = &#8216;errato&#8217;<br />
&gt;&gt;&gt; Opposti['giusto']<br />
&#8216;errato&#8217;</p>
<p>Opposti resta immutato se modifichiamo Copia:</p>
<p>&gt;&gt;&gt; Copia['giusto'] = &#8216;errato&#8217;<br />
&gt;&gt;&gt; Opposti['giusto']<br />
&#8216;sbagliato&#8217;</p>
<p>10.4 Matrici sparse</p>
<p>Nella sezione 8.15 abbiamo usato una lista di liste per rappresentare<br />
una matrice. Questa è una buona scelta quando si tratta di<br />
rappresentare matrici i cui valori sono in buona parte diversi da<br />
zero, ma c&#8217;è un tipo di matrice detta &#8220;sparsa&#8221; i cui valori sono di<br />
tipo particolare:</p>
<p>[sparse.png]</p>
<p>La rappresentazione sotto forma di lista di questa matrice contiene<br />
molti zeri:</p>
<p>&gt;&gt;&gt; Matrice = [ [0,0,0,1,0],<br />
[0,0,0,0,0],<br />
[0,2,0,0,0],<br />
[0,0,0,0,0],<br />
[0,0,0,3,0] ]</p>
<p>L&#8217;alternativa in questo caso è quella di usare un dizionario, usando<br />
come chiavi tuple composte dalla coppia riga/colonna. Ecco la stessa<br />
matrice rappresentata con un dizionario:</p>
<p>&gt;&gt;&gt; Matrice = {(0,3): 1, (2, 1): 2, (4, 3): 3}</p>
<p>In questo caso abbiamo solo 3 coppie chiave-valore, una per ciascun<br />
elemento diverso da zero nella matrice. Ogni chiave è una tupla ed<br />
ogni valore un intero.</p>
<p>Per l&#8217;accesso ad un elemento della matrice possiamo usare l&#8217;operatore<br />
[]:</p>
<p>&gt;&gt;&gt; Matrice[(0,3)]<br />
1<br />
&gt;&gt;&gt; Matrice[0,3]    # questa sintassi e&#8217; equivalente<br />
1</p>
<p>Nota come la sintassi per la rappresentazione della matrice sotto<br />
forma di dizionario sia diversa da quella della lista di liste: invece<br />
di due valori indice usiamo un unico indice che è una tupla di interi.</p>
<p>C&#8217;è un problema: se cerchiamo un elemento che è pari a zero otteniamo<br />
un errore, dato che non c&#8217;è una voce nel dizionario corrispondente<br />
alla tupla con quelle coordinate:</p>
<p>&gt;&gt;&gt; Matrice[1,3]<br />
KeyError: (1, 3)</p>
<p>Il metodo get risolve il problema:</p>
<p>&gt;&gt;&gt; Matrice.get((0,3), 0)<br />
1</p>
<p>Il primo argomento è la tupla-chiave, il secondo il valore che get<br />
deve ritornare nel caso la chiave non sia presente nel dizionario:</p>
<p>&gt;&gt;&gt; Matrice.get((1,3), 0)<br />
0</p>
<p>Anche se la sintassi di get non è la più intuitiva almeno abbiamo un<br />
modo efficiente per accedere ad una matrice sparsa.</p>
<p>10.5 Suggerimenti</p>
<p>Se hai fatto qualche prova con la funzione di Fibonacci nella sezione<br />
5.7 avrai notato che man mano che l&#8217;argomento passato alla funzione<br />
cresce il tempo trascorso prima di ottenere il risultato aumenta molto<br />
rapidamente. Mentre Fibonacci(20) termina quasi istantaneamente<br />
Fibonacci(30) impiega qualche secondo e Fibonacci(40) impiega un tempo<br />
lunghissimo.</p>
<p>Per comprenderne il motivo consideriamo questo grafico delle chiamate<br />
per la funzione Fibonacci con n=4:</p>
<p>[i_fibonacci.png]</p>
<p>Un grafico delle chiamate mostra una serie di frame (uno per ogni<br />
funzione) con linee che collegano ciascun frame alle funzioni<br />
chiamate. A iniziare dall&#8217;alto Fibonacci con n=4 chiama Fibonacci con<br />
n=3 e n=2. A sua volta Fibonacci con n=3 chiama Fibonacci con n=2 e<br />
n=1. E così via.</p>
<p>Se conti il numero di volte in cui Fibonacci(0) e Fibonacci(1) sono<br />
chiamate ti accorgerai facilmente che questa soluzione è evidentemente<br />
inefficiente e le sue prestazioni tendono a peggiorare man mano che<br />
l&#8217;argomento diventa più grande.</p>
<p>Una buona soluzione è quella di tenere traccia in un dizionario di<br />
tutti i valori già calcolati per evitare il ricalcolo in tempi<br />
successivi. Un valore che viene memorizzato per un uso successivo è<br />
chiamato suggerimento. Ecco un&#8217;implementazione di Fibonacci fatta<br />
usando i &#8220;suggerimenti&#8221;:</p>
<p>Precedenti = {0:1, 1:1}<br />
def Fibonacci(n):<br />
if Precedenti.has_key(n):<br />
return Precedenti[n]<br />
else:<br />
NuovoValore = Fibonacci(n-1) + Fibonacci(n-2)<br />
Precedenti[n] = NuovoValore<br />
return NuovoValore</p>
<p>Il dizionario Precedenti tiene traccia dei numeri di Fibonacci già<br />
calcolati. Lo creiamo inserendo solo due coppie: 0 associato a 1<br />
(Fibonacci(0) = 1) e 1 associato a 1 (Fibonacci(1) = 1).</p>
<p>La nuova funzione Fibonacci prima di tutto controlla se nel dizionario<br />
è già presente il valore cercato: se c&#8217;è viene restituito senza<br />
ulteriori elaborazioni. Nel caso non sia presente deve essere<br />
calcolato il nuovo valore che poi viene aggiunto al dizionario (per<br />
poter essere usato in momenti successivi) prima che la funzione<br />
termini.</p>
<p>Usando questa funzione di Fibonacci ora riusciamo a calcolare<br />
Fibonacci(40) in un attimo. Ma quando chiamiamo Fibonacci(50) abbiamo<br />
un altro tipo di problema:</p>
<p>&gt;&gt;&gt; Fibonacci(50)<br />
OverflowError: integer addition</p>
<p>La risposta che volevamo ottenere è 20365011074 ed il problema è che<br />
questo numero è troppo grande per essere memorizzato in un intero di<br />
Python. Durante il calcolo otteniamo un overflow che non è altro che<br />
uno &#8220;sbordamento&#8221; dall&#8217;intero. Fortunatamente in questo caso la<br />
soluzione è molto semplice.</p>
<p>10.6 Interi lunghi</p>
<p>Python fornisce un tipo chiamato long int che può maneggiare interi di<br />
qualsiasi grandezza.</p>
<p>Ci sono due modi per creare un valore intero lungo. Il primo consiste<br />
nello scrivere un intero immediatamente seguito da una L maiuscola:</p>
<p>&gt;&gt;&gt; type(1L)<br />
&lt;type &#8216;long int&#8217;&gt;</p>
<p>Il secondo è l&#8217;uso della funzione long per convertire un valore in<br />
intero lungo. long può accettare qualsiasi tipo di numero e anche una<br />
stringa di cifre:</p>
<p>&gt;&gt;&gt; long(1)<br />
1L<br />
&gt;&gt;&gt; long(3.9)<br />
3L<br />
&gt;&gt;&gt; long(&#8217;57&#8242;)<br />
57L</p>
<p>Tutte le operazioni matematiche operano correttamente sugli interi<br />
lunghi così non dobbiamo fare molto per adattare Fibonacci:</p>
<p>&gt;&gt;&gt; Precedenti = {0:1L, 1:1L}<br />
&gt;&gt;&gt; Fibonacci(50)<br />
20365011074L</p>
<p>Solamente cambiando il valore iniziale di Precedenti abbiamo cambiato<br />
il comportamento di Fibonacci. I primi elementi della sequenza sono<br />
interi lunghi così tutti i numeri successivi diventano dello stesso<br />
tipo.</p>
<p>Esercizio: converti Fattoriale così da produrre interi lunghi come<br />
risultato.</p>
<p>10.7 Conteggio di lettere</p>
<p>Nel capitolo 7 abbiamo scritto una funzione che conta il numero di<br />
lettere in una stringa. Una possibile estensione è la creazione di un<br />
istogramma della stringa per mostrare la frequenza di ciascuna<br />
lettera.</p>
<p>Questo tipo di istogramma può essere utile per comprimere un file di<br />
testo: dato che le lettere compaiono con frequenza diversa possiamo<br />
usare codici brevi per le lettere più frequenti e codici via via più<br />
lunghi per le meno frequenti.</p>
<p>I dizionari permettono di realizzare istogrammi in modo elegante:</p>
<p>&gt;&gt;&gt; ConteggioLettere = {}<br />
&gt;&gt;&gt; for Lettera in &#8220;Mississippi&#8221;:<br />
&#8230;   ConteggioLettere [Lettera] = ConteggioLettere.get \<br />
(Lettera, 0) + 1<br />
&#8230;<br />
&gt;&gt;&gt; ConteggioLettere<br />
{&#8216;M&#8217;: 1, &#8216;s&#8217;: 4, &#8216;p&#8217;: 2, &#8216;i&#8217;: 4}</p>
<p>Siamo partiti con un dizionario vuoto e per ogni lettera della stringa<br />
abbiamo incrementato il corrispondente conteggio. Alla fine il<br />
dizionario contiene coppie formate da lettera e frequenza e queste<br />
coppie rappresentano il nostro istogramma.</p>
<p>Può essere più interessante mostrare l&#8217;istogramma in ordine<br />
alfabetico, e in questo caso facciamo uso dei metodi items e sort:</p>
<p>&gt;&gt;&gt; ConteggioLettere = ConteggioLettere.items()<br />
&gt;&gt;&gt; ConteggioLettere.sort()<br />
&gt;&gt;&gt; print ConteggioLettere<br />
[('M', 1), ('i', 4), ('p', 2), ('s', 4)]</p>
<p>Abbiamo visto già il metodo items ma sort è il primo metodo che<br />
incontriamo ad essere applicabile alle liste. Ci sono parecchi altri<br />
metodi applicabili alle liste (tra gli altri append, extend e<br />
reverse). Puoi consultare la documentazione di Python per avere<br />
ulteriori informazioni a riguardo.</p>
<p>10.8 Glossario</p>
<p>Dizionario<br />
collezione di coppie chiave-valore dove si associa ogni chiave<br />
ad un valore. Le chiavi devono essere immutabili; i valori<br />
possono essere di qualsiasi tipo.</p>
<p>Chiave<br />
valore usato per cercare una voce in un dizionario.</p>
<p>Coppia chiave-valore<br />
elemento di un dizionario.</p>
<p>Metodo<br />
tipo di funzione chiamata con una sintassi particolare ed<br />
invocata su un oggetto.</p>
<p>Invocare<br />
chiamare un metodo.</p>
<p>Suggerimento<br />
deposito temporaneo di valori precalcolati per evitare<br />
elaborazioni inutili.</p>
<p>Overflow<br />
errore generato quando un risultato è troppo grande per essere<br />
rappresentato da un determinato formato numerico.</p>
<p>Capitolo 11</p>
<p>File ed eccezioni</p>
<p>Quando un programma è in esecuzione i suoi dati sono in memoria; nel<br />
momento in cui il programma termina o il computer viene spento tutti i<br />
dati in memoria vengono irrimediabilmente persi. Per conservare i dati<br />
devi quindi memorizzarli in un file, solitamente memorizzato su hard<br />
disk, floppy o CD-ROM.</p>
<p>Lavorando con un gran numero di file è logico cercare di organizzarli:<br />
questo viene fatto inserendoli in cartelle (dette anche &#8220;folder&#8221; o<br />
&#8220;directory&#8221;). Ogni file all&#8217;interno di una cartella è identificato da<br />
un nome unico.</p>
<p>Leggendo e scrivendo file i programmi possono scambiare informazioni e<br />
anche generare documenti stampabili usando il formato PDF o altri<br />
formati simili.</p>
<p>Lavorare con i file è molto simile a leggere un libro: per usarli li<br />
devi prima aprire e quando hai finito li chiudi. Mentre il libro è<br />
aperto lo puoi leggere o puoi scrivere una nota sulle sue pagine,<br />
sapendo in ogni momento dove ti trovi al suo interno. La maggior parte<br />
delle volte leggerai il libro in ordine, ma nulla ti vieta di saltare<br />
a determinate pagine facendo uso dell&#8217;indice.</p>
<p>Questa metafora può essere applicata ai file. Per aprire un file devi<br />
specificarne il nome e l&#8217;uso che intendi farne (lettura o scrittura).</p>
<p>L&#8217;apertura del file crea un oggetto file: nell&#8217;esempio che segue<br />
useremo la variabile f per riferirci all&#8217;oggetto file appena creato.</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.dat&#8221;,&#8221;w&#8221;)<br />
&gt;&gt;&gt; print f<br />
&lt;open file &#8216;test.dat&#8217;, mode &#8216;w&#8217; at fe820&gt;</p>
<p>La funzione open prende due argomenti: il primo è il nome del file ed<br />
il secondo il suo &#8220;modo&#8221;. Il modo &#8220;w&#8221; significa che stiamo aprendo il<br />
file in scrittura.</p>
<p>Nel caso non dovesse esistere un file chiamato test.dat l&#8217;apertura in<br />
scrittura farà in modo di crearlo vuoto. Nel caso dovesse già<br />
esistere, la vecchia copia verrà rimpiazzata da quella nuova e<br />
definitivamente persa.</p>
<p>Quando stampiamo l&#8217;oggetto file possiamo leggere il nome del file<br />
aperto, il modo e la posizione dell&#8217;oggetto in memoria.</p>
<p>Per inserire dati nel file invochiamo il metodo write:</p>
<p>&gt;&gt;&gt; f.write(&#8220;Adesso&#8221;)<br />
&gt;&gt;&gt; f.write(&#8220;chiudi il file&#8221;)</p>
<p>La chiusura del file avvisa il sistema che abbiamo concluso la<br />
scrittura e rende il file disponibile alla lettura:</p>
<p>&gt;&gt;&gt; f.close()</p>
<p>Solo dopo aver chiuso il file possiamo riaprirlo in lettura e leggerne<br />
il contenuto. Questa volta l&#8217;argomento di modo è &#8220;r&#8221;:</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.dat&#8221;,&#8221;r&#8221;)</p>
<p>Se cerchiamo di aprire un file che non esiste otteniamo un errore:</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.cat&#8221;,&#8221;r&#8221;)<br />
IOError: [Errno 2] No such file or directory: &#8216;test.cat&#8217;</p>
<p>Il metodo read legge dati da un file. Senza argomenti legge l&#8217;intero<br />
contenuto del file:</p>
<p>&gt;&gt;&gt; Testo = f.read()<br />
&gt;&gt;&gt; print Testo<br />
Adessochiudi il file</p>
<p>Non c&#8217;è spazio tra Adesso e chiudi perché non abbiamo scritto uno<br />
spazio tra le due stringhe al momento della scrittura.</p>
<p>read accetta anche un argomento che specifica quanti caratteri<br />
leggere:</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.dat&#8221;,&#8221;r&#8221;)<br />
&gt;&gt;&gt; print f.read(5)<br />
Adess</p>
<p>Se non ci sono caratteri sufficienti nel file, read ritorna quelli<br />
effettivamente disponibili. Quando abbiamo raggiunto la fine del file<br />
read ritorna una stringa vuota:</p>
<p>&gt;&gt;&gt; print f.read(1000006)<br />
ochiudi il file<br />
&gt;&gt;&gt; print f.read()<br />
&gt;&gt;&gt;</p>
<p>La funzione che segue copia un file leggendo e scrivendo fino a 50<br />
caratteri per volta. Il primo argomento è il nome del file originale,<br />
il secondo quello della copia:</p>
<p>def CopiaFile(Originale, Copia):<br />
f1 = open(Originale, &#8220;r&#8221;)<br />
f2 = open(Copia, &#8220;w&#8221;)<br />
while 1:<br />
Testo = f1.read(50)<br />
if Testo == &#8220;&#8221;:<br />
break<br />
f2.write(Testo)<br />
f1.close()<br />
f2.close()<br />
return</p>
<p>L&#8217;istruzione break è nuova: la sua esecuzione interrompe<br />
immediatamente il loop saltando alla prima istruzione che lo segue (in<br />
questo caso f1.close()).</p>
<p>Il ciclo while dell&#8217;esempio è apparentemente infinito dato che la sua<br />
condizione ha valore 1 ed è quindi sempre vera. L&#8217;unico modo per<br />
uscire da questo ciclo è di eseguire un break che viene invocato<br />
quando Testo è una stringa vuota e cioè quando abbiamo raggiunto la<br />
fine del file in lettura.</p>
<p>11.1 File di testo</p>
<p>Un file di testo è un file che contiene caratteri stampabili e spazi<br />
bianchi, organizzati in linee separate da caratteri di ritorno a capo.<br />
Python è stato specificatamente progettato per elaborare file di testo<br />
e fornisce metodi molto efficaci per rendere facile questo compito.</p>
<p>Per dimostrarlo creeremo un file di testo composto da tre righe di<br />
testo separate da dei ritorno a capo:</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.dat&#8221;,&#8221;w&#8221;)<br />
&gt;&gt;&gt; f.write(&#8220;linea uno\nlinea due\nlinea tre\n&#8221;)<br />
&gt;&gt;&gt; f.close()</p>
<p>Il metodo readline legge tutti i caratteri fino al prossimo ritorno a<br />
capo:</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.dat&#8221;,&#8221;r&#8221;)<br />
&gt;&gt;&gt; print f.readline()<br />
linea uno<br />
&gt;&gt;&gt;</p>
<p>readlines ritorna tutte le righe rimanenti come lista di stringhe:</p>
<p>&gt;&gt;&gt; print f.readlines()<br />
['linea due\n', 'linea tre\n']</p>
<p>In questo caso il risultato è in formato lista e ciò significa che le<br />
stringhe appaiono racchiuse tra apici e i caratteri di ritorno a capo<br />
come sequenze di escape.</p>
<p>Alla fine del file readline ritorna una stringa vuota e readlines una<br />
lista vuota:</p>
<p>&gt;&gt;&gt; print f.readline()<br />
&gt;&gt;&gt; print f.readlines()<br />
[]</p>
<p>Quello che segue è un esempio di elaborazione di un file: FiltraFile<br />
fa una copia del file Originale omettendo tutte le righe che iniziano<br />
con #:</p>
<p>def FiltraFile(Originale, Nuovo):<br />
f1 = open(Originale, &#8220;r&#8221;)<br />
f2 = open(Nuovo, &#8220;w&#8221;)<br />
while 1:<br />
Linea = f1.readline()<br />
if Linea == &#8220;&#8221;:<br />
break<br />
if Linea[0] == &#8216;#&#8217;:<br />
continue<br />
f2.write(Linea)<br />
f1.close()<br />
f2.close()<br />
return</p>
<p>L&#8217;istruzione continue termina l&#8217;iterazione corrente e continua con il<br />
prossimo ciclo: il flusso di programma torna all&#8217;inizio del ciclo,<br />
controlla la condizione e procede di conseguenza.</p>
<p>Non appena Linea è una stringa vuota il ciclo termina grazie al break.<br />
Se il primo carattere di Linea è un carattere cancelletto l&#8217;esecuzione<br />
continua tornando all&#8217;inizio del ciclo. Se entrambe le condizioni<br />
falliscono (non siamo in presenza della fine del file né il primo<br />
carattere è un cancelletto) la riga viene copiata nel nuovo file.</p>
<p>11.2 Scrittura delle variabili</p>
<p>L&#8217;argomento di write dev&#8217;essere una stringa così se vogliamo inserire<br />
altri tipi di valore in un file li dobbiamo prima convertire in<br />
stringhe. Il modo più semplice è quello di usare la funzione str:</p>
<p>&gt;&gt;&gt; x = 52<br />
&gt;&gt;&gt; f.write (str(x))</p>
<p>Un&#8217;alternativa è quella di usare l&#8217;operatore di formato %. Quando è<br />
applicato agli interi % è l&#8217;operatore modulo, ma quando il primo<br />
operando è una stringa % identifica l&#8217;operatore formato.</p>
<p>Il primo operando in questo caso è la stringa di formato ed il secondo<br />
una tupla di espressioni. Il risultato è una stringa formattata<br />
secondo la stringa di formato.</p>
<p>Cominciamo con un esempio semplice: la sequenza di formato &#8220;%d&#8221;<br />
significa che la prima espressione della tupla che segue deve essere<br />
formattata come un intero:</p>
<p>&gt;&gt;&gt; NumAuto = 52<br />
&gt;&gt;&gt; &#8220;%d&#8221; % NumAuto<br />
&#8217;52&#8242;</p>
<p>Il risultato è la stringa &#8217;52&#8242; che non deve essere confusa con il<br />
valore intero 52.</p>
<p>Una sequenza di formato può comparire dovunque all&#8217;interno di una<br />
stringa di formato così possiamo inserire un valore in una frase<br />
qualsiasi:</p>
<p>&gt;&gt;&gt; NumAuto = 52<br />
&gt;&gt;&gt; &#8220;In luglio abbiamo venduto %d automobili.&#8221; % NumAuto<br />
&#8216;In luglio abbiamo venduto 52 automobili.&#8217;</p>
<p>La sequenza di formato &#8220;%f&#8221; formatta il valore nella tupla in un<br />
numero in virgola mobile e &#8220;%s&#8221; lo formatta come stringa:</p>
<p>&gt;&gt;&gt; &#8220;Ricavo in %d giorni: %f milioni di %s.&#8221; % (34,6.1,&#8217;euro&#8217;)<br />
&#8216;Ricavo in 34 giorni: 6.100000 milioni di euro.&#8217;</p>
<p>Per default la formattazione in virgola mobile %f stampa sei cifre<br />
decimali.</p>
<p>Il numero delle espressioni nella tupla deve naturalmente essere<br />
corrispondente a quello delle sequenze di formato nella stringa di<br />
formato, ed i tipi delle espressioni devono corrispondere a quelli<br />
delle sequenze di formato:</p>
<p>&gt;&gt;&gt; &#8220;%d %d %d&#8221; % (1,2)<br />
TypeError: not enough arguments for format string<br />
&gt;&gt;&gt; &#8220;%d&#8221; % &#8216;euro&#8217;<br />
TypeError: illegal argument type for built-in operation</p>
<p>Nel primo esempio la stringa di formato si aspetta tre espressioni ma<br />
nella tupla ne abbiamo passate solo due. Nel secondo vogliamo stampare<br />
un intero ma stiamo passando una stringa.</p>
<p>Per avere un miglior controllo del risultato possiamo modificare le<br />
sequenze di formato aggiungendo delle cifre:</p>
<p>&gt;&gt;&gt; &#8220;%6d&#8221; % 62<br />
&#8216;    62&#8242;<br />
&gt;&gt;&gt; &#8220;%12f&#8221; % 6.1<br />
&#8216;    6.100000&#8242;</p>
<p>Il numero dopo il segno di percentuale è il numero minimo di caratteri<br />
che dovrà occupare il nostro valore dopo essere stato convertito. Se<br />
la conversione occuperà un numero di spazi minori verranno aggiunti<br />
spazi alla sinistra. Se il numero è negativo sono aggiunti spazi dopo<br />
il numero convertito:</p>
<p>&gt;&gt;&gt; &#8220;%-6d&#8221; % 62<br />
&#8217;62    &#8216;</p>
<p>Nel caso dei numeri in virgola mobile possiamo anche specificare<br />
quante cifre devono comparire dopo il punto decimale:</p>
<p>&gt;&gt;&gt; &#8220;%12.2f&#8221; % 6.1<br />
&#8216;        6.10&#8242;</p>
<p>In questo esempio abbiamo stabilito di creare una stringa di 12<br />
caratteri con due cifre dopo il punto decimale. Questo tipo di<br />
formattazione è utile quando si devono stampare dei valori contabili<br />
in forma tabellare con il punto decimale allineato.</p>
<p>Immaginiamo un dizionario che contiene il nome (la chiave) e tariffa<br />
oraria (il valore) per una serie di lavoratori. Ecco una funzione che<br />
stampa il contenuto del dizionario in modo formattato:</p>
<p>def Report(Tariffe) :<br />
Lavoratori = Tariffe.keys()<br />
Lavoratori.sort()<br />
for Lavoratore in Lavoratori:<br />
print &#8220;%-20s %12.02f&#8221; % (Lavoratore, Tariffe[Lavoratore])</p>
<p>Per provare questa funzione creiamo un piccolo dizionario e<br />
stampiamone il contenuto:</p>
<p>&gt;&gt;&gt; Tariffe = {&#8216;Maria&#8217;: 6.23, &#8216;Giovanni&#8217;: 5.45, &#8216;Alberto&#8217;: 4.25}<br />
&gt;&gt;&gt; Report(Tariffe)<br />
Alberto                      4.25<br />
Giovanni                     5.45<br />
Maria                        6.23</p>
<p>Controllando la larghezza di ciascun valore garantiamo che le colonne<br />
saranno perfettamente allineate, sempre che il nome rimanga al di<br />
sotto dei 20 caratteri e la tariffa oraria al di sotto delle 12<br />
cifre&#8230;</p>
<p>11.3 Directory</p>
<p>Quando crei un nuovo file aprendolo e scrivendoci qualcosa, questo<br />
viene memorizzato nella directory corrente e cioè in quella dalla<br />
quale sei partito per eseguire l&#8217;interprete Python. Allo stesso modo<br />
se richiedi la lettura di un file Python lo cercherà nella directory<br />
corrente.</p>
<p>Se vuoi aprire il file da qualche altra parte dovrai anche specificare<br />
il percorso per raggiungerlo, e cioè il nome della directory di<br />
appartenenza:</p>
<p>&gt;&gt;&gt;   f = open(&#8220;/usr/share/dict/words&#8221;,&#8221;r&#8221;)<br />
&gt;&gt;&gt;   print f.readline()<br />
Aarhus</p>
<p>In questo esempio apriamo un file chiamato words che risiede in una<br />
directory chiamata dict che risiede in un&#8217;altra directory chiamata<br />
share che a sua volta risiede in usr. Quest&#8217;ultima risiede nella<br />
directory principale del sistema, /, secondo il formato Linux.</p>
<p>Non puoi usare / come parte di un nome di file proprio perché questo è<br />
un carattere delimitatore che viene inserito tra i vari nomi delle<br />
directory nella definizione del percorso.</p>
<p>11.4 Pickling</p>
<p>Abbiamo visto come per mettere dei valori in un file di testo li<br />
abbiamo dovuti preventivamente convertire in stringhe. Hai già visto<br />
come farlo usando str:</p>
<p>&gt;&gt;&gt; f.write (str(12.3))<br />
&gt;&gt;&gt; f.write (str(4.567))<br />
&gt;&gt;&gt; f.write (str([1,2,3]))</p>
<p>Il problema è che quando cerchi di recuperare il valore dal file<br />
ottieni una stringa, e non l&#8217;informazione originale che avevi<br />
memorizzato. Oltretutto non c&#8217;è nemmeno il modo di sapere dove inizia<br />
o termina di preciso la stringa che definisce il valore nel file:</p>
<p>&gt;&gt;&gt;   f.readline()<br />
&#8217;12.34.567[1, 2, 3]&#8216;</p>
<p>La soluzione è il pickling (che letteralmente significa &#8220;conservazione<br />
sotto vetro&#8221;) chiamato così perché &#8220;preserva&#8221; le strutture dei dati.<br />
Il modulo pickle contiene tutti i comandi necessari. Importiamo il<br />
modulo e poi apriamo il file nel solito modo:</p>
<p>&gt;&gt;&gt; import pickle<br />
&gt;&gt;&gt; f = open(&#8220;test.pck&#8221;,&#8221;w&#8221;)</p>
<p>Per memorizzare una struttura di dati usa il metodo dump e poi chiudi<br />
il file:</p>
<p>&gt;&gt;&gt; pickle.dump(12.3, f)<br />
&gt;&gt;&gt; pickle.dump(4.567, f)<br />
&gt;&gt;&gt; pickle.dump([1,2,3], f)<br />
&gt;&gt;&gt; f.close()</p>
<p>Puoi riaprire il file e caricare le strutture di dati memorizzati con<br />
il metodo load:</p>
<p>&gt;&gt;&gt; f = open(&#8220;test.pck&#8221;,&#8221;r&#8221;)<br />
&gt;&gt;&gt; x = pickle.load(f)<br />
&gt;&gt;&gt; x<br />
12.3<br />
&gt;&gt;&gt; type(x)<br />
&lt;type &#8216;float&#8217;&gt;<br />
&gt;&gt;&gt; x2 = pickle.load(f)<br />
&gt;&gt;&gt; x2<br />
4.567<br />
&gt;&gt;&gt; type(x2)<br />
&lt;type &#8216;float&#8217;&gt;<br />
&gt;&gt;&gt; y = pickle.load(f)<br />
&gt;&gt;&gt; y<br />
[1, 2, 3]<br />
&gt;&gt;&gt; type(y)<br />
&lt;type &#8216;list&#8217;&gt;</p>
<p>Ogni volta che invochiamo load otteniamo un singolo valore completo<br />
del suo tipo originale.</p>
<p>11.5 Eccezioni</p>
<p>Se il programma si blocca a causa di un errore in esecuzione viene<br />
creata un&#8217;eccezione: l&#8217;interprete si ferma e mostra un messaggio<br />
d&#8217;errore.</p>
<p>Le eccezioni più comuni per i programmi che hai visto finora possono<br />
essere:<br />
* la divisione di un valore per zero:<br />
&gt;&gt;&gt; print 55/0<br />
ZeroDivisionError: integer division or modulo<br />
* la richiesta di un elemento di una lista con un indice errato:<br />
&gt;&gt;&gt; a = []<br />
&gt;&gt;&gt; print a[5]<br />
IndexError: list index out of range<br />
* la richiesta di una chiave non esistente in un dizionario:<br />
&gt;&gt;&gt; b = {}<br />
&gt;&gt;&gt; print b['pippo']<br />
KeyError: pippo</p>
<p>In ogni caso il messaggio d&#8217;errore è composto di due parti: la<br />
categoria dell&#8217;errore e le specifiche separati dai due punti.<br />
Normalmente Python stampa la traccia del programma al momento<br />
dell&#8217;errore ma in questi esempi sarà omessa per questioni di<br />
leggibilità.</p>
<p>Molte operazioni possono generare errori in esecuzione ma in genere<br />
desideriamo che il programma non si blocchi quando questo avviene. La<br />
soluzione è quella di gestire l&#8217;eccezione usando le istruzioni try ed<br />
except.</p>
<p>Per fare un esempio possiamo chiedere ad un operatore di inserire il<br />
nome di un file per poi provare ad aprirlo. Se il file non dovesse<br />
esistere non vogliamo che il programma si blocchi mostrando un<br />
messaggio di errore; così cerchiamo di gestire questa possibile<br />
eccezione:</p>
<p>NomeFile = raw_input(&#8216;Inserisci il nome del file: &#8216;)<br />
try:<br />
f = open (NomeFile, &#8220;r&#8221;)<br />
except:<br />
print &#8216;Il file&#8217;, NomeFile, &#8216;non esiste&#8217;</p>
<p>L&#8217;istruzione try esegue le istruzioni nel suo blocco. Se non si<br />
verificano eccezioni (e cioè se le istruzioni del blocco try sono<br />
eseguite senza errori) l&#8217;istruzione except ed il blocco corrispondente<br />
vengono saltate ed il flusso del programma prosegue dalla prima<br />
istruzione presente dopo il blocco except. Nel caso si verifichi<br />
qualche eccezione (nel nostro caso la più probabile è che il file<br />
richiesto non esiste) viene interrotto immediatamente il flusso del<br />
blocco try ed eseguito il blocco except.</p>
<p>Possiamo incapsulare questa capacità in una funzione: FileEsiste<br />
prende un nome di un file e ritorna vero se il file esiste, falso se<br />
non esiste.</p>
<p>def FileEsiste(NomeFile):<br />
try:<br />
f = open(NomeFile)<br />
f.close()<br />
return 1<br />
except:<br />
return 0</p>
<p>Puoi anche usare blocchi di except multipli per gestire diversi tipi<br />
di eccezioni. Vedi a riguardo il Python Reference Manual.</p>
<p>Con try/except possiamo fare in modo di continuare ad eseguire un<br />
programma in caso di errore. Possiamo anche &#8220;sollevare&#8221; delle<br />
eccezioni nel corso del programma con l&#8217;istruzione raise in modo da<br />
generare un errore in esecuzione quando qualche condizione non è<br />
verificata:</p>
<p>def InputNumero() :<br />
x = input (&#8216;Dimmi un numero: &#8216;)<br />
if x &gt; 16 :<br />
raise &#8216;ErroreNumero&#8217;, &#8216;mi aspetto numeri minori di 17!&#8217;<br />
return x</p>
<p>In questo caso viene generato un errore in esecuzione quando è<br />
introdotto un numero maggiore di 16.</p>
<p>L&#8217;istruzione raise prende due argomenti: il tipo di eccezione e<br />
l&#8217;indicazione specifica del tipo di errore. ErroreNumero è un nuovo<br />
tipo di eccezione che abbiamo inventato appositamente per questa<br />
applicazione.</p>
<p>Se la funzione che chiama InputNumero gestisce gli errori il programma<br />
continua, altrimenti Python stampa il messaggio d&#8217;errore e termina<br />
l&#8217;esecuzione:</p>
<p>&gt;&gt;&gt; InputNumero()<br />
Dimmi un numero: 17<br />
ErroreNumero: mi aspetto numeri minori di 17!</p>
<p>Il messaggio di errore include l&#8217;indicazione del tipo di eccezione e<br />
l&#8217;informazione aggiuntiva che è stata fornita.</p>
<p>Esercizio: scrivi una funzione che usa InputNumero per inserire un<br />
numero da tastiera e gestisce l&#8217;eccezione ErroreNumero quando il<br />
numero non è corretto.</p>
<p>11.6 Glossario</p>
<p>File<br />
entità identificata da un nome solitamente memorizzata su hard<br />
disk, floppy disk o CD-ROM, e contenente una serie di dati.</p>
<p>Directory<br />
contenitore di file; è anche chiamata cartella o folder.</p>
<p>Percorso<br />
sequenza di nomi di directory che specifica l&#8217;esatta locazione<br />
di un file.</p>
<p>File di testo<br />
file che contiene solo caratteri stampabili organizzati come<br />
serie di righe separate da caratteri di ritorno a capo.</p>
<p>Istruzione break<br />
istruzione che causa l&#8217;interruzione immediata del flusso del<br />
programma e l&#8217;uscita da un ciclo.</p>
<p>Istruzione continue<br />
istruzione che causa l&#8217;immediato ritorno del flusso del<br />
programma all&#8217;inizio del ciclo senza completarne il corpo.</p>
<p>Operatore formato<br />
operatore indicato da &#8216;%&#8217; che produce una stringa di caratteri<br />
in base ad una stringa di formato passata come argomento.</p>
<p>Stringa di formato<br />
stringa che contiene caratteri stampabili e stabilisce come<br />
debbano essere formattati una serie di valori in una stringa.</p>
<p>Sequenza di formato<br />
sequenza di caratteri che inizia con % e che stabilisce,<br />
all&#8217;interno di una stringa di formato, come debba essere<br />
convertito in stringa un singolo valore.</p>
<p>Pickling<br />
operazione di scrittura su file di un valore assieme alla<br />
descrizione del suo tipo, in modo da poterlo recuperare<br />
facilmente in seguito.</p>
<p>Eccezione<br />
errore in esecuzione.</p>
<p>Gestire un&#8217;eccezione<br />
prevedere i possibili errori in esecuzione per fare in modo che<br />
questi non interrompano l&#8217;esecuzione del programma.</p>
<p>Sollevare un&#8217;eccezione<br />
segnalare la presenza di una situazione anomala facendo uso<br />
dell&#8217;istruzione raise.</p>
<p>Capitolo 12</p>
<p>Classi e oggetti</p>
<p>12.1 Tipi composti definiti dall&#8217;utente</p>
<p>Abbiamo usato alcuni dei tipi composti predefiniti e ora siamo pronti<br />
per crearne uno tutto nostro: il tipo Punto.</p>
<p>Considerando il concetto matematico di punto nelle due dimensioni, il<br />
punto è definito da una coppia di numeri (le coordinate). In notazione<br />
matematica le coordinate dei punti sono spesso scritte tra parentesi<br />
con una virgola posta a separare i due valori. Per esempio (0, 0)<br />
rappresenta l&#8217;origine e (x, y) il punto che si trova x unità a destra<br />
e y unità in alto rispetto all&#8217;origine.</p>
<p>Un modo naturale di rappresentare un punto in Python è una coppia di<br />
numeri in virgola mobile e la questione che ci rimane da definire è in<br />
che modo raggruppare questa coppia di valori in un oggetto composto:<br />
un sistema veloce anche se poco elegante sarebbe l&#8217;uso di una tupla,<br />
anche se possiamo fare di meglio.</p>
<p>Un modo alternativo è quello di definire un nuovo tipo composto<br />
chiamato classe. Questo tipo di approccio richiede un po&#8217; di sforzo<br />
iniziale, ma i suoi benefici saranno subito evidenti.</p>
<p>Una definizione di classe ha questa sintassi:</p>
<p>class Punto:<br />
pass</p>
<p>Le definizioni di classe possono essere poste in qualsiasi punto di un<br />
programma ma solitamente per questioni di leggibilità sono poste<br />
all&#8217;inizio, subito sotto le istruzioni import. Le regole di sintassi<br />
per la definizione di una classe sono le stesse degli altri tipi<br />
composti: la definizione dell&#8217;esempio crea una nuova classe chiamata<br />
Punto. L&#8217;istruzione pass non ha effetti: è stata usata per il solo<br />
fatto che la definizione prevede un corpo che deve ancora essere<br />
scritto.</p>
<p>Creando la classe Punto abbiamo anche creato un nuovo tipo di dato<br />
chiamato con lo stesso nome. I membri di questo tipo sono detti<br />
istanze del tipo o oggetti. La creazione di una nuova istanza è detta<br />
istanziazione: solo al momento dell&#8217;istanziazione parte della memoria<br />
è riservata per depositare il valore dell&#8217;oggetto. Per creare un<br />
oggetto di tipo Punto viene chiamata una funzione chiamata Punto:</p>
<p>P1 = Punto()</p>
<p>Alla variabile P1 è assegnato il riferimento ad un nuovo oggetto<br />
Punto. Una funzione come Punto, che crea nuovi oggetti e riserva<br />
quindi della memoria per depositarne i valori, è detta costruttore.</p>
<p>12.2 Attributi</p>
<p>Possiamo aggiungere un nuovo dato ad un&#8217;istanza usando la notazione<br />
punto:</p>
<p>&gt;&gt;&gt; P1.x = 3.0<br />
&gt;&gt;&gt; P1.y = 4.0</p>
<p>Questa sintassi è simile a quella usata per la selezione di una<br />
variabile appartenente ad un modulo, tipo math.pi e string.uppercase.<br />
In questo caso stiamo selezionando una voce da un&#8217;istanza e queste<br />
voci che fanno parte dell&#8217;istanza sono dette attributi.</p>
<p>Questo diagramma di stato mostra il risultato delle assegnazioni:</p>
<p>[i_point.png]</p>
<p>La variabile P1 si riferisce ad un oggetto Punto che contiene due<br />
attributi ed ogni attributo (una coordinata) si riferisce ad un numero<br />
in virgola mobile.</p>
<p>Possiamo leggere il valore di un attributo con la stessa sintassi:</p>
<p>&gt;&gt;&gt; print P1.y<br />
4.0<br />
&gt;&gt;&gt; x = P1.x<br />
&gt;&gt;&gt; print x<br />
3.0</p>
<p>L&#8217;espressione P1.x significa &#8220;vai all&#8217;oggetto puntato da P1 e ottieni<br />
il valore del suo attributo x&#8221;. In questo caso assegniamo il valore ad<br />
una variabile chiamata x: non c&#8217;è conflitto tra la variabile locale x<br />
e l&#8217;attributo x di P1: lo scopo della notazione punto è proprio quello<br />
di identificare la variabile cui ci si riferisce evitando le<br />
ambiguità.</p>
<p>Puoi usare la notazione punto all&#8217;interno di ogni espressione così che<br />
le istruzioni proposte di seguito sono a tutti gli effetti<br />
perfettamente lecite:</p>
<p>print &#8216;(&#8216; + str(P1.x) + &#8216;, &#8216; + str(P1.y) + &#8216;)&#8217;<br />
DistanzaAlQuadrato = P1.x * P1.x + P1.y * P1.y</p>
<p>La prima riga stampa (3.0, 4.0); la seconda calcola il valore 25.0.</p>
<p>Potresti essere tentato di stampare direttamente il valore di P1:</p>
<p>&gt;&gt;&gt; print P1<br />
&lt;__main__.Punto instance at 80f8e70&gt;</p>
<p>Il risultato indica che P1 è un&#8217;istanza della classe Punto e che è<br />
stato definito in __main__. 80f8e70 è l&#8217;identificatore univoco<br />
dell&#8217;oggetto, scritto in base 16 (esadecimale). Probabilmente questo<br />
non è il modo più pratico di mostrare un oggetto Punto ma vedrai<br />
subito come renderlo più comprensibile.</p>
<p>Esercizio: crea e stampa un oggetto Punto e poi usa id per stampare<br />
l&#8217;identificatore univoco dell&#8217;oggetto. Traduci la forma esadecimale<br />
dell&#8217;identificatore in decimale e verifica che i due valori trovati<br />
coincidono.</p>
<p>12.3 Istanze come parametri</p>
<p>Puoi passare un&#8217;istanza come parametro ad una funzione nel solito<br />
modo:</p>
<p>def StampaPunto(Punto):<br />
print &#8216;(&#8216; + str(Punto.x) + &#8216;, &#8216; + str(Punto.y) + &#8216;)&#8217;</p>
<p>StampaPunto prende un oggetto Punto come argomento e ne stampa gli<br />
attributi in forma standard. Se chiami StampaPunto(P1) la stampa è<br />
(3.0, 4.0).</p>
<p>Esercizio: riscrivi la funzione DistanzaTraDuePunti che abbiamo già<br />
visto alla sezione 5.2 così da accettare due oggetti di tipo Punto<br />
invece di quattro numeri.</p>
<p>12.4 Uguaglianza</p>
<p>La parola &#8220;uguale&#8221; sembra così intuitiva che probabilmente non hai mai<br />
pensato più di tanto a cosa significa veramente.</p>
<p>Quando dici &#8220;Alberto ed io abbiamo la stessa auto&#8221; naturalmente vuoi<br />
dire che entrambi possedete un&#8217;auto dello stesso modello ed è<br />
sottinteso che stai parlando di due auto diverse e non di una<br />
soltanto. Se dici &#8220;Alberto ed io abbiamo la stessa madre&#8221; è sottinteso<br />
che la madre è la stessa e voi siete fratelli * Note. L&#8217;idea stessa di<br />
uguaglianza dipende quindi dal contesto.</p>
<p>Quando parli di oggetti abbiamo la stessa ambiguità: se due oggetti di<br />
tipo Punto sono gli stessi, significa che hanno semplicemente gli<br />
stessi dati (coordinate) o che si sta parlando di un medesimo oggetto?</p>
<p>Per vedere se due riferimenti fanno capo allo stesso oggetto usa<br />
l&#8217;operatore ==:</p>
<p>&gt;&gt;&gt; P1 = Punto()<br />
&gt;&gt;&gt; P1.x = 3<br />
&gt;&gt;&gt; P1.y = 4<br />
&gt;&gt;&gt; P2 = Punto()<br />
&gt;&gt;&gt; P2.x = 3<br />
&gt;&gt;&gt; P2.y = 4<br />
&gt;&gt;&gt; P1 == P2<br />
0</p>
<p>Anche se P1 e P2 hanno le stesse coordinate non fanno riferimento allo<br />
stesso oggetto ma a due oggetti diversi. Se assegniamo P1 a P2 allora<br />
le due variabili sono alias dello stesso oggetto:</p>
<p>&gt;&gt;&gt; P2 = P1<br />
&gt;&gt;&gt; P1 == P2<br />
1</p>
<p>Questo tipo di uguaglianza è detta uguaglianza debole perché si limita<br />
a confrontare solo i riferimenti delle variabili e non il contenuto<br />
degli oggetti.</p>
<p>Per confrontare il contenuto degli oggetti (uguaglianza forte)<br />
possiamo scrivere una funzione chiamata StessoPunto:</p>
<p>def StessoPunto(P1, P2) :<br />
return (P1.x == P2.x) and (P1.y == P2.y)</p>
<p>Se creiamo due differenti oggetti che contengono gli stessi dati<br />
possiamo ora usare StessoPunto per verificare se entrambi<br />
rappresentano lo stesso punto:</p>
<p>&gt;&gt;&gt; P1 = Punto()<br />
&gt;&gt;&gt; P1.x = 3<br />
&gt;&gt;&gt; P1.y = 4<br />
&gt;&gt;&gt; P2 = Punto()<br />
&gt;&gt;&gt; P2.x = 3<br />
&gt;&gt;&gt; P2.y = 4<br />
&gt;&gt;&gt; StessoPunto(P1, P2)<br />
1</p>
<p>Logicamente se le due variabili si riferiscono allo stesso punto e<br />
sono alias l&#8217;una dell&#8217;altra allo stesso tempo garantiscono<br />
l&#8217;uguaglianza debole e quella forte.</p>
<p>12.5 Rettangoli</p>
<p>Se volessimo creare una classe per rappresentare un rettangolo quali<br />
informazioni dovremmo fornire per specificarlo in modo univoco? Per<br />
rendere le cose più semplici partiremo con un rettangolo orientato<br />
lungo gli assi.</p>
<p>Ci sono poche possibilità tra cui scegliere: potremmo specificare il<br />
centro del rettangolo e le sue dimensioni (altezza e larghezza);<br />
oppure specificare un angolo di riferimento e le dimensioni (ancora<br />
altezza e larghezza); o ancora specificare le coordinate di due punti<br />
opposti. Una scelta convenzionale abbastanza comune è quella di<br />
specificare il punto in alto a sinistra e le dimensioni.</p>
<p>Definiamo la nuova classe:</p>
<p>class Rettangolo:<br />
pass</p>
<p>Per istanziare un nuovo oggetto rettangolo:</p>
<p>Rett = Rettangolo()<br />
Rett.Larghezza = 100.0<br />
Rett.Altezza = 200.0</p>
<p>Questo codice crea un nuovo oggetto Rettangolo con due attributi in<br />
virgola mobile. Ci manca solo il punto di riferimento in alto a<br />
sinistra e per specificarlo possiamo inserire un oggetto all&#8217;interno<br />
di un altro oggetto:</p>
<p>Rett.AltoSinistra = Punto()<br />
Rett.AltoSinistra.x = 0.0;<br />
Rett.AltoSinistra.y = 0.0;</p>
<p>L&#8217;operatore punto è usato per comporre l&#8217;espressione:<br />
Rett.AltoSinistra.x significa &#8220;vai all&#8217;oggetto cui si riferisce Rett e<br />
seleziona l&#8217;attributo chiamato AltoSinistra; poi vai all&#8217;oggetto cui<br />
si riferisce AltoSinistra e seleziona l&#8217;attributo chiamato x.&#8221;</p>
<p>La figura mostra lo stato di questo oggetto:</p>
<p>[i_rectangle.png]</p>
<p>12.6 Istanze come valori di ritorno</p>
<p>Le funzioni possono ritornare istanze. Possiamo quindi scrivere una<br />
funzione TrovaCentro che prende un oggetto Rettangolo come argomento e<br />
restituisce un oggetto Punto che contiene le coordinate del centro del<br />
rettangolo:</p>
<p>def TrovaCentro(Rettangolo):<br />
P = Punto()<br />
P.x = Rettangolo.AltoSinistra.x + Rettangolo.Larghezza/2.0<br />
P.y = Rettangolo.AltoSinistra.y + Rettangolo.Altezza/2.0<br />
return P</p>
<p>Per chiamare questa funzione passa Rett come argomento e assegna il<br />
risultato ad una variabile:</p>
<p>&gt;&gt;&gt; Centro = TrovaCentro(Rett)<br />
&gt;&gt;&gt; StampaPunto(Centro)<br />
(50.0, 100.0)</p>
<p>12.7 Gli oggetti sono mutabili</p>
<p>Possiamo cambiare lo stato di un oggetto facendo un&#8217;assegnazione ad<br />
uno dei suoi attributi. Per fare un esempio possiamo cambiare le<br />
dimensioni di Rett:</p>
<p>Rett.Larghezza = Rett.Larghezza + 50<br />
Rett.Altezza = Rett.Altezza + 100</p>
<p>Incapsulando questo codice in un metodo e generalizzandolo diamo la<br />
possibilità di aumentare le dimensioni di qualsiasi rettangolo:</p>
<p>def AumentaRettangolo(Rettangolo, AumentoLargh, AumentoAlt) :<br />
Rettangolo.Larghezza = Rettangolo.Larghezza + AumentoLargh;<br />
Rettangolo.Altezza = Rettangolo.Altezza + AumentoAlt;</p>
<p>Le variabili AumentoLargh e AumentoAlt indicano di quanto devono<br />
essere aumentate le dimensioni del rettangolo. Invocare questo metodo<br />
ha lo stesso effetto di modificare il Rettangolo che è passato come<br />
argomento.</p>
<p>Creiamo un nuovo rettangolo chiamato R1 e passiamolo a<br />
AumentaRettangolo:</p>
<p>&gt;&gt;&gt; R1 = Rettangolo()<br />
&gt;&gt;&gt; R1.Larghezza = 100.0<br />
&gt;&gt;&gt; R1.Altezza = 200.0<br />
&gt;&gt;&gt; R1.AltoSinistra = Punto()<br />
&gt;&gt;&gt; R1.AltoSinistra.x = 0.0;<br />
&gt;&gt;&gt; R1.AltoSinistra.y = 0.0;<br />
&gt;&gt;&gt; AumentaRettangolo(R1, 50, 100)</p>
<p>Mentre stiamo eseguendo AumentaRettangolo il parametro Rettangolo è un<br />
alias per R1. Ogni cambiamento apportato a Rettangolo modifica<br />
direttamente R1 e viceversa.</p>
<p>Esercizio: scrivi una funzione chiamata MuoviRettangolo che prende<br />
come parametri un Rettangolo e due valori dx e dy. La funzione deve<br />
spostare le coordinate del punto in alto a sinistra sommando alla<br />
posizione x il valore dx e alla posizione y il valore dy.</p>
<p>12.8 Copia</p>
<p>Abbiamo già visto che gli alias possono rendere il programma difficile<br />
da leggere perché una modifica può cambiare il valore di variabili che<br />
apparentemente non hanno nulla a che vedere con quelle modificate. Man<br />
mano che le dimensioni del programma crescono diventa difficile tenere<br />
a mente quali variabili si riferiscano ad un dato oggetto.</p>
<p>La copia di un oggetto è spesso una comoda alternativa all&#8217;alias. Il<br />
modulo copy contiene una funzione copy che permette di duplicare<br />
qualsiasi oggetto:</p>
<p>&gt;&gt;&gt; import copy<br />
&gt;&gt;&gt; P1 = Punto()<br />
&gt;&gt;&gt; P1.x = 3<br />
&gt;&gt;&gt; P1.y = 4<br />
&gt;&gt;&gt; P2 = copy.copy(P1)<br />
&gt;&gt;&gt; P1 == P2<br />
0<br />
&gt;&gt;&gt; StessoPunto(P1, P2)<br />
1</p>
<p>Dopo avere importato il modulo copy possiamo usare il metodo copy in<br />
esso contenuto per creare un nuovo oggetto Punto. P1 e P2 non solo<br />
sono lo stesso punto ma contengono gli stessi dati.</p>
<p>Per copiare un semplice oggetto come Punto che non contiene altri<br />
oggetti al proprio interno copy è sufficiente. Questa è chiamata copia<br />
debole:</p>
<p>&gt;&gt;&gt; Punto2 = copy.copy(Punto1)</p>
<p>Quando abbiamo a che fare con un Rettangolo che contiene al proprio<br />
interno un riferimento ad un altro oggetto Punto, copy non lavora come<br />
ci si aspetta dato che viene copiato il riferimento a Punto così che<br />
sia il vecchio che il nuovo Rettangolo si riferiscono allo stesso<br />
oggetto invece di averne uno proprio per ciascuno.</p>
<p>Se creiamo il rettangolo R1 nel solito modo e ne facciamo una copia R2<br />
usando copy il diagramma di stato risultante sarà:</p>
<p>[i_rectangle2.png]</p>
<p>Quasi certamente non è questo ciò che vogliamo. In questo caso,<br />
invocando AumentaRettangolo su uno dei rettangoli non si cambieranno<br />
le dimensioni dell&#8217;altro, ma MuoviRettangolo sposterà entrambi! Questo<br />
comportamento genera parecchia confusione e porta facilmente a<br />
commettere errori.</p>
<p>Fortunatamente il modulo copy contiene un altro metodo chiamato<br />
deepcopy che copia correttamente non solo l&#8217;oggetto ma anche gli<br />
eventuali oggetti presenti al suo interno:</p>
<p>&gt;&gt;&gt; Oggetto2 = copy.deepcopy(Oggetto1)</p>
<p>Ora Oggetto1 e Oggetto2 sono oggetti completamente separati e occupano<br />
diverse zone di memoria.</p>
<p>Possiamo usare deepcopy per riscrivere completamente AumentaRettangolo<br />
così da non cambiare il Rettangolo originale ma restituire una copia<br />
con le nuove dimensioni:</p>
<p>def AumentaRettangolo(Rettangolo, AumentoLargh, AumentoAlt) :<br />
import copy<br />
NuovoRett = copy.deepcopy(Rettangolo)<br />
NuovoRett.Larghezza = NuovoRett.Larghezza + AumentoLargh<br />
NuovoRett.Altezza = NuovoRett.Altezza + AumentoAlt;<br />
return NuovoRett</p>
<p>Esercizio: riscrivi MuoviRettangolo per creare e restituire un<br />
nuovo rettangolo invece di modificare quello originale.</p>
<p>12.9 Glossario</p>
<p>Classe<br />
tipo di dato composto definito dall&#8217;utente.</p>
<p>Istanziare<br />
creare un&#8217;istanza di una determinata classe.</p>
<p>Istanza<br />
oggetto che appartiene ad una classe.</p>
<p>Oggetto<br />
tipo di dato composto che è spesso usato per definire un<br />
concetto o una cosa del mondo reale.</p>
<p>Costruttore<br />
metodo usato per definire nuovi oggetti.</p>
<p>Attributo<br />
uno dei componenti che costituiscono un&#8217;istanza.</p>
<p>Uguaglianza debole<br />
uguaglianza di riferimenti che si verifica quando due variabili<br />
si riferiscono allo stesso oggetto.</p>
<p>Uguaglianza forte<br />
uguaglianza di valori che si verifica quando due variabili si<br />
riferiscono a oggetti che hanno lo stesso valore.</p>
<p>Copia debole<br />
copia del contenuto di un oggetto includendo ogni riferimento<br />
ad eventuali oggetti interni, realizzata con la funzione copy<br />
del modulo copy.</p>
<p>Copia forte<br />
copia sia del contenuto di un oggetto che degli eventuali<br />
oggetti interni e degli oggetti eventualmente contenuti in<br />
essi; è realizzata dalla funzione deepcopy del modulo copy.</p>
<p>Capitolo 13</p>
<p>Classi e funzioni</p>
<p>13.1 Tempo</p>
<p>Definiamo ora una classe chiamata Tempo che permette di registrare<br />
un&#8217;ora del giorno:</p>
<p>class Tempo:<br />
pass</p>
<p>Possiamo creare un nuovo oggetto Tempo assegnando gli attributi per le<br />
ore, i minuti e i secondi:</p>
<p>Time = Tempo()<br />
Time.Ore = 11<br />
Time.Minuti = 59<br />
Time.Secondi = 30</p>
<p>Il diagramma di stato per l&#8217;oggetto Time è:</p>
<p>[i_time.png]</p>
<p>Esercizio: scrivi una funzione StampaTempo che prende un oggetto<br />
Tempo come argomento e ne stampa il risultato nella classica forma<br />
ore:minuti:secondi.</p>
<p>Esercizio: scrivi una funzione booleana Dopo che prende come<br />
argomenti due oggetti Tempo (Tempo1 e Tempo2) e ritorna vero se<br />
Tempo1 segue cronologicamente Tempo2 e falso in caso contrario.</p>
<p>13.2 Funzioni pure</p>
<p>Nelle prossime funzioni scriveremo due versioni di una funzione che<br />
chiameremo SommaTempi che calcola la somma di due oggetti Tempo.<br />
Questo permetterà di mostrare due tipi di funzioni: le funzioni pure e<br />
i modificatori.</p>
<p>Questa è una versione grezza di SommaTempi:</p>
<p>def SommaTempi(T1, T2):<br />
Somma = Tempo()<br />
Somma.Ore = T1.Ore + T2.Ore<br />
Somma.Minuti = T1.Minuti + T2.Minuti<br />
Somma.Secondi = T1.Secondi + T2.Secondi<br />
return Somma</p>
<p>La funzione crea un nuovo oggetto Tempo, inizializza i suoi attributi<br />
e ritorna un riferimento al nuovo oggetto. Questa viene chiamata<br />
funzione pura perché non modifica in alcun modo gli oggetti passati<br />
come suoi parametri e non ha effetti collaterali (del tipo richiedere<br />
l&#8217;immissione di un valore da parte dell&#8217;operatore o stampare un valore<br />
a video).</p>
<p>Ecco un esempio che mostra come usare questa funzione: creiamo due<br />
oggetti Tempo, OraCorrente che contiene l&#8217;ora corrente e TempoCottura<br />
che indica il tempo necessario a preparare un pasto. Se non hai ancora<br />
scritto StampaTempo guarda la sezione 14.2 prima di continuare:</p>
<p>&gt;&gt;&gt; OraCorrente = Tempo()<br />
&gt;&gt;&gt; OraCorrente.Ore = 9<br />
&gt;&gt;&gt; OraCorrente.Minuti = 14<br />
&gt;&gt;&gt; OraCorrente.Secondi=  30<br />
&gt;&gt;&gt; TempoCottura = Tempo()<br />
&gt;&gt;&gt; TempoCottura.Ore =  3<br />
&gt;&gt;&gt; TempoCottura.Minuti = 35<br />
&gt;&gt;&gt; TempoCottura.Secondi = 0<br />
&gt;&gt;&gt; PastoPronto = SommaTempi(OraCorrente, TempoCottura)<br />
&gt;&gt;&gt; StampaTempo(PastoPronto)</p>
<p>La stampa di questo programma è 12:49:30 ed il risultato è corretto.<br />
D&#8217;altra parte ci sono dei casi in cui il risultato è sbagliato: riesci<br />
a pensarne uno?</p>
<p>Il problema è che nella nostra funzione non abbiamo tenuto conto del<br />
fatto che le somme dei secondi e dei minuti possono andare oltre il<br />
59. Quando capita questo dobbiamo conteggiare il riporto come facciamo<br />
con le normali addizioni.</p>
<p>Ecco una seconda versione corretta della funzione:</p>
<p>def SommaTempi(T1, T2):<br />
Somma = Tempo()<br />
Somma.Ore = T1.Ore + T2.Ore<br />
Somma.Minuti = T1.Minuti + T2.Minuti<br />
Somma.Secondi = T1.Secondi + T2.Secondi<br />
if Somma.Secondi &gt;= 60:<br />
Somma.Secondi = Somma.Secondi &#8211; 60<br />
Somma.Minuti = Somma.Minuti + 1<br />
if Somma.Minuti &gt;= 60:<br />
Somma.Minuti = Somma.Minuti &#8211; 60<br />
Somma.Ore = Somma.Ore + 1<br />
return Somma</p>
<p>Questa funzione è corretta e comincia ad avere una certa lunghezza. In<br />
seguito ti mostreremo un approccio alternativo che ti permetterà di<br />
ottenere un codice più corto.</p>
<p>13.3 Modificatori</p>
<p>Ci sono dei casi in cui è utile una funzione che possa modificare gli<br />
oggetti passati come suoi parametri. Quando questo si verifica la<br />
funzione è detta modificatore.</p>
<p>La funzione Incremento che somma un certo numero di secondi a Tempo<br />
può essere scritta in modo molto intuitivo come modificatore. La prima<br />
stesura potrebbe essere questa:</p>
<p>def Incremento(Tempo, Secondi):<br />
Tempo.Secondi = Tempo.Secondi + Secondi<br />
if Tempo.Secondi &gt;= 60:<br />
Tempo.Secondi = Tempo.Secondi &#8211; 60<br />
Tempo.Minuti = Tempo.Minuti + 1<br />
if Tempo.Minuti &gt;= 60:<br />
Tempo.Minuti = Tempo.Minuti &#8211; 60<br />
Tempo.Ore = Tempo.Ore + 1</p>
<p>La prima riga calcola il valore mentre le successive controllano che<br />
il risultato sia nella gamma di valori accettabili come abbiamo già<br />
visto.</p>
<p>Questa funzione è corretta? Cosa succede se il parametro Secondi è<br />
molto più grande di 60? In questo caso non è abbastanza il riporto 1<br />
tra secondi e minuti, e quindi dobbiamo riscrivere il controllo per<br />
fare in modo di continuarlo finché Secondi non diventa minore di 60.<br />
Una possibile soluzione è quella di sostituire l&#8217;istruzione if con un<br />
ciclo while:</p>
<p>def Incremento(Tempo, Secondi):<br />
Tempo.Secondi = Tempo.Secondi + Secondi<br />
while Tempo.Secondi &gt;= 60:<br />
Tempo.Secondi = Tempo.Secondi &#8211; 60<br />
Tempo.Minuti = Tempo.Minuti + 1<br />
while Tempo.Minuti &gt;= 60:<br />
Tempo.Minuti = Tempo.Minuti &#8211; 60<br />
Tempo.Ore = Tempo.Ore + 1</p>
<p>La funzione è corretta, ma certamente non si tratta ancora della<br />
soluzione più efficiente possibile.</p>
<p>Esercizio: riscrivi questa funzione facendo in modo di non usare<br />
alcun tipo di ciclo.</p>
<p>Esercizio: riscrivi Incremento come funzione pura e scrivi delle<br />
chiamate di funzione per entrambe le versioni.</p>
<p>13.4 Qual è la soluzione migliore?</p>
<p>Qualsiasi cosa che può essere fatta con i modificatori può anche<br />
essere fatta con le funzioni pure e alcuni linguaggi di programmazione<br />
non prevedono addirittura i modificatori. Si può affermare che le<br />
funzioni pure sono più veloci da sviluppare e portano ad un minor<br />
numero di errori, anche se in qualche caso può essere utile fare<br />
affidamento sui modificatori.</p>
<p>In generale raccomandiamo di usare funzioni pure quando possibile e<br />
usare i modificatori come ultima risorsa solo se c&#8217;è un evidente<br />
vantaggio nel farlo. Questo tipo di approccio può essere definito<br />
stile di programmazione funzionale.</p>
<p>13.5 Sviluppo prototipale e sviluppo pianificato</p>
<p>In questo capitolo abbiamo mostrato un approccio allo sviluppo del<br />
programma che possiamo chiamare sviluppo prototipale: siamo partiti<br />
con lo stendere una versione grezza (prototipo) che poteva effettuare<br />
solo i calcoli di base, migliorandola e correggendo gli errori man<br />
mano che questi venivano trovati.</p>
<p>Sebbene questo approccio possa essere abbastanza efficace può portare<br />
a scrivere un codice inutilmente complesso (perché ha a che fare con<br />
molti casi speciali) e inaffidabile (dato che è difficile sapere se<br />
tutti gli errori sono stati rimossi).</p>
<p>Un&#8217;alternativa è lo sviluppo pianificato nel quale lo studio<br />
preventivo del problema da affrontare rende la programmazione molto<br />
più semplice. Nel nostro caso potremmo accorgerci che a tutti gli<br />
effetti l&#8217;oggetto Tempo è rappresentabile da tre cifre in base<br />
numerica 60.</p>
<p>Quando abbiamo scritto SommaTempi e Incremento stavamo effettivamente<br />
calcolando una somma in base 60 e questo è il motivo per cui dovevamo<br />
gestire il riporto tra secondi e minuti, e tra minuti e ore quando la<br />
somma era maggiore di 59.</p>
<p>Questa osservazione ci suggerisce un altro tipo di approccio al<br />
problema: possiamo convertire l&#8217;oggetto Tempo in un numero singolo ed<br />
avvantaggiarci del fatto che il computer lavora bene con le operazioni<br />
aritmetiche. Questa funzione converte un oggetto Tempo in un intero:</p>
<p>def ConverteInSecondi(Orario):<br />
Minuti = Orario.Ore * 60 + Orario.Minuti<br />
Secondi = Minuti * 60 + Orario.Secondi<br />
return Secondi</p>
<p>Tutto quello che ci serve è ora un modo per convertire da un intero ad<br />
un oggetto Tempo:</p>
<p>def ConverteInTempo(Secondi):<br />
Orario = Tempo()<br />
Orario.Ore = Secondi/3600<br />
Secondi = Secondi &#8211; Orario.Ore * 3600<br />
Orario.Minuti = Secondi/60<br />
Secondi = Secondi &#8211; Orario.Minuti * 60<br />
Orario.Secondi = Secondi<br />
return Orario</p>
<p>Forse dovrai pensarci un po&#8217; su per convincerti che questa tecnica per<br />
convertire un numero da una base all&#8217;altra è formalmente corretta.<br />
Comunque ora puoi usare queste funzioni per riscrivere SommaTempi:</p>
<p>def SommaTempi(T1, T2):<br />
Secondi = ConverteInSecondi(T1) + ConverteInSecondi(T2)<br />
return ConverteInTempo(Secondi)</p>
<p>Questa versione è molto più concisa dell&#8217;originale e ed è molto più<br />
facile dimostrare la sua correttezza.</p>
<p>Esercizio: riscrivi Incremento usando lo stesso principio.</p>
<p>13.6 Generalizzazione</p>
<p>Sicuramente la conversione numerica da base 10 a base 60 e viceversa è<br />
meno intuitiva da capire, data la sua astrazione. Il nostro intuito ci<br />
aveva portato a lavorare con i tempi in un modo molto più<br />
comprensibile anche se meno efficace.</p>
<p>Malgrado lo sforzo iniziale abbiamo progettato il nostro programma<br />
facendo in modo di trattare i tempi come numeri in base 60, il tempo<br />
investito nello scrivere le funzioni di conversione viene<br />
abbondantemente recuperato quando riusciamo a scrivere un programma<br />
molto più corto, facile da leggere e correggere, e soprattutto più<br />
affidabile.</p>
<p>Se il programma è progettato in modo oculato è anche più facile<br />
aggiungere nuove caratteristiche. Per esempio immagina di sottrarre<br />
due tempi per determinare l&#8217;intervallo trascorso. L&#8217;approccio iniziale<br />
avrebbe portato alla necessità di dover implementare una sottrazione<br />
con il prestito. Con le funzioni di conversione, scritte una sola<br />
volta ma riutilizzate in varie funzioni, è molto più facile e rapido<br />
avere un programma funzionante anche in questo caso.</p>
<p>Talvolta il fatto di rendere un problema più generale e quindi<br />
leggermente più difficile da implementare permette di gestirlo in modo<br />
più semplice dato che ci sono meno casi speciali da gestire e di<br />
conseguenza minori possibilità di errore.</p>
<p>13.7 Algoritmi</p>
<p>Quando trovi una soluzione ad una classe di problemi, invece che ad un<br />
singolo problema, hai a che fare con un algoritmo. Abbiamo già usato<br />
questa parola in precedenza ma non l&#8217;abbiamo propriamente definita ed<br />
il motivo risiede nel fatto che non è facile trovare una definizione.<br />
Proveremo un paio di approcci.</p>
<p>Consideriamo qualcosa che non è un algoritmo: quando hai imparato a<br />
moltiplicare due numeri di una cifra hai sicuramente memorizzato la<br />
tabella della moltiplicazione. In effetti si è trattato di memorizzare<br />
100 soluzioni specifiche: questo tipo di lavoro non è un algoritmo.</p>
<p>Ma se sei stato &#8220;pigro&#8221; probabilmente hai trovato delle scorciatoie<br />
che ti hanno permesso di alleggerire il lavoro. Per fare un esempio<br />
per moltiplicare n per 9 potevi scrivere n-1 come prima cifra, seguito<br />
da 10-n come seconda cifra. Questo sistema è una soluzione generale<br />
per moltiplicare ogni numero di una cifra maggiore di zero per 9: in<br />
questo caso ci troviamo a che fare con un algoritmo.</p>
<p>Le varie tecniche che hai imparato per calcolare la somma col riporto,<br />
la sottrazione con il prestito, la moltiplicazione, la divisione sono<br />
tutti algoritmi. Una delle caratteristiche degli algoritmi è che non<br />
richiedono intelligenza per essere eseguiti in quanto sono processi<br />
meccanici nei quali ogni passo segue il precedente secondo un insieme<br />
di regole più o meno semplice.</p>
<p>D&#8217;altro canto la progettazione di algoritmi è molto interessante ed<br />
intellettualmente stimolante rappresentando il fulcro di ciò che<br />
chiamiamo programmazione.</p>
<p>Alcune delle cose più semplici che facciamo naturalmente, senza<br />
difficoltà o pensiero cosciente, sono tra le cose più difficili da<br />
esprimere sotto forma di algoritmo. Comprendere un linguaggio naturale<br />
è un buon esempio: lo sappiamo fare tutti ma finora nessuno è stato in<br />
grado di spiegare come ci riusciamo esprimendolo sotto forma di<br />
algoritmo.</p>
<p>13.8 Glossario</p>
<p>Funzione pura<br />
funzione che non modifica gli oggetti ricevuti come parametri.<br />
La maggior parte delle funzioni pure sono produttive.</p>
<p>Modificatore<br />
funzione che cambia uno o più oggetti ricevuti come parametri.<br />
La maggior parte dei modificatori non restituisce valori di<br />
ritorno.</p>
<p>Stile di programmazione funzionale<br />
stile di programmazione dove la maggior parte delle funzioni è<br />
pura.</p>
<p>Sviluppo prototipale<br />
tipo di sviluppo del programma a partire da un prototipo che<br />
viene gradualmente testato, esteso e migliorato.</p>
<p>Sviluppo pianificato<br />
tipo di sviluppo del programma che prevede uno studio<br />
preventivo del problema da risolvere.</p>
<p>Algoritmo<br />
serie di passi per risolvere una classe di problemi in modo<br />
meccanico.</p>
<p>Capitolo 14</p>
<p>Classi e metodi</p>
<p>14.1 Funzionalità orientate agli oggetti</p>
<p>Python è un linguaggio di programmazione orientato agli oggetti il che<br />
significa che fornisce il supporto alla programmazione orientata agli<br />
oggetti.</p>
<p>Non è facile definire cosa sia la programmazione orientata agli<br />
oggetti ma abbiamo già visto alcune delle sue caratteristiche:<br />
* I programmi sono costituiti da definizioni di oggetti e<br />
definizioni di funzioni e la gran parte dell&#8217;elaborazione è<br />
espressa in termini di operazioni sugli oggetti.<br />
* Ogni definizione di oggetto corrisponde ad un oggetto o concetto<br />
del mondo reale e le funzioni che operano su un oggetto<br />
corrispondono a modi reali di interazione tra cose reali.</p>
<p>Per esempio la classe Tempo definita nel capitolo 13 corrisponde al<br />
modo in cui tendiamo a pensare alle ore del giorno e le funzioni che<br />
abbiamo definite corrispondono al genere di operazioni che facciamo<br />
con gli orari. Le classi Punto e Rettangolo sono estremamente simili<br />
ai concetti matematici corrispondenti.</p>
<p>Finora non ci siamo avvantaggiati delle funzionalità di supporto della<br />
programmazione orientata agli oggetti fornite da Python. Sia ben<br />
chiaro che queste funzionalità non sono indispensabili in quanto<br />
forniscono solo una sintassi alternativa per fare qualcosa che<br />
possiamo fare in modi più tradizionali, ma in molti casi questa<br />
alternativa è più concisa e accurata.</p>
<p>Per esempio nel programma Tempo non c&#8217;è una chiara connessione tra la<br />
definizione della classe e le definizioni di funzioni che l&#8217;hanno<br />
seguita: un esame superficiale è sufficiente per accorgersi che tutte<br />
queste funzioni prendono almeno un oggetto Tempo come parametro.</p>
<p>Questa osservazione giustifica la presenza dei metodi. Ne abbiamo già<br />
visto qualcuno nel caso dei dizionari, quando abbiamo invocato keys e<br />
values. Ogni metodo è associato ad una classe ed è destinato ad essere<br />
invocato sulle istanze di quella classe.</p>
<p>I metodi sono simili alle funzioni con due differenze:<br />
* I metodi sono definiti all&#8217;interno della definizione di classe per<br />
rendere più esplicita la relazione tra la classe ed i metodi<br />
corrispondenti.<br />
* La sintassi per invocare un metodo è diversa da quella usata per<br />
chiamare una funzione.</p>
<p>Nelle prossime sezioni prenderemo le funzioni scritte nei due capitoli<br />
precedenti e le trasformeremo in metodi. Questa trasformazione è<br />
puramente meccanica e puoi farla seguendo una serie di semplici passi:<br />
se sei a tuo agio nel convertire tra funzione e metodo e viceversa<br />
riuscirai anche a scegliere di volta in volta la forma migliore.</p>
<p>14.2 StampaTempo</p>
<p>Nel capitolo 13 abbiamo definito una classe chiamata Tempo e scritto<br />
una funzione StampaTempo:</p>
<p>class Tempo:<br />
pass<br />
def StampaTempo(Orario):<br />
print str(Orario.Ore) + &#8220;:&#8221; +<br />
str(Orario.Minuti) + &#8220;:&#8221; +<br />
str(Orario.Secondi)</p>
<p>Per chiamare la funzione abbiamo passato un oggetto Tempo come<br />
parametro:</p>
<p>&gt;&gt;&gt; OraAttuale = Tempo()<br />
&gt;&gt;&gt; OraAttuale.Ore = 9<br />
&gt;&gt;&gt; OraAttuale.Minuti = 14<br />
&gt;&gt;&gt; OraAttuale.Secondi = 30<br />
&gt;&gt;&gt; StampaTempo(OraAttuale)</p>
<p>Per rendere StampaTempo un metodo tutto quello che dobbiamo fare è<br />
muovere la definizione della funzione all&#8217;interno della definizione<br />
della classe. Fai attenzione al cambio di indentazione:</p>
<p>class Tempo:<br />
def StampaTempo(Orario):<br />
print str(Orario.Ore) + &#8220;:&#8221; +   \<br />
str(Orario.Minuti) + &#8220;:&#8221; +  \<br />
str(Orario.Secondi)</p>
<p>Ora possiamo invocare StampaTempo usando la notazione punto.</p>
<p>&gt;&gt;&gt; OraAttuale.StampaTempo()</p>
<p>Come sempre l&#8217;oggetto su cui il metodo è invocato appare prima del<br />
punto ed il nome del metodo subito dopo.</p>
<p>L&#8217;oggetto su cui il metodo è invocato è automaticamente assegnato al<br />
primo parametro, quindi nel caso di OraAttuale è assegnato a Orario.</p>
<p>Per convenzione il primo parametro di un metodo è chiamato self,<br />
traducibile in questo caso come &#8220;l&#8217;oggetto stesso&#8221;.</p>
<p>Come nel caso di StampaTempo(OraAttuale), la sintassi di una chiamata<br />
di funzione tradizionale suggerisce che la funzione sia l&#8217;agente<br />
attivo: equivale pressappoco a dire &#8220;StampaTempo! C&#8217;è un oggetto per<br />
te da stampare!&#8221;</p>
<p>Nella programmazione orientata agli oggetti sono proprio gli oggetti<br />
ad essere considerati l&#8217;agente attivo: un&#8217;invocazione del tipo<br />
OraAttuale.StampaTempo() significa &#8220;OraAttuale! Invoca il metodo per<br />
stampare il tuo valore!&#8221;</p>
<p>Questo cambio di prospettiva non sembra così utile ed effettivamente<br />
negli esempi che abbiamo visto finora è così. Comunque lo spostamento<br />
della responsabilità dalla funzione all&#8217;oggetto rende possibile<br />
scrivere funzioni più versatili e rende più immediati il mantenimento<br />
ed il riutilizzo del codice.</p>
<p>14.3 Un altro esempio</p>
<p>Convertiamo Incremento (dalla sezione 13.3) da funzione a metodo. Per<br />
risparmiare spazio eviteremo di riscrivere il metodo StampaTempo che<br />
abbiamo già definito ma tu lo devi tenere nella tua versione del<br />
programma:</p>
<p>class Tempo:<br />
&#8230;<br />
def Incremento(self, Secondi):<br />
self.Secondi = Secondi + self.Secondi<br />
while self.Secondi &gt;= 60:<br />
self.Secondi = self.Secondi &#8211; 60<br />
self.Minuti = self.Minuti + 1<br />
while self.Minuti &gt;= 60:<br />
self.Minuti = self.Minuti &#8211; 60<br />
self.Ore = self.Ore + 1</p>
<p>D&#8217;ora in poi i tre punti di sospensione &#8230; all&#8217;interno del codice<br />
indicheranno che è stata omessa per questioni di leggibilità una parte<br />
del codice già definito in precedenza.</p>
<p>La trasformazione, come abbiamo già detto, è puramente meccanica:<br />
abbiamo spostato la definizione di una funzione all&#8217;interno di una<br />
definizione di classe e cambiato il nome del primo parametro.</p>
<p>Ora possiamo invocare Incremento come metodo.</p>
<p>OraAttuale.Incremento(500)</p>
<p>Ancora una volta l&#8217;oggetto su cui il metodo è invocato viene<br />
automaticamente assegnato al primo parametro, self. Il secondo<br />
parametro, Secondi, vale 500.</p>
<p>Esercizio: converti ConverteInSecondi della sezione 13.5 a metodo<br />
della classe Tempo.</p>
<p>14.4 Un esempio più complesso</p>
<p>La funzione Dopo è leggermente più complessa perché opera su due<br />
oggetti Tempo e non soltanto su uno com&#8217;è successo per i metodi appena<br />
visti. Uno dei parametri è chiamato self; l&#8217;altro non cambia:</p>
<p>class Tempo:<br />
&#8230;<br />
def Dopo(self, Tempo2):<br />
if self.Ore &gt; Tempo2.Ore:<br />
return 1<br />
if self.Ore &lt; Tempo2.Ore:<br />
return 0<br />
if self.Minuti &gt; Tempo2.Minuti:<br />
return 1<br />
if self.Minuti &lt; Tempo2.Minuti:<br />
return 0<br />
if self.Secondi &gt; Tempo2.Secondi:<br />
return 1<br />
return 0</p>
<p>Invochiamo questo metodo su un oggetto e passiamo l&#8217;altro come<br />
argomento:</p>
<p>if TempoCottura.Dopo(OraAttuale):<br />
print &#8220;Il pranzo e&#8217; pronto&#8221;</p>
<p>14.5 Argomenti opzionali</p>
<p>Abbiamo già visto delle funzioni predefinite che accettano un numero<br />
variabile di argomenti: string.find accetta due, tre o quattro<br />
argomenti.</p>
<p>Possiamo scrivere funzioni con una lista di argomenti opzionali.<br />
Scriviamo la nostra versione di Trova per farle fare la stessa cosa di<br />
string.find.</p>
<p>Ecco la versione originale che abbiamo scritto nella sezione 7.7:</p>
<p>def Trova(Stringa, Carattere):<br />
Indice = 0<br />
while Indice &lt; len(Stringa):<br />
if Stringa[Indice] == Carattere:<br />
return Indice<br />
Indice = Indice + 1<br />
return -1</p>
<p>Questa è la versione aggiornata e migliorata:</p>
<p>def Trova(Stringa, Carattere, Inizio=0):<br />
Indice = Inizio<br />
while Inizio &lt; len(Stringa):<br />
if Stringa[Indice] == Carattere:<br />
return Indice<br />
Indice = Indice + 1<br />
return -1</p>
<p>Il terzo parametro, Inizio, è opzionale perché abbiamo fornito il<br />
valore 0 di default. Se invochiamo Trova con solo due argomenti usiamo<br />
il valore di default per il terzo così da iniziare la ricerca<br />
dall&#8217;inizio della stringa:</p>
<p>&gt;&gt;&gt; Trova(&#8220;Mela&#8221;, &#8220;l&#8221;)<br />
2</p>
<p>Se forniamo un terzo parametro questo sovrascrive il valore di<br />
default:</p>
<p>&gt;&gt;&gt; Trova(&#8220;Mela&#8221;, &#8220;l&#8221;, 3)<br />
-1</p>
<p>Esercizio: aggiungi un quarto parametro, Fine, che specifica dove<br />
interrompere la ricerca.</p>
<p>Attenzione: questo esercizio non è semplice come sembra. Il valore<br />
di default di Fine dovrebbe essere len(Stringa) ma questo non<br />
funziona. I valori di default sono valutati al momento della<br />
definizione della funzione, non quando questa è chiamata: quando<br />
Trova viene definita, Stringa non esiste ancora così non puoi<br />
conoscere la sua lunghezza. Trova un sistema per aggirare<br />
l&#8217;ostacolo.</p>
<p>14.6 Il metodo di inizializzazione</p>
<p>Il metodo di inizializzazione è un metodo speciale invocato quando si<br />
crea un oggetto. Il nome di questo metodo è __init__ (due caratteri di<br />
sottolineatura, seguiti da init e da altri due caratteri di<br />
sottolineatura). Un metodo di inizializzazione per la classe Tempo<br />
potrebbe essere:</p>
<p>class Tempo:<br />
def __init__(self, Ore=0, Minuti=0, Secondi=0):<br />
self.Ore = Ore<br />
self.Minuti = Minuti<br />
self.Secondi = Secondi</p>
<p>Non c&#8217;è conflitto tra l&#8217;attributo self.Ore e il parametro Ore. La<br />
notazione punto specifica a quale variabile ci stiamo riferendo.</p>
<p>Quando invochiamo il costruttore Tempo gli argomenti che passiamo sono<br />
girati a __init__:</p>
<p>&gt;&gt;&gt; OraAttuale = Tempo(9, 14, 30)<br />
&gt;&gt;&gt; OraAttuale.StampaTempo()<br />
&gt;&gt;&gt; 9:14:30</p>
<p>Dato che i parametri sono opzionali possiamo anche ometterli:</p>
<p>&gt;&gt;&gt; OraAttuale = Tempo()<br />
&gt;&gt;&gt; OraAttuale.StampaTempo()<br />
&gt;&gt;&gt; 0:0:0</p>
<p>Possiamo anche fornire solo il primo parametro:</p>
<p>&gt;&gt;&gt; OraAttuale = Tempo(9)<br />
&gt;&gt;&gt; OraAttuale.StampaTempo()<br />
&gt;&gt;&gt; 9:0:0</p>
<p>o i primi due parametri:</p>
<p>&gt;&gt;&gt; OraAttuale = Tempo(9, 14)<br />
&gt;&gt;&gt; OraAttuale.StampaTempo()<br />
&gt;&gt;&gt; 9:14:0</p>
<p>Infine possiamo anche passare un sottoinsieme dei parametri<br />
nominandoli esplicitamente:</p>
<p>&gt;&gt;&gt; OraAttuale = Tempo(Secondi = 30, Ore = 9)<br />
&gt;&gt;&gt; OraAttuale.StampaTempo()<br />
&gt;&gt;&gt; 9:0:30</p>
<p>14.7 La classe Punto rivisitata</p>
<p>Riscriviamo la classe Punto che abbiamo già visto alla sezione 12.1 in<br />
uno stile più orientato agli oggetti:</p>
<p>class Punto:<br />
def __init__(self, x=0, y=0):<br />
self.x = x<br />
self.y = y<br />
def __str__(self):<br />
return &#8216;(&#8216; + str(self.x) + &#8216;, &#8216; + str(self.y) + &#8216;)&#8217;</p>
<p>Il metodo di inizializzazione prende x e y come parametri opzionali.<br />
Il loro valore di default è 0.</p>
<p>Il metodo __str__ ritorna una rappresentazione di un oggetto Punto<br />
sotto forma di stringa. Se una classe fornisce un metodo chiamato<br />
__str__ questo sovrascrive il comportamento abituale della funzione<br />
str di Python.</p>
<p>&gt;&gt;&gt; P = Punto(3, 4)<br />
&gt;&gt;&gt; str(P)<br />
&#8216;(3, 4)&#8217;</p>
<p>La stampa di un oggetto Punto invoca __str__ sull&#8217;oggetto: la<br />
definizione di __str__ cambia dunque anche il comportamento di print:</p>
<p>&gt;&gt;&gt; P = Punto(3, 4)<br />
&gt;&gt;&gt; print P<br />
(3, 4)</p>
<p>Quando scriviamo una nuova classe iniziamo quasi sempre scrivendo<br />
__init__ (la funzione che rende più facile istanziare oggetti) e<br />
__str__ (utile per il debug).</p>
<p>14.8 Ridefinizione di un operatore</p>
<p>Alcuni linguaggi consentono di cambiare la definizione degli operatori<br />
predefiniti quando applicati a tipi definiti dall&#8217;utente. Questa<br />
caratteristica è chiamata ridefinizione dell&#8217;operatore (o &#8220;overloading<br />
dell&#8217;operatore&#8221;) e si rivela molto utile soprattutto quando vogliamo<br />
definire nuovi tipi di operazioni matematiche.</p>
<p>Se vogliamo ridefinire l&#8217;operatore somma + scriveremo un metodo<br />
chiamato __add__:</p>
<p>class Punto:<br />
&#8230;<br />
def __add__(self, AltroPunto):<br />
return Punto(self.x + AltroPunto.x, self.y + AltroPunto.y)</p>
<p>Come al solito il primo parametro è l&#8217;oggetto su cui è invocato il<br />
metodo. Il secondo parametro è chiamato AltroPunto per distinguerlo da<br />
self. Ora sommiamo due oggetti Punto restituendo la somma in un terzo<br />
oggetto Punto che conterrà la somma delle coordinate x e delle<br />
coordinate y.</p>
<p>Quando applicheremo l&#8217;operatore + ad oggetti Punto Python invocherà il<br />
metodo __add__:</p>
<p>&gt;&gt;&gt;   P1 = Punto(3, 4)<br />
&gt;&gt;&gt;   P2 = Punto(5, 7)<br />
&gt;&gt;&gt;   P3 = P1 + P2<br />
&gt;&gt;&gt;   print P3<br />
(8, 11)</p>
<p>L&#8217;espressione P1 + P2 è equivalente a P1.__add__(P2) ma ovviamente più<br />
elegante.</p>
<p>Esercizio: aggiungi il metodo __sub__(self, AltroPunto) che<br />
ridefinisca l&#8217;operatore sottrazione per la classe Punto.</p>
<p>Ci sono parecchi modi per ridefinire l&#8217;operatore moltiplicazione,<br />
aggiungendo il metodo __mul__ o __rmul__ o entrambi.</p>
<p>Se l&#8217;operatore a sinistra di * è un Punto Python invoca __mul__<br />
assumendo che anche l&#8217;altro operando sia un oggetto di tipo Punto. In<br />
questo caso si dovrà calcolare il prodotto punto dei due punti secondo<br />
le regole dell&#8217;algebra lineare:</p>
<p>def __mul__(self, AltroPunto):<br />
return self.x * AltroPunto.x + self.y * AltroPunto.y</p>
<p>Se l&#8217;operando a sinistra di * è un tipo primitivo (e quindi diverso da<br />
un oggetto Punto) e l&#8217;operando a destra è di tipo Punto Python<br />
invocherà __rmul__ per calcolare una moltiplicazione scalare:</p>
<p>def __rmul__(self, AltroPunto):<br />
return Punto(AltroPunto * self.x,  AltroPunto * self.y)</p>
<p>Il risultato della moltiplicazione scalare è un nuovo punto le cui<br />
coordinate sono un multiplo di quelle originali. Se AltroPunto è un<br />
tipo che non può essere moltiplicato per un numero in virgola mobile<br />
__rmul__ produrrà un errore in esecuzione.</p>
<p>Questo esempio mostra entrambi i tipi di moltiplicazione:</p>
<p>&gt;&gt;&gt; P1 = Punto(3, 4)<br />
&gt;&gt;&gt; P2 = Punto(5, 7)<br />
&gt;&gt;&gt; print P1 * P2<br />
43<br />
&gt;&gt;&gt; print 2 * P2<br />
(10, 14)</p>
<p>Cosa accade se proviamo a valutare P2 * 2? Dato che il primo parametro<br />
è un Punto Python invoca __mul__ con 2 come secondo argomento.<br />
All&#8217;interno di __mul__ il programma prova ad accedere la coordinata x<br />
di AltroPunto e questo tentativo genera un errore dato che un numero<br />
intero non ha attributi:</p>
<p>&gt;&gt;&gt; print P2 * 2<br />
AttributeError: &#8216;int&#8217; object has no attribute &#8216;x&#8217;</p>
<p>Questo messaggio d&#8217;errore è effettivamente troppo sibiliino per<br />
risultare di una qualche utilità, e questo è ottimo esempio delle<br />
difficoltà che puoi incontrare nella programmazione ad oggetti: non è<br />
sempre semplice capire quale sia il codice che ha causato l&#8217;errore.</p>
<p>Per un trattato più esauriente sulla ridefinizione degli operatori<br />
vedi l&#8217;appendice B.</p>
<p>14.9 Polimorfismo</p>
<p>La maggior parte dei metodi che abbiamo scritto finora lavorano solo<br />
per un tipo specifico di dati. Quando crei un nuovo oggetto scrivi dei<br />
metodi che lavorano su oggetti di quel tipo.</p>
<p>Ci sono comunque operazioni che vorresti poter applicare a molti tipi<br />
come ad esempio le operazioni matematiche che abbiamo appena visto. Se<br />
più tipi di dato supportano lo stesso insieme di operazioni puoi<br />
scrivere funzioni che lavorano indifferentemente con ciascuno di<br />
questi tipi.</p>
<p>Per esempio l&#8217;operazione MoltSomma (comune in algebra lineare) prende<br />
tre parametri: il risultato è la moltiplicazione dei primi due e la<br />
successiva somma del terzo al prodotto. Possiamo scriverla così:</p>
<p>def MoltSomma(x, y, z):<br />
return x * y + z</p>
<p>Questo metodo lavorerà per tutti i valori di x e y che possono essere<br />
moltiplicati e per ogni valore di z che può essere sommato al<br />
prodotto.</p>
<p>Possiamo invocarla con valori numerici:</p>
<p>&gt;&gt;&gt; MoltSomma(3, 2, 1)<br />
7</p>
<p>o con oggetti di tipo Punto:</p>
<p>&gt;&gt;&gt; P1 = Punto(3, 4)<br />
&gt;&gt;&gt; P2 = Punto(5, 7)<br />
&gt;&gt;&gt; print MoltSomma(2, P1, P2)<br />
(11, 15)<br />
&gt;&gt;&gt; print MoltSomma(P1, P2, 1)<br />
44</p>
<p>Nel primo caso il punto P1 è moltiplicato per uno scalare e il<br />
prodotto è poi sommato a un altro punto (P2). Nel secondo caso il<br />
prodotto punto produce un valore numerico al quale viene sommato un<br />
altro valore numerico.</p>
<p>Una funzione che accetta parametri di tipo diverso è chiamata<br />
polimorfica.</p>
<p>Come esempio ulteriore consideriamo il metodo DirittoERovescio che<br />
stampa due volte una stringa, prima direttamente e poi all&#8217;inverso:</p>
<p>def DirittoERovescio(Stringa):<br />
import copy<br />
Rovescio = copy.copy(Stringa)<br />
Rovescio.reverse()<br />
print str(Stringa) + str(Rovescio)</p>
<p>Dato che il metodo reverse è un modificatore si deve fare una copia<br />
della stringa prima di rovesciarla: in questo modo il metodo reverse<br />
non modificherà la lista originale ma solo una sua copia.</p>
<p>Ecco un esempio di funzionamento di DirittoERovescio con le liste:</p>
<p>&gt;&gt;&gt;   Lista = [1, 2, 3, 4]<br />
&gt;&gt;&gt;   DirittoERovescio(Lista)<br />
[1, 2, 3, 4][4, 3, 2, 1]</p>
<p>Era facilmente intuibile che questa funzione riuscisse a maneggiare le<br />
liste. Ma può lavorare con oggetti di tipo Punto?</p>
<p>Per determinare se una funzione può essere applicata ad un tipo nuovo<br />
applichiamo la regola fondamentale del polimorfismo:</p>
<p>Se tutte le operazioni all&#8217;interno della funzione possono essere<br />
applicate ad un tipo di dato allora la funzione stessa può essere<br />
applicata al tipo.</p>
<p>Le operazioni nel metodo DirittoERovescio includono copy, reverse e<br />
print.</p>
<p>copy funziona su ogni oggetto e abbiamo già scritto un metodo __str__<br />
per gli oggetti di tipo Punto così l&#8217;unica cosa che ancora ci manca è<br />
il metodo reverse:</p>
<p>def reverse(self):<br />
self.x , self.y = self.y, self.x</p>
<p>Ora possiamo passare Punto a DirittoERovescio:</p>
<p>&gt;&gt;&gt;   P = Punto(3, 4)<br />
&gt;&gt;&gt;   DirittoERovescio(P)<br />
(3, 4)(4, 3)</p>
<p>Il miglior tipo di polimorfismo è quello involontario, quando scopri<br />
che una funzione già scritta può essere applicata ad un tipo di dati<br />
per cui non era stata pensata.</p>
<p>14.10 Glossario</p>
<p>Linguaggio orientato agli oggetti<br />
linguaggio che è dotato delle caratteristiche che facilitano la<br />
programmazione orientata agli oggetti, tipo la possibilità di<br />
definire classi e l&#8217;ereditarietà.</p>
<p>Programmazione orientata agli oggetti<br />
stile di programmazione nel quale i dati e le operazioni che li<br />
manipolano sono organizzati in classi e metodi.</p>
<p>Metodo<br />
funzione definita all&#8217;interno di una definizione di classe<br />
invocata su istanze di quella classe.</p>
<p>Ridefinire<br />
rimpiazzare un comportamento o un valore di default, scrivendo<br />
un metodo con lo stesso nome o rimpiazzando un parametro di<br />
default con un valore particolare.</p>
<p>Metodo di inizializzazione<br />
metodo speciale invocato automaticamente nel momento in cui<br />
viene creato un nuovo oggetto e usato per inizializzare gli<br />
attributi dell&#8217;oggetto stesso.</p>
<p>Ridefinizione dell&#8217;operatore<br />
estensione degli operatori predefiniti (+, -, *, &gt;, &lt;, ecc.)<br />
per farli lavorare con i tipi definiti dall&#8217;utente.</p>
<p>Prodotto punto<br />
operazione definita nell&#8217;algebra lineare che moltiplica due<br />
punti e produce un valore numerico.</p>
<p>Moltiplicazione scalare<br />
operazione definita nell&#8217;algebra lineare che moltiplica ognuna<br />
delle coordinate di un punto per un valore numerico.</p>
<p>Funzione polimorfica<br />
funzione che può operare su più di un tipo di dati. Se tutte le<br />
operazioni in una funzione possono essere applicate ad un tipo<br />
di dato allora la funzione può essere applicata al tipo.</p>
<p>Capitolo 15</p>
<p>Insiemi di oggetti</p>
<p>15.1 Composizione</p>
<p>Uno dei primi esempi di composizione che hai visto è stato l&#8217;uso di<br />
un&#8217;invocazione di un metodo all&#8217;interno di un&#8217;espressione. Un altro<br />
esempio è stata la struttura di istruzioni annidate, con un if<br />
all&#8217;interno di un ciclo while all&#8217;interno di un altro if e così via.</p>
<p>Dopo aver visto questo modo di operare e aver analizzato le liste e<br />
gli oggetti, non dovresti essere sorpreso del fatto che puoi anche<br />
creare liste di oggetti. Non solo: puoi creare oggetti che contengono<br />
liste come attributi, o liste che contengono liste, oggetti che<br />
contengono oggetti e così via.</p>
<p>In questo capitolo e nel prossimo vedremo alcuni esempi di queste<br />
combinazioni usando l&#8217;oggetto Carta.</p>
<p>15.2 Oggetto Carta</p>
<p>Se non hai dimestichezza con le comuni carte da gioco adesso è il<br />
momento di prendere in mano un mazzo di carte, altrimenti questo<br />
capitolo non avrà molto senso. Per i nostri scopi considereremo un<br />
mazzo di carte americano: questo mazzo è composto da 52 carte, ognuna<br />
delle quali appartiene a un seme (picche, cuori, quadri, fiori,<br />
nell&#8217;ordine di importanza nel gioco del bridge) ed è identificata da<br />
un numero da 1 a 13 (detto &#8220;rango&#8221;). I valori rappresentano, in ordine<br />
crescente, l&#8217;Asso, la serie numerica da 2 a 10, il Jack, la Regina ed<br />
il Re. A seconda del gioco a cui stai giocando il valore dell&#8217;Asso può<br />
essere considerato inferiore al 2 o superiore al Re.</p>
<p>Volendo definire un nuovo oggetto per rappresentare una carta da gioco<br />
è ovvio che gli attributi devono essere il rango ed il seme. Non è<br />
invece evidente di che tipo debbano essere gli attributi. Una<br />
possibilità è quella di usare stringhe contenenti il seme (&#8220;Cuori&#8221;) e<br />
il rango (&#8220;Regina&#8221;) solo che in questo modo non c&#8217;è un sistema<br />
semplice per vedere quale carta ha il rango o il seme più elevato.</p>
<p>Un&#8217;alternativa è quella di usare gli interi per codificare il rango e<br />
il seme. Con &#8220;codifica&#8221; non intendiamo crittografie o traduzioni in<br />
codice segreto ma semplicemente la definizione che lega una sequenza<br />
di numeri agli oggetti che essi vogliono rappresentare. Per esempio:</p>
<p>Picche -&gt; 3<br />
Cuori  -&gt; 2<br />
Quadri -&gt; 1<br />
Fiori  -&gt; 0</p>
<p>Un utile effetto pratico di questa mappatura è il fatto che possiamo<br />
confrontare i semi tra di loro determinando subito quale vale di più.<br />
La mappatura per il rango è abbastanza ovvia: per le carte numeriche<br />
il rango è il numero della carta mentre per le carte figurate usiamo<br />
queste associazioni:</p>
<p>Asso   -&gt; 1<br />
Jack   -&gt; 11<br />
Regina -&gt; 12<br />
Re     -&gt; 13</p>
<p>Cominciamo con il primo abbozzo di definizione di Carta e come sempre<br />
forniamo anche un metodo di inizializzazione dei suoi attributi:</p>
<p>class Carta:<br />
def __init__(self, Seme=0, Rango=0):<br />
self.Seme = Seme<br />
self.Rango = Rango</p>
<p>Per creare un oggetto che rappresenta il 3 di fiori useremo:</p>
<p>TreDiFiori = Carta(0, 3)</p>
<p>dove il primo argomento (0) rappresenta il seme fiori ed il secondo<br />
(3) il rango della carta.</p>
<p>15.3 Attributi della classe e metodo __str__</p>
<p>Per stampare oggetti di tipo Carta in un modo facilmente comprensibile<br />
possiamo mappare i codici interi con stringhe. Assegniamo pertanto due<br />
liste di stringhe all&#8217;inizio della definizione della classe:</p>
<p>class Carta:<br />
ListaSemi = ["Fiori", "Quadri", "Cuori", "Picche"]<br />
ListaRanghi = ["impossibile", "Asso", "2", "3", "4", "5", "6",\<br />
"7", "8", "9", "10", "Jack", "Regina", "Re"]<br />
def __init__(self, Seme=0, Rango=0):<br />
self.Seme = Seme<br />
self.Rango = Rango<br />
def __str__(self):<br />
return (self.ListaRanghi[self.Rango] + &#8221; di &#8221; +<br />
self.ListaSemi[self.Seme])</p>
<p>Le due liste sono in questo caso degli attributi di classe che sono<br />
definiti all&#8217;esterno dei metodi della classe e possono essere<br />
utilizzati da qualsiasi metodo della classe.</p>
<p>All&#8217;interno di __str__ possiamo allora usare ListaSemi e ListaRanghi<br />
per far corrispondere i valori numerici di Seme e Rango a delle<br />
stringhe. Per fare un esempio l&#8217;espressione self.ListaSemi[self.Seme]<br />
significa &#8220;usa l&#8217;attributo Seme dell&#8217;oggetto self come indice<br />
nell&#8217;attributo di classe chiamato ListaSemi e restituisci la stringa<br />
appropriata&#8221;.</p>
<p>Il motivo della presenza dell&#8217;elemento &#8220;impossibile&#8221; nel primo<br />
elemento di ListaRanghi è di agire come segnaposto per l&#8217;elemento 0<br />
che non dovrebbe mai essere usato dato che il rango ha valori da 1 a<br />
13. Meglio sprecare un elemento della lista piuttosto che dover<br />
scalare tutti i ranghi di una posizione e dover far corrispondere<br />
l&#8217;asso allo 0, il due all&#8217;1, il tre al 2, eccetera, con il rischio di<br />
sbagliare.</p>
<p>Con i metodi che abbiamo scritto finora possiamo già creare e stampare<br />
le carte:</p>
<p>&gt;&gt;&gt; Carta1 = Carta(1, 11)<br />
&gt;&gt;&gt; print Carta1<br />
Jack di Quadri</p>
<p>Gli attributi di classe come ListaSemi sono condivisi da tutti gli<br />
oggetti Carta. Il vantaggio è che possiamo usare qualsiasi oggetto<br />
Carta per accedere agli attributi di classe:</p>
<p>&gt;&gt;&gt; Carta2 = Carta(1, 3)<br />
&gt;&gt;&gt; print Carta2<br />
3 di Quadri<br />
&gt;&gt;&gt; print Carta2.ListaSemi[1]<br />
Quadri</p>
<p>Lo svantaggio sta nel fatto che se modifichiamo un attributo di classe<br />
questo cambiamento si riflette in ogni istanza della classe. Per<br />
esempio se decidessimo di cambiare il seme &#8220;Quadri&#8221; in &#8220;Bastoni&#8221;&#8230;</p>
<p>&gt;&gt;&gt; Carta1.ListaSemi[1] = &#8220;Bastoni&#8221;<br />
&gt;&gt;&gt; print Carta1<br />
Jack di Bastoni</p>
<p>&#8230;tutti i Quadri diventerebbero dei Bastoni:</p>
<p>&gt;&gt;&gt; print Carta2<br />
3 di Bastoni</p>
<p>Non è solitamente una buona idea modificare gli attributi di classe.</p>
<p>15.4 Confronto tra carte</p>
<p>Per i tipi primitivi sono già definiti operatori condizionali (&lt;, &gt;,<br />
==, ecc.) che confrontano i valori e determinano se un operatore è più<br />
grande, più piccolo o uguale ad un altro. \ Per i tipi definiti<br />
dall&#8217;utente possiamo ridefinire il comportamento di questi operatori<br />
aggiungendo il metodo __cmp__. Per convenzione __cmp__ prende due<br />
parametri, self e Altro, e ritorna 1 se il primo è il più grande, -1<br />
se è più grande il secondo e 0 se sono uguali.</p>
<p>Alcuni tipi sono completamente ordinati, il che significa che puoi<br />
confrontare due elementi qualsiasi e determinare sempre quale sia il<br />
più grande tra di loro. Per esempio i numeri interi e quelli in<br />
virgola mobile sono completamente ordinati. Altri tipi sono<br />
disordinati, nel senso che non esiste un modo logico per stabilire<br />
quale sia il più grande, così come non è possibile stabilire tra una<br />
serie di colori quale sia il &#8220;minore&#8221;.</p>
<p>L&#8217;insieme delle carte da gioco è parzialmente ordinato e ciò significa<br />
che qualche volta puoi confrontare due carte e qualche volta no. Per<br />
fare un esempio sai che il 3 di Fiori è più alto del 2 di Fiori e il 3<br />
di Quadri più alto del 3 di Fiori. Fino a questo punto il loro valore<br />
relativo e il conseguente ordine sono chiari. Ma qual è la carta più<br />
alta se dobbiamo scegliere tra 3 di Fiori e 2 di Quadri? Una ha il<br />
rango più alto, l&#8217;altra il seme.</p>
<p>Per rendere confrontabili le carte dobbiamo innanzitutto decidere<br />
quale attributo sia il più importante, se il rango o il seme. La<br />
scelta è arbitraria e per il nostro studio decideremo che il seme ha<br />
priorità rispetto al rango.</p>
<p>Detto questo possiamo scrivere __cmp__:</p>
<p>def __cmp__(self, Altro):<br />
# controlla il seme<br />
if self.Seme &gt; Altro.Seme: return 1<br />
if self.Seme &lt; Altro.Seme: return -1<br />
# se i semi sono uguali controlla il rango<br />
if self.Rango &gt; Altro.Rango: return 1<br />
if self.Rango &lt; Altro.Rango: return -1<br />
# se anche i ranghi sono uguali le carte sono uguali!<br />
return 0</p>
<p>In questo tipo di ordinamento gli Assi hanno valore più basso dei 2.</p>
<p>Esercizio: modifica __cmp__ così da rendere gli Assi più importanti<br />
dei Re.</p>
<p>15.5 Mazzi</p>
<p>Ora che abbiamo oggetti per rappresentare le carte il passo più logico<br />
è quello di definire una classe per rappresentare il Mazzo. Il mazzo è<br />
composto di carte così ogni oggetto Mazzo conterrà una lista di carte<br />
come attributo.</p>
<p>Quella che segue è la definizione di classe della classe Mazzo. Il<br />
metodo di inizializzazione crea l&#8217;attributo Carte e genera le 52 carte<br />
standard:</p>
<p>class Mazzo:<br />
def __init__(self):<br />
self.Carte = []<br />
for Seme in range(4):<br />
for Rango in range(1, 14):<br />
self.Carte.append(Carta(Seme, Rango))</p>
<p>Il modo più semplice per creare un mazzo è per mezzo di un ciclo<br />
annidato: il ciclo esterno numera i semi da 0 a 3, quello interno i<br />
ranghi da 1 a 13. Dato che il ciclo esterno viene eseguito 4 volte e<br />
quello interno 13 il corpo è eseguito un totale di 52 volte (4 per<br />
13). Ogni iterazione crea una nuova istanza di Carta con seme e rango<br />
correnti ed aggiunge la carta alla lista Carte.</p>
<p>Il metodo append lavora sulle liste ma non sulle tuple (che sono<br />
immutabili).</p>
<p>15.6 Stampa del mazzo</p>
<p>Com&#8217;è consueto dopo aver creato un nuovo tipo di oggetto è utile<br />
scrivere un metodo per poterne stampare il contenuto. Per stampare<br />
Mazzo attraversiamo la lista stampando ogni elemento Carta:</p>
<p>class Mazzo:<br />
&#8230;<br />
def StampaMazzo(self):<br />
for Carta in self.Carte:<br />
print Carta</p>
<p>Come alternativa a StampaMazzo potremmo anche riscrivere il metodo<br />
__str__ per la classe Mazzo. Il vantaggio nell&#8217;uso di __str__ sta nel<br />
fatto che è più flessibile. Piuttosto che limitarsi a stampare il<br />
contenuto di un oggetto __str__ genera infatti una rappresentazione<br />
sotto forma di stringa che altre parti del programma possono<br />
manipolare o che può essere memorizzata in attesa di essere usata in<br />
seguito.</p>
<p>Ecco una versione di __str__ che ritorna una rappresentazione di un<br />
Mazzo come stringa. Tanto per aggiungere qualcosa facciamo anche in<br />
modo di indentare ogni carta rispetto alla precedente:</p>
<p>class Mazzo:<br />
&#8230;<br />
def __str__(self):<br />
s = &#8220;&#8221;<br />
for i in range(len(self.Carte)):<br />
s = s + &#8221; &#8220;*i + str(self.Carte[i]) + &#8220;\n&#8221;<br />
return s</p>
<p>Questo esempio mostra un bel po&#8217; di cose.</p>
<p>Prima di tutto invece di attraversare self.Carte e assegnare ogni<br />
carta ad una variabile stiamo usando i come variabile del ciclo e come<br />
indice della lista delle carte.</p>
<p>In secondo luogo stiamo usando l&#8217;operatore di moltiplicazione delle<br />
stringhe per indentare le carte. L&#8217;espressione &#8221; &#8220;*i infatti produce<br />
un numero di spazi pari a i.</p>
<p>Terzo, invece di usare un comando print per stampare le carte usiamo<br />
la funzione str. Passare un oggetto come argomento a str è equivalente<br />
ad invocare il metodo __str__ sull&#8217;oggetto.</p>
<p>Infine stiamo usando la variabile s come accumulatore. Inizialmente s<br />
è una stringa vuota. Ogni volta che passiamo attraverso il ciclo viene<br />
generata e concatenata a s una nuova stringa. Quando il ciclo termina<br />
s contiene la rappresentazione completa dell&#8217;oggetto Mazzo sotto forma<br />
di stringa:</p>
<p>&gt;&gt;&gt; Mazzo1 = Mazzo()<br />
&gt;&gt;&gt; print Mazzo1<br />
Asso di Fiori<br />
2 di Fiori<br />
3 di Fiori<br />
4 di Fiori<br />
5 di Fiori<br />
6 di Fiori<br />
7 di Fiori<br />
8 di Fiori<br />
9 di Fiori<br />
10 di Fiori<br />
Jack di Fiori<br />
Regina di Fiori<br />
Re di Fiori<br />
Asso di Quadri<br />
&#8230;</p>
<p>Anche se il risultato appare come una serie di 52 righe (una per ogni<br />
carta) in realtà si tratta di una singola stringa che contiene<br />
caratteri di ritorno a capo per poter essere stampata su più righe.</p>
<p>15.7 Mescolare il mazzo</p>
<p>Se un mazzo è perfettamente mescolato ogni carta ha la stessa<br />
probabilità di comparire in una posizione qualsiasi.</p>
<p>Per mescolare il mazzo useremo la funzione randrange del modulo<br />
random. randrange prende due argomenti interi (a e b) e sceglie un<br />
numero casuale intero nell&#8217;intervallo a &lt;= x &lt; b. Dato che il limite<br />
superiore è escluso possiamo usare la lunghezza di una lista come<br />
secondo parametro avendo la garanzia della validità dell&#8217;indice.<br />
Questa espressione sceglie l&#8217;indice di una carta casuale nel mazzo:</p>
<p>random.randrange(0, len(self.Carte))</p>
<p>Un modo utile per mescolare un mazzo è scambiare ogni carta con<br />
un&#8217;altra scelta a caso. È possibile che la carta possa essere<br />
scambiata con se stessa ma questa situazione è perfettamente<br />
accettabile. Infatti se escludessimo questa possibilità l&#8217;ordine delle<br />
carte sarebbe meno casuale:</p>
<p>class Mazzo:<br />
&#8230;<br />
def Mescola(self):<br />
import random<br />
NumCarte = len(self.Carte)<br />
for i in range(NumCarte):<br />
j = random.randrange(i, NumCarte)<br />
self.Carte[i], self.Carte[j] = self.Carte[j], self.Carte[i]</p>
<p>Piuttosto che partire dal presupposto che le carte del mazzo siano<br />
sempre 52 abbiamo scelto di ricavare la lunghezza della lista e<br />
memorizzarla in NumCarte.</p>
<p>Per ogni carta del mazzo abbiamo scelto casualmente una carta tra<br />
quelle non ancora mescolate. Poi abbiamo scambiato la carta corrente<br />
(i) con la carta selezionata (j). Per scambiare le due carte abbiamo<br />
usato un&#8217;assegnazione di una tupla, come si è già visto nella sezione<br />
9.2:</p>
<p>self.Carte[i], self.Carte[j] = self.Carte[j], self.Carte[i]</p>
<p>Esercizio: riscrivi questa riga di codice senza usare<br />
un&#8217;assegnazione di una tupla.</p>
<p>15.8 Rimuovere e distribuire le carte</p>
<p>Un altro metodo utile per la classe Mazzo è RimuoviCarta che permette<br />
di rimuovere una carta dal mazzo ritornando vero (1) se la carta era<br />
presente e falso (0) in caso contrario:</p>
<p>class Mazzo:<br />
&#8230;<br />
def RimuoviCarta(self, Carta):<br />
if Carta in self.Carte:<br />
self.Carte.remove(Carta)<br />
return 1<br />
else:<br />
return 0</p>
<p>L&#8217;operatore in ritorna vero se il primo operando è contenuto nel<br />
secondo. Quest&#8217;ultimo deve essere una lista o una tupla. Se il primo<br />
operando è un oggetto, Python usa il metodo __cmp__ dell&#8217;oggetto per<br />
determinare l&#8217;uguaglianza tra gli elementi della lista. Dato che<br />
__cmp__ nella classe Carta controlla l&#8217;uguaglianza forte il metodo<br />
RimuoviCarta usa anch&#8217;esso l&#8217;uguaglianza forte.</p>
<p>Per distribuire le carte si deve poter rimuovere la prima carta del<br />
mazzo e il metodo delle liste pop fornisce un ottimo sistema per<br />
farlo:</p>
<p>class Mazzo:<br />
&#8230;<br />
def PrimaCarta(self):<br />
return self.Carte.pop()</p>
<p>In realtà pop rimuove l&#8217;ultima carta della lista, così stiamo in<br />
effetti togliendo dal fondo del mazzo, ma dal nostro punto di vista<br />
questa anomalia è indifferente.</p>
<p>Una operazione che può essere utile è la funzione booleana EVuoto che<br />
ritorna vero (1) se il mazzo non contiene più carte:</p>
<p>class Mazzo:<br />
&#8230;<br />
def EVuoto(self):<br />
return (len(self.Carte) == 0)</p>
<p>15.9 Glossario</p>
<p>Mappare<br />
rappresentare un insieme di valori usando un altro insieme di<br />
valori e costruendo una mappa di corrispondenza tra i due<br />
insiemi.</p>
<p>Codificare<br />
in campo informatico sinonimo di mappare.</p>
<p>Attributo di classe<br />
variabile definita all&#8217;interno di una definizione di classe ma<br />
al di fuori di qualsiasi metodo. Gli attributi di classe sono<br />
accessibili da ognuno dei metodi della classe e sono condivisi<br />
da tutte le istanze della classe.</p>
<p>Accumulatore<br />
variabile usata in un ciclo per accumulare una serie di valori,<br />
concatenati sotto forma di stringa o sommati per ottenere un<br />
valore totale.</p>
<p>Capitolo 16</p>
<p>Ereditarietà</p>
<p>16.1 Ereditarietà</p>
<p>La caratteristica più frequentemente associata alla programmazione ad<br />
oggetti è l&#8217;ereditarietà che è la capacità di definire una nuova<br />
classe come versione modificata di una classe già esistente.</p>
<p>Il vantaggio principale dell&#8217;ereditarietà è che si possono aggiungere<br />
nuovi metodi ad una classe senza dover modificare la definizione<br />
originale. È chiamata &#8220;ereditarietà&#8221; perché la nuova classe &#8220;eredita&#8221;<br />
tutti i metodi della classe originale. Estendendo questa metafora la<br />
classe originale è spesso definita &#8220;genitore&#8221; e la classe derivata<br />
&#8220;figlia&#8221; o &#8220;sottoclasse&#8221;.</p>
<p>L&#8217;ereditarietà è una caratteristica potente e alcuni programmi possono<br />
essere scritti in modo molto più semplice e conciso grazie ad essa,<br />
dando inoltre la possibilità di personalizzare il comportamento di una<br />
classe senza modificare l&#8217;originale. Il fatto stesso che la struttura<br />
dell&#8217;ereditarietà possa riflettere quella del problema può rendere in<br />
qualche caso il programma più semplice da capire.</p>
<p>D&#8217;altro canto l&#8217;ereditarietà può rendere più difficile la lettura del<br />
programma, visto che quando si invoca un metodo non è sempre chiaro<br />
dove questo sia stato definito (se all&#8217;interno del genitore o delle<br />
classi da questo derivate) con il codice che deve essere rintracciato<br />
all&#8217;interno di più moduli invece che essere in un unico posto ben<br />
definito. Molte delle cose che possono essere fatte con l&#8217;ereditarietà<br />
possono essere di solito gestite elegantemente anche senza di essa, ed<br />
è quindi il caso di usarla solo se la struttura del problema la<br />
richiede: se usata nel momento sbagliato può arrecare più danni che<br />
apportare benefici.</p>
<p>In questo capitolo mostreremo l&#8217;uso dell&#8217;ereditarietà come parte di un<br />
programma che gioca a Old Maid, un gioco di carte piuttosto meccanico<br />
e semplice. Anche se implementeremo un gioco particolare uno dei<br />
nostri scopi è quello di scrivere del codice che possa essere<br />
riutilizzato per implementare altri tipi di giochi di carte.</p>
<p>16.2 Una mano</p>
<p>Per la maggior parte dei giochi di carte abbiamo la necessità di<br />
rappresentare una mano di carte. La mano è simile al mazzo, dato che<br />
entrambi sono insiemi di carte e richiedono metodi per aggiungere e<br />
rimuovere carte. Inoltre abbiamo bisogno sia per la mano che per il<br />
mazzo di poter mescolare le carte.</p>
<p>La mano si differenzia dal mazzo perché, a seconda del gioco, possiamo<br />
avere la necessità di effettuare su una mano alcuni tipi di operazioni<br />
che per un mazzo non avrebbero senso: nel poker posso avere l&#8217;esigenza<br />
di classificare una mano (full, colore, ecc.) o confrontarla con<br />
un&#8217;altra mano mentre nel bridge devo poter calcolare il punteggio di<br />
una mano per poter effettuare una puntata.</p>
<p>Questa situazione suggerisce l&#8217;uso dell&#8217;ereditarietà: se creiamo Mano<br />
come sottoclasse di Mazzo avremo immediatamente disponibili tutti i<br />
metodi di Mazzo con la possibilità di riscriverli o di aggiungerne<br />
altri.</p>
<p>Nella definizione della classe figlia il nome del genitore compare tra<br />
parentesi:</p>
<p>class Mano(Mazzo):<br />
pass</p>
<p>Questa istruzione indica che la nuova classe Mano eredita dalla classe<br />
già esistente Mazzo.</p>
<p>Il costruttore Mano inizializza gli attributi della mano, che sono il<br />
Nome e le Carte. La stringa Nome identifica la mano ed è probabilmente<br />
il nome del giocatore che la sta giocando: è un parametro opzionale<br />
che per default è una stringa vuota. Carte è la lista delle carte<br />
nella mano, inizializzata come lista vuota:</p>
<p>class Mano(Mazzo):<br />
def __init__(self, Nome=&#8221;"):<br />
self.Carte = []<br />
self.Nome = Nome</p>
<p>In quasi tutti i giochi di carte è necessario poter aggiungere e<br />
rimuovere carte dalla mano. Della rimozione ce ne siamo già occupati,<br />
dato che Mano eredita immediatamente RimuoviCarta da Mazzo. Dobbiamo<br />
invece scrivere AggiungeCarta:</p>
<p>class Mano(Mazzo):<br />
def __init__(self, Nome=&#8221;"):<br />
self.Carte = []<br />
self.Nome = Nome<br />
def AggiungeCarta(self,Carta) :<br />
self.Carte.append(Carta)</p>
<p>Il metodo di lista append aggiunge una nuova carta alla fine della<br />
lista di carte.</p>
<p>16.3 Distribuire le carte</p>
<p>Ora che abbiamo una classe Mano vogliamo poter spostare delle carte<br />
dal Mazzo alle singole mani. Non è immediatamente ovvio se questo<br />
metodo debba essere inserito nella classe Mano o nella classe Mazzo ma<br />
dato che opera su un mazzo singolo e (probabilmente) su più mani è più<br />
naturale inserirlo in Mazzo.</p>
<p>Il metodo Distribuisci dovrebbe essere abbastanza generale da poter<br />
essere usato in vari giochi e deve permettere la distribuzione tanto<br />
dell&#8217;intero mazzo che di una singola carta.</p>
<p>Distribuisci prende due argomenti: una lista (o tupla) di mani e il<br />
numero totale di carte da distribuire. Se non ci sono carte<br />
sufficienti per la distribuzione il metodo distribuisce quelle in suo<br />
possesso e poi si ferma:</p>
<p>class Mazzo:<br />
&#8230;<br />
def Distribuisci(self, ListaMani, NumCarte=999):<br />
NumMani = len(ListaMani)<br />
for i in range(NumCarte):<br />
if self.EVuoto(): break         # si ferma se non ci sono<br />
# ulteriori carte<br />
Carta = self.PrimaCarta()       # prende la carta superiore<br />
# del mazzo<br />
Mano = ListaMani[i % NumMani]   # di chi e&#8217; il prossimo<br />
# turno?<br />
Mano.AggiungeCarta(Carta)       # aggiungi la carta alla<br />
# mano</p>
<p>Il secondo parametro, NumCarte, è opzionale; il valore di default è<br />
molto grande per essere certi che vengano distribuite tutte le carte<br />
del mazzo.</p>
<p>La variabile del ciclo i va da 0 a NumCarte-1. Ogni volta che viene<br />
eseguito il corpo del ciclo, la prima carta del mazzo viene rimossa<br />
usando il metodo di lista pop che rimuove e ritorna l&#8217;ultimo valore di<br />
una lista.</p>
<p>L&#8217;operatore modulo (%) ci permette di distribuire le carte in modo<br />
corretto, una carta alla volta per ogni mano: quando i è uguale al<br />
numero delle mani nella lista l&#8217;espressione i % NumMani restituisce 0<br />
permettendo di ricominciare dal primo elemento della lista delle mani.</p>
<p>16.4 Stampa di una mano</p>
<p>Per stampare il contenuto di una mano possiamo avvantaggiarci dei<br />
metodi StampaMazzo e __str__ ereditati da Mazzo. Per esempio:</p>
<p>&gt;&gt;&gt; Mazzo1 = Mazzo()<br />
&gt;&gt;&gt; Mazzo1.Mescola()<br />
&gt;&gt;&gt; Mano1 = Mano(&#8220;pippo&#8221;)<br />
&gt;&gt;&gt; Mazzo1.Distribuisci([Mano1], 5)<br />
&gt;&gt;&gt; print Mano1<br />
2 di Picche<br />
3 di Picche<br />
4 di Picche<br />
Asso di Cuori<br />
9 di Fiori</p>
<p>Anche se è comodo ereditare da metodi esistenti può essere necessario<br />
modificare il metodo __str__ nella classe Mano per aggiungere qualche<br />
informazione, ridefinendo il metodo omonimo ereditato dalla classe<br />
Mazzo:</p>
<p>class Mano(Mazzo)<br />
&#8230;<br />
def __str__(self):<br />
s = &#8220;La mano di &#8221; + self.Nome<br />
if self.EVuoto():<br />
s = s + &#8221; e&#8217; vuota\n&#8221;<br />
else:<br />
s = s + &#8221; contiene queste carte:\n&#8221;<br />
return s + Mazzo.__str__(self)</p>
<p>s è una stringa che inizialmente indica chi è il proprietario della<br />
mano. Se la mano è vuota vengono aggiunte ad s le parole &#8220;e&#8217; vuota&#8221; e<br />
viene ritornata s. IN caso contrario vengono aggiunte le parole<br />
&#8220;contiene queste carte&#8221; e la rappresentazione della mano sotto forma<br />
di stringa già vista in Mazzo, elaborata invocando il metodo __str__<br />
della classe Mazzo su self.</p>
<p>Potrebbe sembrarti strano il fatto di usare self, che si riferisce<br />
alla mano corrente, con un metodo appartenente alla classe Mazzo:<br />
ricorda che Mano è un tipo di Mazzo. Gli oggetti Mano possono fare<br />
qualsiasi cosa di cui è capace Mazzo e così è legale invocare un<br />
metodo Mazzo con la mano self.</p>
<p>In genere è sempre legale usare un&#8217;istanza di una sottoclasse invece<br />
di un&#8217;istanza della classe genitore.</p>
<p>16.5 La classe GiocoDiCarte</p>
<p>La classe GiocoDiCarte si occupa delle operazioni comuni in tutti i<br />
giochi di carte, quali possono essere la creazione del mazzo ed il<br />
mescolamento delle sue carte:</p>
<p>class GiocoDiCarte:<br />
def __init__(self):<br />
self.Mazzo = Mazzo()<br />
self.Mazzo.Mescola()</p>
<p>In questo primo caso abbiamo visto come il metodo di inizializzazione<br />
non si limiti ad assegnare dei valori agli attributi, ma esegua una<br />
elaborazione significativa.</p>
<p>Per implementare dei giochi specifici possiamo successivamente<br />
ereditare da GiocoDiCarte e aggiungere a questa classe le<br />
caratteristiche del nuovo gioco. Per fare un esempio scriveremo una<br />
simulazione di Old Maid.</p>
<p>L&#8217;obiettivo di Old Maid è quello di riuscire a sbarazzarsi di tutte le<br />
carte che si hanno in mano. Questo viene fatto eliminando coppie di<br />
carte che hanno lo stesso rango e colore: il 4 di fiori viene<br />
eliminato con il 4 di picche perché entrambi i segni sono neri; il<br />
jack di cuori con il jack di quadri perché entrambi sono rossi.</p>
<p>Per iniziare il gioco la Regina di Fiori è tolta dal mazzo per fare in<br />
modo che la Regina di Picche non possa essere eliminata durante la<br />
partita. Le 51 carte sono poi tutte distribuite una alla volta in<br />
senso orario ai giocatori e dopo la distribuzione tutti i giocatori<br />
scartano immediatamente quante più carte possibili eliminando le<br />
coppie presenti nella mano appena distribuita.</p>
<p>Quando non si possono più scartare carte il gioco ha inizio. A turno<br />
ogni giocatore pesca senza guardarla una carta dal giocatore che, in<br />
senso orario, ha ancora delle carte in mano. Se la carta scelta<br />
elimina una carta in mano la coppia viene rimossa. In caso contrario<br />
la carta scelta rimane in mano.</p>
<p>Alla fine della partita tutte le eliminazioni saranno state fatte ed<br />
il perdente è chi rimane con la Regina di Picche in mano.</p>
<p>Nella nostra simulazione del gioco il computer giocherà tutte le mani.<br />
Sfortunatamente alcune sottigliezze del gioco verranno perse: nel<br />
gioco reale chi si trova in mano la Regina di Picche farà di tutto per<br />
fare in modo che questa venga scelta da un vicino, disponendola in<br />
modo da facilitare un successo in tal senso. Il computer invece<br />
sceglierà le carte completamente a caso.</p>
<p>16.6 Classe ManoOldMaid</p>
<p>Una mano per giocare a Old Maid richiede alcune capacità che vanno<br />
oltre rispetto a quelle fornite da Mano. Sarà opportuno quindi<br />
definire una nuova classe ManoOldMaid, che erediterà i metodi da Mano<br />
e a questi metodi ne verrà aggiunto uno (RimuoveCoppie) per rimuovere<br />
le coppie di carte:</p>
<p>class ManoOldMaid(Mano):<br />
def RimuoveCoppie(self):<br />
Conteggio = 0<br />
CarteOriginali = self.Carte[:]<br />
for CartaOrig in CarteOriginali:<br />
CartaDaCercare = Carta(3-CartaOrig.Seme, CartaOrig.Rango)<br />
if CartaDaCercare in self.Carte:<br />
self.Carte.remove(CartaOrig)<br />
self.Carte.remove(CartaDaCercare)<br />
print &#8220;Mano di %s : %s elimina %s&#8221; %<br />
(self.Nome,CartaOrig,CartaDaCercare)<br />
Conteggio = Conteggio + 1<br />
return Conteggio</p>
<p>Iniziamo facendo una copia della lista di carte, così da poter<br />
attraversare la copia finché non rimuoviamo l&#8217;originale: dato che<br />
self.Carte viene modificata durante l&#8217;attraversamento, non possiamo di<br />
certo usarla per controllare tutti i suoi elementi. Python potrebbe<br />
essere confuso dal fatto di veder cambiare la lista che sta<br />
attraversando!</p>
<p>Per ogni carta della mano andiamo a controllare se quella che la<br />
elimina è presente nella stessa mano. La carta &#8220;eliminante&#8221; ha lo<br />
stesso rango e l&#8217;altro seme dello stesso colore di quella<br />
&#8220;eliminabile&#8221;: l&#8217;espressione 3-Carta.Seme serve proprio a trasformare<br />
una carta di Fiori (seme 0) in Picche (seme 3) e viceversa; una carta<br />
di Quadri (seme 1) in Cuori (seme 2) e viceversa.</p>
<p>Se entrambe le carte sono presenti sono rimosse con RimuoveCoppie:</p>
<p>&gt;&gt;&gt; Partita = GiocoDiCarte()<br />
&gt;&gt;&gt; Mano1 = ManoOldMaid(&#8220;Franco&#8221;)<br />
&gt;&gt;&gt; Partita.Mazzo.Mescola([Mano1], 13)<br />
&gt;&gt;&gt; print Mano1<br />
La mano di Franco contiene queste carte:<br />
Asso di Picche<br />
2 di Quadri<br />
7 di Picche<br />
8 di Fiori<br />
6 di Cuori<br />
8 di Picche<br />
7 di Fiori<br />
Regina di Fiori<br />
7 di Quadri<br />
5 di Fiori<br />
Jack di Quadri<br />
10 di Quadri<br />
10 di Cuori<br />
&gt;&gt;&gt; Mano1.RimuoveCoppie()<br />
Mano di Franco: 7 di Picche elimina 7 di Fiori<br />
Mano di Franco: 8 di Picche elimina 8 di Fiori<br />
Mano di Franco: 10 di Quadri elimina 10 di Cuori<br />
&gt;&gt;&gt; print Mano1<br />
La mano di Franco contiene queste carte:<br />
Asso di Picche<br />
2 di Quadri<br />
6 di Cuori<br />
Regina di Fiori<br />
7 di Quadri<br />
5 di Fiori<br />
Jack di Quadri</p>
<p>Nota che non c&#8217;è un metodo di inizializzazione __init__ per la classe<br />
ManoOldMaid dato che l&#8217;abbiamo ereditato da Mano.</p>
<p>16.7 Classe GiocoOldMaid</p>
<p>Ora possiamo dedicarci al gioco vero e proprio: GiocoOldMaid è una<br />
sottoclasse di GiocoDiCarte con un metodo Giocatori che prende una<br />
lista di giocatori come parametro.</p>
<p>Dato che __init__ è ereditato da GiocoDiCarte un nuovo oggetto<br />
GiocoOldMaid contiene un mazzo già mescolato:</p>
<p>class GiocoOldMaid(GiocoDiCarte):<br />
def Partita(self, Nomi):<br />
# rimozione della regina di fiori<br />
self.Mazzo.RimuoviCarta(Carta(0,12))<br />
# creazione di una mano per ogni giocatore<br />
self.Mani = []<br />
for Nome in Nomi:<br />
self.Mani.append(ManoOldMaid(Nome))<br />
# distribuzione delle carte<br />
self.Mazzo.Distribuisci(self.Mani)<br />
print &#8220;&#8212;&#8212;&#8212;- Le carte sono state distribuite&#8221;<br />
self.StampaMani()<br />
# toglie le coppie iniziali<br />
NumCoppie = self.RimuoveTutteLeCoppie()<br />
print &#8220;&#8212;&#8212;&#8212;- Coppie scartate, inizia la partita&#8221;<br />
self.StampaMani()<br />
# gioca finche&#8217; non sono state fatte 25 coppie<br />
Turno = 0<br />
NumMani = len(self.Mani)<br />
while NumCoppie &lt; 25:<br />
NumCoppie = NumCoppie + self.GiocaUnTurno(Turno)<br />
Turno = (Turno + 1) % NumMani<br />
print &#8220;&#8212;&#8212;&#8212;- La partita e&#8217; finita&#8221;<br />
self.StampaMani()</p>
<p>Alcuni dei passi della partita sono stati separati in metodi singoli<br />
per ragioni di chiarezza anche se dal punto di vista del programma<br />
questo non era strettamente necessario.</p>
<p>RimuoveTutteLeCoppie attraversa la lista di mani e invoca<br />
RimuoveCoppie su ognuna:</p>
<p>class GiocoOldMaid(GiocoDiCarte):<br />
&#8230;<br />
def RimuoveTutteLeCoppie(self):<br />
Conteggio = 0<br />
for Mano in self.Mani:<br />
Conteggio = Conteggio + Mano.RimuoveCoppie()<br />
return Conteggio</p>
<p>Esercizio: scrivi StampaMani che attraversa self.Mani e stampa<br />
ciascuna mano.</p>
<p>Conteggio è un accumulatore che tiene traccia del numero di coppie<br />
rimosse dall&#8217;inizio della partita: quando il numero totale di coppie<br />
raggiunge 25 sono state rimosse dalle mani esattamente 50 carte, e ciò<br />
significa che è rimasta solo una carta (la Regina di Picche) ed il<br />
gioco è finito.</p>
<p>La variabile Turno tiene traccia di quale giocatore debba giocare.<br />
Parte da 0 e viene incrementata di 1 ad ogni mano. Quando arriva a<br />
NumMani l&#8217;operatore modulo % la riporta a 0.</p>
<p>Il metodo GiocaUnTurno prende un parametro dal giocatore che sta<br />
giocando. Il valore ritornato è il numero di coppie rimosse durante il<br />
turno:</p>
<p>class GiocoOldMaid(GiocoDiCarte):<br />
&#8230;<br />
def GiocaUnTurno(self, Giocatore):<br />
if self.Mani[Giocatore].EVuoto():<br />
return 0<br />
Vicino = self.TrovaVicino(Giocatore)<br />
CartaScelta = self.Mani[Vicino].PrimaCarta()<br />
self.Mani[Giocatore].AggiungeCarta(CartaScelta)<br />
print &#8220;Mano di&#8221;, self.Mani[Giocatore].Nome, \<br />
&#8220;: scelta&#8221;, CartaScelta<br />
Conteggio = self.Mani[Giocatore].RimuoveCoppie()<br />
self.Mani[Giocatore].Mescola()<br />
return Conteggio</p>
<p>Se la mano di un giocatore è vuota quel giocatore è fuori dal gioco e<br />
non fa nulla. Il valore di ritorno in questo caso è 0.</p>
<p>In caso contrario un turno consiste nel trovare il primo giocatore in<br />
senso orario che abbia delle carte in mano, prendergli una carta e<br />
cercare coppie da rimuovere dopo avere aggiunto la carta scelta alla<br />
mano. Prima di tornare le carte in mano devono essere mescolate così<br />
che la scelta del prossimo giocatore sia ancora una volta casuale.</p>
<p>Il metodo TrovaVicino inizia con il giocatore all&#8217;immediata sinistra e<br />
continua in senso orario finché non trova qualcuno che ha ancora carte<br />
in mano:</p>
<p>class GiocoOldMaid(GiocoDiCarte):<br />
&#8230;<br />
def TrovaVicino(self, Giocatore):<br />
NumMani = len(self.Mani)<br />
for Prossimo in range(1,NumMani):<br />
Vicino = (Giocatore + Prossimo) % NumMani<br />
if not self.Mani[Vicino].EVuoto():<br />
return Vicino</p>
<p>Se TrovaVicino dovesse effettuare un giro completo dei giocatori senza<br />
trovare qualcuno con delle carte in mano tornerebbe None e causerebbe<br />
un errore da qualche parte del programma. Fortunatamente possiamo<br />
provare che questo non succederà mai, sempre che la condizione di fine<br />
partita sia riconosciuta correttamente.</p>
<p>Abbiamo omesso il metodo StampaMani dato che puoi scriverlo tu senza<br />
problemi.</p>
<p>La stampa che mostriamo in seguito mostra una partita effettuata<br />
usando le sole quindici carte di valore più elevato (i 10, i jack, le<br />
regine ed i re), ed è stata ridotta per questioni di spazio. La<br />
partita ha visto come protagonisti tre giocatori: Allen, Jeff e Chris.<br />
Con un mazzo così piccolo il gioco si ferma dopo aver rimosso 7 coppie<br />
invece delle consuete 25.</p>
<p>&gt;&gt;&gt; import Carte<br />
&gt;&gt;&gt; Gioco = Carte.GiocoOldMaid()<br />
&gt;&gt;&gt; Gioco.Partita(["Allen","Jeff","Chris"])<br />
&#8212;&#8212;&#8212;- Le carte sono state distribuite<br />
La mano di Allen contiene queste carte:<br />
Re di Cuori<br />
Jack di Fiori<br />
Regina di Picche<br />
Re di Picche<br />
10 di Quadri<br />
La mano di Jeff contiene queste carte:<br />
Regina di Cuori<br />
Jack di Picche<br />
Jack di Cuori<br />
Re di Quadri<br />
Regina di Quadri<br />
La mano di Chris contiene queste carte:<br />
Jack di Quadri<br />
Re di Fiori<br />
10 di Picche<br />
10 di Cuori<br />
10 di Fiori<br />
Mano di Jeff: Regina di Cuori elimina Regina di Quadri<br />
Mano di Chris: 10 di Picche elimina 10 di Fiori<br />
&#8212;&#8212;&#8212;- Coppie scartate, inizia la partita<br />
La mano di Allen contiene queste carte:<br />
Re di Cuori<br />
Jack di Fiori<br />
Regina di Picche<br />
Re di Picche<br />
10 di Quadri<br />
La mano di Jeff contiene queste carte:<br />
Jack di Picche<br />
Jack di Cuori<br />
Re di Quadri<br />
La mano di Chris contiene queste carte:<br />
Jack di Quadri<br />
Re di Fiori<br />
10 di Cuori<br />
Mano di Allen: scelta Re di Quadri<br />
Mano di Allen: Re di Cuori elimina Re di Quadri<br />
Mano di Jeff: scelta 10 di Cuori<br />
Mano di Chris: scelta Jack di Fiori<br />
Mano di Allen: scelta Jack di Cuori<br />
Mano di Jeff: scelta Jack di Quadri<br />
Mano di Chris: scelta Regina di Picche<br />
Mano di Allen: scelta Jack di Quadri<br />
Mano di Allen: Jack di Cuori elimina Jack di Quadri<br />
Mano di Jeff: scelta Re di Fiori<br />
Mano di Chris: scelta Re di Picche<br />
Mano di Allen: scelta 10 di Cuori<br />
Mano di Allen: 10 di Quadri elimina 10 di Cuori<br />
Mano di Jeff: scelta Regina di Picche<br />
Mano di Chris: scelta Jack di Picche<br />
Mano di Chris: Jack di Fiori elimina Jack di Picche<br />
Mano di Jeff: scelta Re di Picche<br />
Mano di Jeff: Re di Fiori elimina Re di Picche<br />
&#8212;&#8212;&#8212;- La partita e&#8217; finita<br />
La mano di Allen e&#8217; vuota<br />
La mano di Jack contiene queste carte:<br />
Regina di Picche<br />
La mano di Chris e&#8217; vuota</p>
<p>Così Jeff ha perso.</p>
<p>16.8 Glossario</p>
<p>Ereditarietà<br />
capacità di definire una nuova classe come versione modificata<br />
di una classe precedentemente definita.</p>
<p>Classe genitore<br />
classe da cui si deriva un&#8217;altra classe.</p>
<p>Classe figlia<br />
nuova classe creata derivandola da una classe già esistente; è<br />
anche chiamata &#8220;sottoclasse&#8221;.</p>
<p>Capitolo 17</p>
<p>Liste linkate</p>
<p>17.1 Riferimenti interni</p>
<p>Abbiamo visto esempi di attributi che si riferiscono ad altri oggetti<br />
(riferimenti interni, vedi sezione 12.8). Una struttura di dati<br />
piuttosto comune, la lista linkata, fa uso di questa caratteristica.</p>
<p>Le liste linkate sono costituite da nodi ed ognuno di questi nodi<br />
contiene il riferimento al successivo nodo della lista ed un&#8217;unità di<br />
dati utili chiamata contenuto.</p>
<p>Una lista linkata è considerata una struttura di dati ricorsiva perché<br />
la sua definizione è di per sé ricorsiva:</p>
<p>Una lista linkata è:<br />
* una lista vuota, rappresentata da None, oppure<br />
* un nodo che contiene un oggetto &#8220;contenuto&#8221; ed un riferimento ad<br />
una lista linkata.</p>
<p>Le strutture di dati di tipo ricorsivo sono gestite da metodi<br />
ricorsivi.</p>
<p>17.2 La classe Nodo</p>
<p>Come abbiamo già visto in occasione della scrittura di nuove classi,<br />
cominciamo a scrivere la classe Nodo dalla sua inizializzazione e dal<br />
metodo __str__ così da poter testare immediatamente il meccanismo di<br />
creazione e visualizzazione del nuovo tipo:</p>
<p>class Nodo:<br />
def __init__(self, Contenuto=None, ProssimoNodo=None):<br />
self.Contenuto = Contenuto<br />
self.ProssimoNodo  = ProssimoNodo<br />
def __str__(self):<br />
return str(self.Contenuto)</p>
<p>Abbiamo definito come opzionali i parametri per il metodo di<br />
inizializzazione: di default sia Contenuto che il link ProssimoNodo<br />
hanno valore None.</p>
<p>La rappresentazione a stringa del nodo è solo la stampa del suo<br />
contenuto: dato che alla funzione str può essere passato qualsiasi<br />
tipo di valore possiamo memorizzare nella lista ogni tipo di dato.</p>
<p>Per testare l&#8217;implementazione possiamo creare un Nodo e stamparne il<br />
valore:</p>
<p>&gt;&gt;&gt; Nodo1 = Nodo(&#8220;test&#8221;)<br />
&gt;&gt;&gt; print Nodo1<br />
test</p>
<p>Per rendere il tutto più interessante abbiamo bisogno di una lista che<br />
contiene più di un nodo:</p>
<p>&gt;&gt;&gt; Nodo1 = Nodo(1)<br />
&gt;&gt;&gt; Nodo2 = Nodo(2)<br />
&gt;&gt;&gt; Nodo3 = Nodo(3)</p>
<p>Questo codice crea tre nodi ma non siamo in presenza di una lista dato<br />
che questi nodi non sono linkati (collegati uno all&#8217;altro). Il<br />
diagramma di stato in questo caso è:</p>
<p>[i_link1.png]</p>
<p>Per linkare i nodi dobbiamo fare in modo che il primo si riferisca al<br />
secondo, ed il secondo al terzo:</p>
<p>&gt;&gt;&gt; Nodo1.ProssimoNodo = Nodo2<br />
&gt;&gt;&gt; Nodo2.ProssimoNodo = Nodo3</p>
<p>Il riferimento del terzo nodo è None e questo indica che ci troviamo<br />
alla fine della lista. Ecco il nuovo diagramma di stato:</p>
<p>[i_link2.png]</p>
<p>Ora sai come creare nodi e come linkarli in liste. Ciò che<br />
probabilmente è meno chiaro è il motivo per cui questo possa rivelarsi<br />
utile.</p>
<p>17.3 Liste come collezioni</p>
<p>Le liste sono utili perché forniscono un modo per assemblare più<br />
oggetti in una entità singola talvolta chiamata collezione.<br />
Nell&#8217;esempio che abbiamo visto il primo nodo serve come riferimento<br />
all&#8217;intera lista dato che ne rappresenta il punto di partenza.</p>
<p>Per passare una lista di questo tipo come parametro ad una funzione<br />
dobbiamo passare quindi soltanto il riferimento al suo primo nodo. Per<br />
fare un esempio, la funzione StampaLista prende un singolo nodo come<br />
argomento, considerandolo l&#8217;inizio della lista e stampa il contenuto<br />
di ogni nodo finché non viene raggiunta la fine della lista:</p>
<p>def StampaLista(Nodo):<br />
while Nodo:<br />
print Nodo,<br />
Nodo = Nodo.ProssimoNodo<br />
print</p>
<p>Per invocare questo metodo passiamo un riferimento al primo nodo:</p>
<p>&gt;&gt;&gt; StampaLista(Nodo1)<br />
1 2 3</p>
<p>All&#8217;interno di StampaLista abbiamo un riferimento al primo nodo della<br />
lista ma non c&#8217;è alcuna variabile che si riferisce agli altri nodi:<br />
per passare da un nodo al successivo usiamo il valore<br />
Nodo.ProssimoNodo, usando la variabile Nodo per riferirsi ad ognuno<br />
dei nodi in successione.</p>
<p>Questo diagramma mostra il valore di Lista ed il valore assunto da<br />
Nodo:</p>
<p>[i_link3.png]</p>
<p>Esercizio: per convenzione le liste sono stampate tra parentesi<br />
quadrate con virgole che ne separano gli elementi, come in [1, 2,<br />
3]. Modifica StampaLista così da generare una stampa in questo<br />
formato.</p>
<p>17.4 Liste e ricorsione</p>
<p>Data la sua natura ricorsiva è intuitivo esprimere molte operazioni<br />
sulle liste con metodi ricorsivi. Questo è un algoritmo per stampare<br />
una lista a partire dall&#8217;ultimo elemento:<br />
1. Separa la lista in due parti: il primo nodo (chiamato testa) ed il<br />
resto (la coda).<br />
2. Stampa la coda in ordine inverso.<br />
3. Stampa la testa.</p>
<p>Logicamente il passo 2, la chiamata ricorsiva, parte dal presupposto<br />
che ci sia un metodo per stampare la lista al contrario. Se partiamo<br />
dal presupposto che la chiamata ricorsiva funziona correttamente<br />
questo algoritmo lavora in modo corretto.</p>
<p>Tutto ciò di cui abbiamo bisogno è un caso base ed un modo per<br />
verificare che per ogni tipo di lista riusciremo ad arrivare al caso<br />
base per interrompere la serie di chiamate ricorsive. Data la<br />
definizione ricorsiva della lista un caso base intuitivo è la lista<br />
vuota, rappresentata da None:</p>
<p>def StampaInversa(Lista):<br />
if Lista == None: return<br />
Testa = Lista<br />
Coda = Lista.ProssimoNodo<br />
StampaInversa(Coda)<br />
print Testa,</p>
<p>La prima riga gestisce il caso base senza fare niente. Le due righe<br />
successive dividono la lista in due parti (Testa e Coda). Le ultime<br />
due righe stampano la lista. Ricorda che la virgola alla fine del<br />
print evita la stampa del ritorno a capo tra un nodo e l&#8217;altro.</p>
<p>Invochiamo questo metodo come abbiamo fatto con StampaLista:</p>
<p>&gt;&gt;&gt; StampaInversa(Nodo1)<br />
3 2 1</p>
<p>Potresti chiederti perché StampaLista e StampaInversa sono funzioni e<br />
non metodi nella classe Nodo. La ragione è che vogliamo usare il<br />
valore None per rappresentare la lista vuota e non è lecito invocare<br />
un metodo su None. Questa limitazione in effetti rende poco pulito il<br />
codice, costringendo alla sua implementazione senza poter fare uso di<br />
uno stile orientato agli oggetti.</p>
<p>17.5 Liste infinite</p>
<p>Possiamo provare che StampaInversa giungerà sempre alla fine,<br />
raggiungendo il caso base? La risposta è no e infatti la sua chiamata<br />
causerà un errore in esecuzione nel caso in cui la lista passata come<br />
parametro sia di tipo particolare.</p>
<p>Non c&#8217;è nulla che vieti ad un nodo di fare riferimento ad un nodo<br />
precedente della lista o addirittura a se stesso. Questa figura mostra<br />
una lista di due nodi ognuno dei quali si riferisce a se stesso:</p>
<p>[i_link4.png]</p>
<p>Se invocassimo StampaLista o StampaInversa su questa lista si<br />
creerebbe una ricorsione infinita: questo tipo di comportamento rende<br />
particolarmente difficile lavorare con le liste&#8230;</p>
<p>Ciononostante le liste infinite possono rivelarsi molto utili in<br />
(poche) occasioni particolari, come quando vogliamo rappresentare un<br />
numero come lista di cifre usando una lista infinita per la<br />
descrizione della parte decimale periodica.</p>
<p>Ci rimane comunque il problema che non possiamo dimostrare che<br />
StampaLista e StampaInversa raggiungono sempre il caso base. Il meglio<br />
che possiamo fare è stabilire una precondizione, assumendo che &#8220;se non<br />
sono presenti anelli all&#8217;interno della lista questi metodi<br />
termineranno&#8221;. La precondizione impone una limitazione ai parametri e<br />
descrive il comportamento di un metodo nel caso essa venga<br />
soddisfatta. Vediamo subito qualche esempio.</p>
<p>17.6 Il teorema dell&#8217;ambiguità fondamentale</p>
<p>Una parte di StampaInversa aveva qualcosa di sospetto:</p>
<p>Testa = Lista<br />
Coda = Lista.ProssimoNodo</p>
<p>Dopo la prima assegnazione Testa e Lista hanno lo stesso tipo e lo<br />
stesso valore. Perché dunque abbiamo creato una nuova variabile?</p>
<p>La ragione è che le due variabili giocano ruoli differenti. Pensiamo a<br />
Testa come riferimento ad un singolo nodo e a Lista come riferimento<br />
al primo nodo della lista. Questi &#8220;ruoli&#8221; non sono espressamente<br />
necessari al programma, ma sono molto utili per chiarire il concetto<br />
al programmatore.</p>
<p>In generale non possiamo dire quale ruolo giochi una variabile<br />
semplicemente guardando un programma. Spesso si usano nomi come Nodo e<br />
Lista per documentare l&#8217;uso della variabile e si introducono variabili<br />
addizionali solo per rendere meno ambiguo il codice al momento della<br />
lettura.</p>
<p>Avremmo anche potuto scrivere StampaInversa senza Testa e Coda. Il<br />
risultato sarebbe stato più conciso, ma decisamente meno chiaro:</p>
<p>def StampaInversa(Lista) :<br />
if Lista == None : return<br />
StampaInversa(Lista.ProssimoNodo)<br />
print Lista,</p>
<p>Con un&#8217;attenzione alle due chiamate di funzione è necessario<br />
ricordarci che StampaInversa tratta il suo argomento Lista come una<br />
collezione e print il proprio come un oggetto singolo.</p>
<p>Il teorema dell&#8217;ambiguità fondamentale descrive l&#8217;ambiguità inerente<br />
al riferimento ad un nodo:</p>
<p>Una variabile che si riferisce ad un nodo può trattare il nodo come<br />
oggetto singolo o come primo elemento di una lista di nodi linkati.</p>
<p>17.7 Modifica delle liste</p>
<p>Ci sono due modi per modificare una lista linkata: possiamo cambiare<br />
il contenuto di uno dei nodi o aggiungere, rimuovere o riordinare i<br />
nodi.</p>
<p>Come esempio scriviamo un metodo per rimuovere il secondo nodo di una<br />
lista, ritornando un riferimento al nodo rimosso:</p>
<p>def RimuoviSecondo(Lista):<br />
if Lista == None: return<br />
Primo = Lista<br />
Secondo = Lista.ProssimoNodo<br />
# il primo nodo deve riferirsi al terzo<br />
Primo.ProssimoNodo = Secondo.ProssimoNodo<br />
# separa il secondo nodo dal resto della lista<br />
Secondo.ProssimoNodo = None<br />
return Secondo</p>
<p>Ancora una volta abbiamo usato delle variabili temporanee per rendere<br />
il codice più leggibile. Ecco come usare questo metodo:</p>
<p>&gt;&gt;&gt; StampaLista(Nodo1)<br />
1 2 3<br />
&gt;&gt;&gt; Rimosso = RimuoviSecondo(Nodo1)<br />
&gt;&gt;&gt; StampaLista(Rimosso)<br />
2<br />
&gt;&gt;&gt; StampaLista(Nodo1)<br />
1 3</p>
<p>Questo diagramma di stato mostra l&#8217;effetto dell&#8217;operazione:</p>
<p>[i_link5.png]</p>
<p>Cosa succede se invochi questo metodo e passi una lista composta da un<br />
solo elemento (elemento singolo)? Cosa succede se passi come argomento<br />
una lista vuota? C&#8217;è una precondizione per questo metodo? Se esiste<br />
riscrivi il metodo per gestire gli eventuali problemi.</p>
<p>17.8 Metodi contenitore e aiutante</p>
<p>Spesso è utile dividere un&#8217;operazione su una lista in due metodi. Per<br />
esempio per stampare una lista al contrario secondo il formato<br />
convenzionale [3, 2, 1] possiamo usare il metodo StampaInversa per<br />
stampare 3, 2, ma abbiamo bisogno di un metodo diverso per stampare le<br />
parentesi ed il primo nodo. Chiamiamo questo metodo<br />
StampaInversaFormato:</p>
<p>def StampaInversaFormato(Lista) :<br />
print &#8220;[",<br />
if Lista != None :<br />
Testa = Lista<br />
Coda = Lista.ProssimoNodo<br />
StampaInversa(Coda)<br />
print Testa,<br />
print "]&#8220;,</p>
<p>Ancora una volta è una buona idea testare questo metodo per vedere se<br />
funziona correttamente anche in casi particolari, quando cioè una<br />
lista è vuota o composta da un solo elemento.</p>
<p>Quando usiamo questo metodo da qualche parte nel programma invochiamo<br />
direttamente StampaInversaFormato e questa invoca a sua volta<br />
StampaInversa. In questo senso StampaInversaFormato agisce come un<br />
contenitore che usa StampaInversa come aiutante.</p>
<p>17.9 La classe ListaLinkata</p>
<p>Dal modo in cui abbiamo implementato le liste sorgono dei problemi<br />
concettuali piuttosto sottili. Procedendo in modo diverso dal consueto<br />
proporremo un&#8217;implementazione alternativa spiegando solo in seguito<br />
quali problemi vengono risolti da questa nuova versione.</p>
<p>Creiamo innanzitutto una nuova classe chiamata ListaLinkata. I suoi<br />
attributi sono un intero che contiene la lunghezza della lista e il<br />
riferimento al primo nodo. Gli oggetti ListaLinkata ci serviranno per<br />
gestire liste di oggetti Nodo:</p>
<p>class ListaLinkata:<br />
def __init__(self) :<br />
self.Lunghezza = 0<br />
self.Testa = None</p>
<p>Una cosa positiva per quanto concerne la classe ListaLinkata è che<br />
fornisce un posto naturale dove inserire funzioni contenitore quale<br />
StampaInversaFormato e che possiamo far diventare metodi della classe:</p>
<p>class ListaLinkata:<br />
&#8230;<br />
def StampaInversa(self):<br />
print &#8220;[",<br />
if self.Testa != None:<br />
self.Testa.StampaInversa()<br />
print "]&#8220;,<br />
class Nodo:<br />
&#8230;<br />
def StampaInversa(self):<br />
if self.ProssimoNodo != None:<br />
Coda = self.ProssimoNodo<br />
Coda.StampaInversa()<br />
print self.Contenuto,</p>
<p>Per rendere le cose più interessanti, rinominiamo<br />
StampaInversaFormato. Ora abbiamo due metodi chiamati StampaInversa:<br />
quello nella classe Nodo che è l&#8217;aiutante e quello nella classe<br />
ListaLinkata che è il contenitore. Quando il metodo contenitore invoca<br />
self.Testa.StampaInversa sta in effetti invocando l&#8217;aiutante dato che<br />
self.Testa è un oggetto di tipo Nodo.</p>
<p>Un altro beneficio della classe ListaLinkata è che rende semplice<br />
aggiungere o rimuovere il primo elemento di una lista. AggiuntaPrimo è<br />
il metodo di ListaLinkata per aggiungere un contenuto all&#8217;inizio di<br />
una lista:</p>
<p>class ListaLinkata:<br />
&#8230;<br />
def AggiuntaPrimo(self, Contenuto):<br />
NodoAggiunto = Nodo(Contenuto)<br />
NodoAggiunto.ProssimoNodo = self.Testa<br />
self.Testa = NodoAggiunto<br />
self.Lunghezza = self.Lunghezza + 1</p>
<p>Come sempre occorre verificare che questo codice funzioni<br />
correttamente anche nel caso di liste speciali: cosa succede se la<br />
lista è inizialmente vuota?</p>
<p>17.10 Invarianti</p>
<p>Alcune liste sono &#8220;ben formate&#8221; mentre altre non lo sono. Se una lista<br />
contiene un anello questo può creare problemi a un certo numero dei<br />
nostri metodi, tanto che potremmo richiedere solo liste che non<br />
contengono anelli al loro interno. Un altro prerequisito è che il<br />
valore di Lunghezza nell&#8217;oggetto ListaLinkata corrisponda sempre al<br />
numero di nodi della lista.</p>
<p>Prerequisiti come questi sono chiamati invarianti perché dovrebbero<br />
essere sempre verificati in ogni momento per ogni oggetto della<br />
classe. Specificare gli invarianti degli oggetti è una pratica di<br />
programmazione molto indicata in quanto consente di rendere molto più<br />
facile la verifica del codice, il controllo dell&#8217;integrità delle<br />
strutture e il riconoscimento degli errori.</p>
<p>Una cosa che può rendere confusi per quanto riguarda gli invarianti è<br />
che a volte i prerequisiti che essi rappresentano possono essere<br />
violati, anche se solo temporaneamente: nel metodo AggiungiPrimo, dopo<br />
aver aggiunto il nodo ma prima di avere aggiornato Lunghezza, il<br />
prerequisito invariante non è soddisfatto. Questo tipo di violazione è<br />
accettabile, dato che spesso è impossibile modificare un oggetto senza<br />
violare un invariante almeno per un breve istante. Normalmente<br />
richiediamo che qualsiasi metodo che si trovi a violare un invariante<br />
lo ripristini non appena possibile.</p>
<p>Se l&#8217;invariante è violato in una parte significativa del codice è<br />
molto importante commentare questo comportamento anomalo per evitare<br />
che, anche a distanza di tempo, possano essere richieste delle<br />
operazioni che dipendono dall&#8217;integrità dei dati proprio dove questi<br />
dati non sono corretti.</p>
<p>17.11 Glossario</p>
<p>Riferimento interno<br />
riferimento depositato in un attributo di un oggetto.</p>
<p>Lista linkata<br />
struttura di dati che implementa una collezione usando una<br />
sequenza di nodi linkati.</p>
<p>Nodo<br />
elemento di una lista solitamente implementato come un oggetto<br />
che contiene un riferimento ad un altro oggetto dello stesso<br />
tipo.</p>
<p>Contenuto<br />
insieme dei dati utili contenuti in un nodo.</p>
<p>Link<br />
riferimento interno ad un oggetto usato per legarlo ad un altro<br />
oggetto.</p>
<p>Precondizione<br />
condizione che deve essere vera per permettere ad un metodo di<br />
funzionare in modo corretto.</p>
<p>Teorema dell&#8217;ambiguità fondamentale<br />
il riferimento ad un nodo di una lista può essere considerato<br />
sia un singolo oggetto che il primo di una lista di nodi.</p>
<p>Elemento singolo<br />
lista linkata composta da un singolo nodo.</p>
<p>Contenitore<br />
metodo che agisce da interfaccia tra una funzione chiamante e<br />
un metodo aiutante, spesso semplificando l&#8217;uso del metodo<br />
aiutante o rendendo l&#8217;invocazione più immune da errori.</p>
<p>Aiutante<br />
metodo che non è invocato direttamente da una funzione chiamate<br />
ma che è usato da un altro metodo per portare a termine una<br />
parte di un&#8217;operazione.</p>
<p>Invariante<br />
condizione che deve essere vera per un oggetto in ogni momento,<br />
con l&#8217;unica eccezione degli istanti in cui l&#8217;oggetto è in fase<br />
di modifica.</p>
<p>Capitolo 18</p>
<p>Pile</p>
<p>18.1 Tipi di dati astratti</p>
<p>Tutti i tipi di dati che hai visto finora sono concreti, nel senso che<br />
abbiamo completamente specificato quale sia la loro implementazione.<br />
La classe Carta rappresenta una carta da gioco usando due numeri<br />
interi: come abbiamo detto durante lo sviluppo della classe questa non<br />
è l&#8217;unica implementazione possibile ma ne esistono infinite altre.</p>
<p>Un tipo di dato astratto (TDA) specifica un insieme di operazioni (o<br />
metodi) e la loro semantica (cosa fa ciascuna operazione) ma senza<br />
specificare la loro implementazione: questa caratteristica è ciò che<br />
lo rende astratto.</p>
<p>Per che cosa è utile questa &#8220;astrazione&#8221;?<br />
* Semplifica il compito di specificare un algoritmo, dato che puoi<br />
decidere cosa dovranno fare le operazioni senza dover pensare allo<br />
stesso tempo a come implementarle.<br />
* Ci sono molti modi per implementare un TDA e può essere utile<br />
scrivere un solo algoritmo in grado di funzionare per ciascuna<br />
delle possibili implementazioni.<br />
* TDA molto ben conosciuti, tipo la Pila (o Stack) che vedremo in<br />
questo capitolo, sono spesso implementati nelle librerie standard<br />
dei vari linguaggi di programmazione così da poter essere usati da<br />
molti programmatori senza dover essere reinventati ogni volta.<br />
* Le operazioni sui TDA forniscono un linguaggio di alto livello che<br />
consente di specificare e descrivere gli algoritmi.</p>
<p>Quando parliamo di TDA spesso distinguiamo il codice che usa il TDA<br />
(cliente) dal codice che lo implementa (fornitore).</p>
<p>18.2 Il TDA Pila</p>
<p>In questo capitolo esamineremo la pila, un tipo di dato astratto molto<br />
comune. Una pila è una collezione e cioè una struttura di dati che<br />
contiene elementi multipli. Altre collezioni che abbiamo già visto<br />
sono i dizionari e le liste.</p>
<p>Un TDA è definito dalle operazioni che possono essere effettuate su di<br />
esso e che sono chiamate interfaccia. L&#8217;interfaccia per una pila<br />
consiste di queste operazioni:</p>
<p>__init__<br />
Inizializza un pila vuota.</p>
<p>Push<br />
Aggiunge un elemento alla pila.</p>
<p>Pop<br />
Rimuove e ritorna un elemento dalla pila. L&#8217;elemento tornato è<br />
sempre l&#8217;ultimo inserito.</p>
<p>EVuota<br />
Controlla se la pila è vuota.</p>
<p>Una pila è spesso chiamata struttura di dati LIFO (&#8220;last in/first<br />
out&#8221;, ultimo inserito, primo fuori) perché l&#8217;ultimo elemento inserito<br />
in ordine di tempo è il primo ad essere rimosso: un esempio è una<br />
serie di piatti da cucina sovrapposti, ai quali aggiungiamo ogni<br />
ulteriore piatto appoggiandolo sopra agli altri, ed è proprio<br />
dall&#8217;alto che ne preleviamo uno quando ci serve.</p>
<p>18.3 Implementazione delle pile con le liste di Python</p>
<p>Le operazioni che Python fornisce per le liste sono simili a quelle<br />
definite per la nostra pila. L&#8217;interfaccia non è proprio quella che ci<br />
si aspetta ma scriveremo del codice per tradurla nel formato utile al<br />
nostro TDA Pila.</p>
<p>Questo codice è chiamato implementazione del TDA Pila. Più in generale<br />
un&#8217;implementazione è un insieme di metodi che soddisfano la sintassi e<br />
la semantica dell&#8217;interfaccia richiesta.</p>
<p>Ecco un&#8217;implementazione della Pila con le liste predefinite in Python:</p>
<p>class Pila:<br />
def __init__(self):<br />
self.Elementi = []<br />
def Push(self, Elemento) :<br />
self.Elementi.append(Elemento)<br />
def Pop(self):<br />
return self.Elementi.pop()<br />
def EVuota(self):<br />
return (self.Elementi == [])</p>
<p>L&#8217;oggetto Pila contiene un attributo chiamato Elementi che è la lista<br />
di oggetti contenuta nella pila. Il metodo __init__ inizializza<br />
Elementi come lista vuota.</p>
<p>Push inserisce un nuovo elemento nella pila aggiungendolo a Elementi.<br />
Pop esegue l&#8217;operazione inversa, rimuovendo e ritornando l&#8217;ultimo<br />
elemento inserito nella pila.</p>
<p>Per controllare se la pila è vuota EVuota confronta Elementi con una<br />
lista vuota e ritorna vero/falso.</p>
<p>Un&#8217;implementazione di questo tipo in cui i metodi sono solo una<br />
semplice invocazione di metodi già esistenti viene detta maschera.<br />
Nella vita reale la maschera (o impiallacciatura) è tra le altre cose<br />
quello strato di legno di buona qualità che copre un legno di bassa<br />
qualità sottostante. In informatica è un pezzo di codice che nasconde<br />
i dettagli di un&#8217;implementazione per fornire un&#8217;interfaccia più<br />
semplice e standard.</p>
<p>18.4 Push e Pop</p>
<p>Una pila è una struttura di dati generica dato che possiamo aggiungere<br />
qualsiasi tipo di dato al suo interno. Gli esempi seguenti aggiungono<br />
due interi ed una stringa alla pila:</p>
<p>&gt;&gt;&gt; P = Pila()<br />
&gt;&gt;&gt; P.Push(54)<br />
&gt;&gt;&gt; P.Push(45)<br />
&gt;&gt;&gt; P.Push(&#8220;+&#8221;)</p>
<p>Possiamo usare EVuota e Pop per rimuovere e stampare tutti gli<br />
elementi della pila:</p>
<p>while not P.EVuota() :<br />
print P.Pop(),</p>
<p>Il risultato è + 45 54. In altre parole abbiamo usato la pila per<br />
stampare gli elementi in ordine inverso! Anche se questo non è il<br />
formato standard per la stampa di una lista usando una pila è stato<br />
comunque facile ottenerla.</p>
<p>Confronta questo codice con l&#8217;implementazione di StampaInversa nella<br />
sezione 17.4. Le due versioni sono molto più simili di ciò che sembra<br />
a prima vista, dato che entrambe fanno uso dello stesso meccanismo:<br />
mentre nell&#8217;implementazione della classe Pila appena scritta l&#8217;uso<br />
della pila è evidente, nella versione ricorsiva vista in precedenza il<br />
carico della gestione della pila era delegato all&#8217;interprete stesso.<br />
Ad ogni chiamata di funzione infatti viene usata una pila interna<br />
all&#8217;interprete che tiene conto della successione delle chiamate alle<br />
funzioni.</p>
<p>18.5 Uso della pila per valutare espressioni postfisse</p>
<p>Nella maggior parte dei linguaggi di programmazione le espressioni<br />
matematiche sono scritte con l&#8217;operatore tra i due operandi, come<br />
nella consueta 1+2. Questo formato è chiamato notazione infissa. Un<br />
modo alternativo che ha avuto qualche successo in passato in<br />
particolari modelli di calcolatrici tascabili ma ora è usato meno<br />
frequentemente, è chiamato notazione postfissa: nella notazione<br />
postfissa l&#8217;operatore segue gli operandi, tanto che l&#8217;espressione<br />
appena vista sarebbe scritta in questo modo: 1 2 +.</p>
<p>Il motivo per cui la notazione postfissa può rivelarsi utile è che c&#8217;è<br />
un modo del tutto naturale per valutare espressioni postfisse con<br />
l&#8217;uso della pila:<br />
* A partire dall&#8217;inizio dell&#8217;espressione ricava un termine<br />
(operatore o operando) alla volta.</p>
<p>* Se il termine è un operando aggiungilo all&#8217;inizio della pila.<br />
* Se il termine è un operatore estrai dalla pila il numero di<br />
operandi previsto per l&#8217;operatore, elabora il risultato<br />
dell&#8217;operazione su di essi e aggiungi il risultato all&#8217;inizio<br />
della pila.</p>
<p>Quando tutta l&#8217;espressione è stata elaborata nella pila ci dovrebbe<br />
essere un solo elemento che rappresenta il risultato.</p>
<p>Esercizio: applica questo algoritmo all&#8217;espressione 1 2 + 3 *.</p>
<p>Questo esempio mostra uno dei vantaggi della notazione postfissa: non<br />
sono necessarie parentesi per controllare l&#8217;ordine delle operazioni.<br />
Per ottenere lo stesso risultato con la notazione infissa avremmo<br />
dovuto scrivere (1 + 2) * 3.</p>
<p>Esercizio: scrivi l&#8217;espressione postfissa equivalente a 1+2*3.</p>
<p>18.6 Parsing</p>
<p>Per implementare l&#8217;algoritmo di valutazione dell&#8217;espressione dobbiamo<br />
essere in grado di attraversare una stringa e di dividerla in una<br />
serie di operandi e operatori. Questo processo è un esempio di parsing<br />
e il risultato è una serie di elementi chiamati token. Abbiamo già<br />
visto questi termini all&#8217;inizio del libro.</p>
<p>Python fornisce un metodo split in due moduli, sia in string (per la<br />
gestione delle stringhe) che in re (per le espressioni regolari). La<br />
funzione string.split divide una stringa scomponendola in una lista di<br />
token e usando un singolo carattere come delimitatore. Per esempio:</p>
<p>&gt;&gt;&gt; import string<br />
&gt;&gt;&gt; string.split(&#8220;Nel mezzo del cammin&#8221;,&#8221; &#8220;)<br />
['Nel', 'mezzo', 'del', 'cammin']</p>
<p>In questo caso il delimitatore è il carattere spazio così che la<br />
stringa viene spezzata ad ogni spazio.</p>
<p>La funzione re.split è molto più potente, permettendo l&#8217;uso di una<br />
espressione regolare invece di un delimitatore singolo. Un&#8217;espressione<br />
regolare è un modo per specificare un insieme di stringhe e non<br />
soltanto un&#8217;unica stringa: [A-Z] è l&#8217;insieme di tutte le lettere<br />
maiuscole dell&#8217;alfabeto, mentre [0-9] è l&#8217;insieme di tutti i numeri.<br />
L&#8217;operatore ^ effettua la negazione dell&#8217;insieme così che [^0-9]<br />
rappresenta l&#8217;insieme di tutto ciò che non è un numero. Questi sono<br />
soltanto gli esempi più semplici di ciò che possono fare le<br />
espressioni regolari e per le nostre necessità ci fermeremo qui:<br />
infatti abbiamo già ricavato l&#8217;espressione regolare che ci serve per<br />
dividere un&#8217;espressione postfissa:</p>
<p>&gt;&gt;&gt; import re<br />
&gt;&gt;&gt; re.split(&#8220;([^0-9])&#8221;, &#8220;123+456*/&#8221;)<br />
['123', '+', '456', '*', '', '/', '']</p>
<p>Nota come l&#8217;ordine degli operandi sia diverso da quello di<br />
string.split in quanto i delimitatori sono indicati prima della<br />
stringa da dividere.</p>
<p>La lista risultante include gli operandi 123 e 456, e gli operatori *<br />
e /. Include inoltre due stringhe vuote inserite dopo gli operandi.</p>
<p>18.7 Valutazione postfissa</p>
<p>Per valutare un&#8217;espressione postfissa useremo il parser e l&#8217;algoritmo<br />
che abbiamo visto nelle sezioni precedenti. Per cominciare dalle cose<br />
più semplici inizialmente implementeremo solo gli operatori + e *:</p>
<p>def ValutaPostfissa(Espressione):<br />
import re<br />
ListaToken = re.split(&#8220;([^0-9])&#8221;, Espressione)<br />
Pila = Pila()<br />
for Token in ListaToken:<br />
if  Token == &#8221; or Token == &#8216; &#8216;:<br />
continue<br />
if  Token == &#8216;+&#8217;:<br />
Somma = Pila.Pop() + Pila.Pop()<br />
Pila.Push(Somma)<br />
elif Token == &#8216;*&#8217;:<br />
Prodotto = Pila.Pop() * Pila.Pop()<br />
Pila.Push(Prodotto)<br />
else:<br />
Pila.Push(int(Token))<br />
return Pila.Pop()</p>
<p>La prima condizione tiene a bada gli spazi e le stringhe vuote. Le due<br />
condizioni successive gestiscono gli operatori, partendo dal<br />
presupposto che qualsiasi altra cosa sia un operatore valido.<br />
Logicamente dovremo controllare la validità dell&#8217;espressione da<br />
valutare ed eventualmente mostrare un messaggio di errore se ci<br />
fossero dei problemi, ma questo lo faremo più avanti.</p>
<p>Testiamola per valutare l&#8217;espressione postfissa di (56+47)*2:</p>
<p>&gt;&gt;&gt; print ValutaPostfissa(&#8220;56 47 + 2 *&#8221;)<br />
206</p>
<p>18.8 Clienti e fornitori</p>
<p>Uno degli obiettivi fondamentali di un TDA è quello di separare gli<br />
interessi del fornitore, che scrive il codice del TDA, da quelli del<br />
cliente, che usa il TDA. Il fornitore deve solo preoccuparsi di<br />
verificare che l&#8217;implementazione sia corretta, secondo le specifiche<br />
del TDA, e non ha idea di come sarà usato il suo codice.</p>
<p>D&#8217;altra parte il cliente parte dal presupposto che l&#8217;implementazione<br />
del TDA sia corretta e non si preoccupa dei dettagli già considerati<br />
dal fornitore. Quando stai usando dei tipi predefiniti in Python hai<br />
il vantaggio di dover pensare solo da cliente, senza doverti<br />
preoccupare di verificare la corretta implementazione del codice.</p>
<p>Logicamente nel momento in cui implementi un TDA (e quindi sei il<br />
fornitore) devi scrivere del codice cliente per testarlo, e questo<br />
fatto può mettere un po&#8217; in confusione dato che si devono giocare<br />
entrambi i ruoli.</p>
<p>18.9 Glossario</p>
<p>Tipo di dato astratto (TDA)<br />
tipo di dato (solitamente una collezione di oggetti) definito<br />
da una serie di operazioni e che può essere implementato in una<br />
varietà di modi diversi.</p>
<p>Interfaccia<br />
insieme di operazioni che definiscono un TDA.</p>
<p>Implementazione<br />
codice che soddisfa i prerequisiti di sintassi e semantica di<br />
un&#8217;interfaccia.</p>
<p>Cliente<br />
programma (o persona che scrive un programma) che usa un TDA.</p>
<p>Fornitore<br />
programma (o persona che scrive un programma) che implementa un<br />
TDA.</p>
<p>Maschera<br />
definizione di classe che implementa un TDA con definizioni di<br />
metodi che sono invocazioni di altri metodi, talvolta con<br />
l&#8217;apporto di semplici trasformazioni. Le maschere non fanno un<br />
lavoro significativo, ma migliorano o standardizzano<br />
l&#8217;interfaccia usata dal cliente.</p>
<p>Struttura di dati generica<br />
struttura di dati che può contenere dati di ogni tipo.</p>
<p>Notazione infissa<br />
modo di scrivere espressioni matematiche con gli operatori tra<br />
gli operandi, eventualmente con l&#8217;uso di parentesi.</p>
<p>Notazione postfissa<br />
modo di scrivere espressioni matematiche con gli operatori<br />
posti dopo gli operandi (detta anche &#8220;notazione polacca<br />
inversa&#8221;).</p>
<p>Parsing<br />
lettura di una stringa di caratteri per l&#8217;analisi dei token e<br />
della struttura grammaticale.</p>
<p>Token<br />
serie di caratteri che viene trattata come un&#8217;unità<br />
nell&#8217;operazione di parsing, allo stesso modo delle parole in un<br />
linguaggio naturale.</p>
<p>Delimitatore<br />
carattere usato per separare i token, allo stesso modo della<br />
punteggiatura in un linguaggio naturale.</p>
<p>Capitolo 19</p>
<p>Code</p>
<p>Questo capitolo presenta due tipi di dati astratti (TDA): la Coda e la<br />
Coda con priorità. Nella vita reale un esempio di coda può essere la<br />
linea di clienti in attesa di un servizio di qualche tipo. Nella<br />
maggior parte dei casi il primo cliente della fila è quello che sarà<br />
servito per primo, anche se ci possono essere delle eccezioni.<br />
All&#8217;aeroporto ai clienti il cui volo sta per partire può essere<br />
concesso di passare davanti a tutti, indipendentemente dalla loro<br />
posizione nella fila. Al supermercato un cliente può scambiare per<br />
cortesia il suo posto con qualcuno che deve pagare solo pochi<br />
prodotti.</p>
<p>La regola che determina chi sarà il prossimo ad essere servito si<br />
chiama politica di accodamento. Quella più semplice è la FIFO (&#8220;first<br />
in, first out&#8221;) dove il primo che arriva è il primo ad essere servito.<br />
La politica di accodamento più generale è l&#8217; accodamento con priorità<br />
dove a ciascun cliente è assegnata una priorità ed il cliente con la<br />
massima priorità viene servito per primo indipendentemente dall&#8217;ordine<br />
di arrivo. Diciamo che questa politica di accodamento è la più<br />
generale perché la priorità può essere basata su qualsiasi fattore:<br />
l&#8217;orario di partenza dell&#8217;aereo, la quantità di prodotti da pagare ad<br />
una cassa, l&#8217;importanza del cliente (!), la gravità dello stato di un<br />
paziente al pronto soccorso. Logicamente non tutte le politiche di<br />
accodamento sono &#8220;giuste&#8221;&#8230;</p>
<p>I tipi di dati astratti Coda e Coda con priorità condividono lo stesso<br />
insieme di operazioni. La differenza sta soltanto nella loro<br />
semantica: una Coda usa la politica FIFO, mentre la Coda con priorità,<br />
come suggerisce il nome stesso, usa la politica di accodamento con<br />
priorità.</p>
<p>19.1 Il TDA Coda</p>
<p>Il TDA Coda è definito dalle operazioni seguenti:</p>
<p>__init__<br />
Inizializza una nuova coda vuota.</p>
<p>Inserimento<br />
Aggiunge un nuovo elemento alla coda.</p>
<p>Rimozione<br />
Rimuove e ritorna un elemento dalla coda. L&#8217;elemento ritornato<br />
è il primo inserito nella coda in ordine di tempo.</p>
<p>EVuota<br />
Controlla se la coda è vuota.</p>
<p>19.2 Coda linkata</p>
<p>La prima implementazione del TDA Coda a cui guarderemo è chiamata coda<br />
linkata perché è composta di oggetti Nodo linkati. Ecco una<br />
definizione della classe:</p>
<p>class Coda:<br />
def __init__(self):<br />
self.Lunghezza = 0<br />
self.Testa = None<br />
def EVuota(self):<br />
return (self.Lunghezza == 0)<br />
def Inserimento(self, Contenuto):<br />
NodoAggiunto = Nodo(Contenuto)<br />
NodoAggiunto.ProssimoNodo = None<br />
if self.Testa == None:<br />
# se la lista e&#8217; vuota il nodo e&#8217; il primo<br />
self.Testa = Nodo<br />
else:<br />
# trova l&#8217;ultimo nodo della lista<br />
Ultimo = self.Testa<br />
while Ultimo.ProssimoNodo: Ultimo = Ultimo.ProssimoNodo<br />
# aggiunge il nuovo nodo<br />
Ultimo.ProssimoNodo = NodoAggiunto<br />
self.Lunghezza = self.Lunghezza + 1<br />
def Rimozione(self):<br />
Contenuto = self.Testa.Contenuto<br />
self.Testa = self.Testa.ProssimoNodo<br />
self.Lunghezza = self.Lunghezza &#8211; 1<br />
return Contenuto</p>
<p>I metodi EVuota e Rimozione sono identici a quelli usati in<br />
ListaLinkata. Il metodo Inserimento è nuovo ed un po&#8217; più complicato.</p>
<p>Vogliamo inserire nuovi elementi alla fine della lista: se la coda è<br />
vuota facciamo in modo che Testa si riferisca al nuovo nodo.</p>
<p>Altrimenti attraversiamo la lista fino a raggiungere l&#8217;ultimo nodo e<br />
attacchiamo a questo il nuovo nodo. Possiamo identificare facilmente<br />
l&#8217;ultimo nodo della lista perché è l&#8217;unico il cui attributo<br />
ProssimoNodo vale None.</p>
<p>Ci sono due invarianti per un oggetto Coda ben formato: il valore di<br />
Lunghezza dovrebbe essere il numero di nodi nella coda e l&#8217;ultimo nodo<br />
dovrebbe avere l&#8217;attributo ProssimoNodo uguale a None. Prova a<br />
studiare il metodo implementato verificando che entrambi gli<br />
invarianti siano sempre soddisfatti.</p>
<p>19.3 Performance</p>
<p>Normalmente quando invochiamo un metodo non ci interessa quali siano i<br />
dettagli della sua implementazione. Ma c&#8217;è uno di questi dettagli che<br />
invece dovrebbe interessarci: le performance del metodo. Quanto<br />
impiega ad essere eseguito? Come cambia il tempo di esecuzione man<br />
mano che la collezione aumenta di dimensioni?</p>
<p>Diamo un&#8217;occhiata a Rimozione. Non ci sono cicli o chiamate a<br />
funzione, e ciò suggerisce che il tempo di esecuzione sarà lo stesso<br />
ogni volta. Questo tipo di metodo è definito operazione a tempo<br />
costante. In realtà il metodo potrebbe essere leggermente più veloce<br />
quando la lista è vuota dato che tutto il corpo della condizione viene<br />
saltato, ma la differenza in questo caso non è molto significativa e<br />
può essere tranquillamente trascurata.</p>
<p>La performance di Inserimento è molto diversa. Nel caso generale<br />
dobbiamo attraversare completamente la lista per trovarne l&#8217;ultimo<br />
elemento.</p>
<p>Questo attraversamento impiega un tempo che è proporzionale alla<br />
grandezza della lista: dato che il tempo di esecuzione in funzione<br />
lineare rispetto alla lunghezza, diciamo che questo metodo è<br />
un&#8217;operazione a tempo lineare. Se confrontato ad un&#8217;operazione a tempo<br />
costante il suo comportamento è decisamente peggiore.</p>
<p>19.4 Lista linkata migliorata</p>
<p>Logicamente un&#8217;implementazione del TDA Coda che può eseguire tutte le<br />
operazioni in un tempo costante è preferibile, dato che in questo caso<br />
il tempo di esecuzione è indipendente dalla grandezza della lista<br />
elaborata. Un modo per fare questo è quello di modificare la classe<br />
Coda per fare in modo che venga tenuta traccia tanto del primo che<br />
dell&#8217;ultimo elemento della lista, come mostrato in questa figura:</p>
<p>[i_queue1.png]</p>
<p>L&#8217;implementazione di CodaMigliorata potrebbe essere:</p>
<p>class CodaMigliorata:<br />
def __init__(self):<br />
self.Lunghezza = 0<br />
self.Testa = None<br />
self.UltimoNodo = None<br />
def EVuota(self):<br />
return (self.Lunghezza == 0)</p>
<p>Finora l&#8217;unico cambiamento riguarda l&#8217;aggiunta dell&#8217;attributo<br />
UltimoNodo.<br />
Questo attributo è usato dai metodi Inserimento e Rimozione:</p>
<p>class CodaMigliorata:<br />
&#8230;<br />
def Inserimento(self, Contenuto):<br />
NodoAggiunto = Nodo(Contenuto)<br />
NodoAggiunto.ProssimoNodo = None<br />
if self.Lunghezza == 0:<br />
# se la lista e&#8217; vuota il nuovo nodo e&#8217;<br />
# sia la testa che la coda<br />
self.Testa = self.UltimoNodo = NodoAggiunto<br />
else:<br />
# trova l&#8217;ultimo nodo<br />
Ultimo = self.UltimoNodo<br />
# aggiunge il nuovo nodo<br />
Ultimo.ProssimoNodo = NodoAggiunto<br />
self.UltimoNodo = NodoAggiunto<br />
self.Lunghezza = self.Lunghezza + 1</p>
<p>Dato che UltimoNodo tiene traccia dell&#8217;ultimo nodo non dobbiamo più<br />
attraversare la lista per cercarlo. Come risultato abbiamo fatto<br />
diventare questo metodo un&#8217;operazione a tempo costante.</p>
<p>Comunque dobbiamo pagare un prezzo per questa modifica: quando<br />
dobbiamo rimuovere l&#8217;ultimo nodo con Rimozione dovremo assegnare None<br />
a UltimoNodo:</p>
<p>class CodaMigliorata:<br />
&#8230;<br />
def Rimozione(self):<br />
Contenuto = self.Testa.Contenuto<br />
self.Testa = self.Testa.ProssimoNodo<br />
self.Lunghezza = self.Lunghezza &#8211; 1<br />
if self.Lunghezza == 0:<br />
self.UltimoNodo = None<br />
return Contenuto</p>
<p>Questa implementazione è più complessa di quella della coda linkata ed<br />
è più difficile dimostrare che è corretta, Il vantaggio che abbiamo<br />
comunque ottenuto è l&#8217;aver reso sia Inserimento che Rimozione<br />
operazioni a tempo costante.</p>
<p>Esercizio: scrivi un&#8217;implementazione del TDA Coda usando una lista<br />
di Python. Confronta le performance di questa implementazione con<br />
quelle di CodaMigliorata per una serie di lunghezze diverse della<br />
coda.</p>
<p>19.5 Coda con priorità</p>
<p>Il TDA Coda con priorità ha la stessa interfaccia del TDA Coda ma una<br />
semantica diversa. L&#8217;interfaccia è sempre:</p>
<p>__init__<br />
Inizializza una nuova coda vuota.</p>
<p>Inserimento<br />
Aggiungi un elemento alla coda.</p>
<p>Rimozione<br />
Rimuovi un elemento dalla coda. L&#8217;elemento da rimuovere e<br />
ritornare è quello con la priorità più alta.</p>
<p>EVuota<br />
Controlla se la coda è vuota.</p>
<p>La differenza di semantica è che l&#8217;elemento da rimuovere non è<br />
necessariamente il primo inserito in coda, ma quello che ha la<br />
priorità più alta. Cosa siano le priorità e come siano implementate<br />
sono fatti non specificati dall&#8217;implementazione, dato che questo<br />
dipende dal genere di elementi che compongono la coda.</p>
<p>Per esempio se gli elementi nella coda sono delle stringhe potremmo<br />
estrarle in ordine alfabetico. Se sono punteggi del bowling dal più<br />
alto al più basso, e viceversa nel caso del golf. In ogni caso<br />
possiamo rimuovere l&#8217;elemento con la priorità più alta da una coda<br />
soltanto se i suoi elementi sono confrontabili tra di loro.</p>
<p>Questa è un&#8217;implementazione di una coda con priorità che usa una lista<br />
Python come attributo per contenere gli elementi della coda:</p>
<p>class CodaConPriorita:<br />
def __init__(self):<br />
self.Elementi = []<br />
def EVuota(self):<br />
return self.Elementi == []<br />
def Inserimento(self, Elemento):<br />
self.Elementi.append(Elemento)</p>
<p>I metodi __init__, EVuota e Inserimento sono tutte maschere delle<br />
operazioni su liste. L&#8217;unico metodo &#8220;interessante&#8221; è Rimozione:</p>
<p>class CodaConPriorita:<br />
&#8230;<br />
def Rimozione(self):<br />
Indice = 0<br />
for i in range(1,len(self.Elementi)):<br />
if self.Elementi[i] &gt; self.Elementi[Indice]:<br />
Indice = i<br />
Elemento = self.Elementi[Indice]<br />
self.Elementi[Indice:Indice+1] = []<br />
return Elemento</p>
<p>All&#8217;inizio di ogni iterazione Indice contiene l&#8217;indice dell&#8217;elemento<br />
con priorità massima. Ad ogni ciclo viene confrontato questo elemento<br />
con l&#8217;i-esimo elemento della lista: se il nuovo elemento ha priorità<br />
maggiore (nel nostro caso è maggiore), il valore di Indice diventa i.</p>
<p>Quando il ciclo for è stato completato Indice è l&#8217;indice dell&#8217;elemento<br />
con priorità massima. Questo elemento è rimosso dalla lista e<br />
ritornato.</p>
<p>Testiamo l&#8217;implementazione:</p>
<p>&gt;&gt;&gt; q = CodaConPriorita()<br />
&gt;&gt;&gt; q.Inserimento(11)<br />
&gt;&gt;&gt; q.Inserimento(12)<br />
&gt;&gt;&gt; q.Inserimento(14)<br />
&gt;&gt;&gt; q.Inserimento(13)<br />
&gt;&gt;&gt; while not q.EVuota(): print q.Rimozione()<br />
14<br />
13<br />
12<br />
11</p>
<p>Se la coda contiene solo numeri o stringhe questi vengono rimossi in<br />
ordine numerico o alfabetico, dal più alto al più basso. Python può<br />
sempre trovare il numero o la stringa più grande perché può<br />
confrontare coppie di questi operandi con operatori di confronto<br />
predefiniti.</p>
<p>Se la coda contenesse un oggetto di tipo non predefinito è necessario<br />
fornire anche un metodo __cmp__ per poter effettuare il confronto.<br />
Quando Rimozione usa l&#8217;operatore &gt; per confrontare gli elementi in<br />
realtà invoca __cmp__ per uno degli operandi e passa l&#8217;altro come<br />
parametro. La Coda con priorità funziona come ci si aspetta solo se il<br />
metodo __cmp__ opera correttamente.</p>
<p>19.6 La classe Golf</p>
<p>Come esempio di oggetto con una definizione inusuale di priorità<br />
implementiamo una classe chiamata Golf che tiene traccia dei nomi e<br />
dei punteggi di un gruppo di golfisti. Partiamo con la definizione di<br />
__init__ e __str__:</p>
<p>class Golf:<br />
def __init__(self, Nome, Punteggio):<br />
self.Nome = Nome<br />
self.Punteggio = Punteggio<br />
def __str__(self):<br />
return &#8220;%-16s: %d&#8221; % (self.Nome, self.Punteggio)</p>
<p>__str__ usa l&#8217;operatore di formato per stampare i nomi ed i punteggi<br />
in forma tabellare su colonne ordinate.</p>
<p>Poi definiamo una versione di __cmp__ dove il punteggio minore ottiene<br />
la priorità più alta: come abbiamo già visto in precedenza __cmp__<br />
ritorna 1 se self è più grande di Altro, -1 se self è minore di Altro,<br />
e 0 se i due valori sono uguali.</p>
<p>class Golf:<br />
&#8230;<br />
def __cmp__(self, Altro):<br />
if self.Punteggio &lt; Altro.Punteggio: return  1<br />
if self.Punteggio &gt; Altro.Punteggio: return -1<br />
return 0</p>
<p>Ora siamo pronti a testare la coda con priorità sulla classe Golf:</p>
<p>&gt;&gt;&gt; tiger = Golf(&#8220;Tiger Woods&#8221;,    61)<br />
&gt;&gt;&gt; phil  = Golf(&#8220;Phil Mickelson&#8221;, 72)<br />
&gt;&gt;&gt; hal   = Golf(&#8220;Hal Sutton&#8221;,     69)<br />
&gt;&gt;&gt;<br />
&gt;&gt;&gt; pq = CodaConPriorità()<br />
&gt;&gt;&gt; pq.Inserimento(tiger)<br />
&gt;&gt;&gt; pq.Inserimento(phil)<br />
&gt;&gt;&gt; pq.Inserimento(hal)<br />
&gt;&gt;&gt; while not pq.EVuota(): print pq.Rimozione()<br />
Tiger Woods    : 61<br />
Hal Sutton     : 69<br />
Phil Mickelson : 72</p>
<p>Esercizio: scrivi un&#8217;implementazione di un TDA Coda con priorità<br />
facendo uso di una lista linkata. Dovrai tenere la lista sempre<br />
ordinata per fare in modo che la rimozione di un elemento sia<br />
un&#8217;operazione a tempo costante. Confronta le performance di questa<br />
implementazione con l&#8217;implementazione delle liste in Python.</p>
<p>19.7 Glossario</p>
<p>Coda<br />
insieme di oggetti in attesa di un servizio di qualche tipo;<br />
abbiamo implementato un TDA Coda che esegue le comuni<br />
operazioni su una coda.</p>
<p>Politica di accodamento<br />
regole che determinano quale elemento di una coda debba essere<br />
rimosso per primo.</p>
<p>FIFO<br />
&#8220;First In, First Out&#8221; (primo inserito, primo rimosso) politica<br />
di accodamento nella quale il primo elemento a essere rimosso è<br />
il primo ad essere stato inserito.</p>
<p>Coda con priorità<br />
politica di accodamento nella quale ogni elemento ha una<br />
priorità determinata da fattori esterni. L&#8217;elemento con la<br />
priorità più alta è il primo ad essere rimosso. Abbiamo<br />
implementato un TDA Coda con priorità che definisce le comuni<br />
operazioni richieste da una coda con priorità.</p>
<p>Coda linkata<br />
implementazione di una coda realizzata usando una lista<br />
linkata.</p>
<p>Operazione a tempo costante<br />
elaborazione il cui tempo di esecuzione non dipende (o dipende<br />
in minima parte) dalla dimensione della struttura di dati da<br />
elaborare.</p>
<p>Operazione a tempo lineare<br />
elaborazione il cui tempo di esecuzione è proporzionale alla<br />
dimensione della struttura di dati da elaborare.</p>
<p>Capitolo 20</p>
<p>Alberi</p>
<p>Come nel caso delle altre liste linkate, un albero è costituito di<br />
nodi. Un tipo comune di albero è l&#8217; albero binario nel quale ciascun<br />
nodo fa riferimento a due altri nodi che possono anche avere valore<br />
None (in questo caso è prassi comune non indicarli nei diagrammi di<br />
stato). Questi riferimenti vengono normalmente chiamati &#8220;rami&#8221; (o<br />
&#8220;sottoalberi&#8221;) sinistro e destro.</p>
<p>Come nel caso dei nodi degli altri tipi di lista anche in questo caso<br />
un nodo possiede un contenuto. Ecco un diagramma di stato per un<br />
albero:</p>
<p>[i_tree1.png]</p>
<p>Il nodo principale dell&#8217;albero è chiamato radice, gli altri nodi rami<br />
e quelli terminali foglie. Si noti come l&#8217;albero viene generalmente<br />
disegnato capovolto, con la radice in alto e le foglie in basso.</p>
<p>Per rendere le cose più confuse vengono talvolta usate delle<br />
terminologie alternative che fanno riferimento ad un albero<br />
genealogico o alla geometria. Nel primo caso il nodo alla sommità è<br />
detto genitore e i nodi cui esso si riferisce figli; nodi con gli<br />
stessi genitori sono detti fratelli. Nel secondo caso parliamo di nodi<br />
a &#8220;sinistra&#8221; e &#8220;destra&#8221;, in &#8220;alto&#8221; (verso il genitore/radice) e in<br />
&#8220;basso&#8221; (verso i figli/foglie).</p>
<p>Indipendentemente dai termini usati tutti i nodi che hanno la stessa<br />
distanza dalla radice appartengono allo stesso livello.</p>
<p>Come nel caso delle liste linkate gli alberi sono strutture di dati<br />
ricorsive:</p>
<p>Un albero è:<br />
* un albero vuoto, rappresentato da None oppure<br />
* un nodo che contiene un riferimento ad un oggetto e due<br />
riferimenti ad alberi.</p>
<p>20.1 La costruzione degli alberi</p>
<p>Il processo di costruzione degli alberi è simile a quello che abbiamo<br />
già visto nel caso delle liste linkate. Ogni invocazione del<br />
costruttore aggiunge un singolo nodo:</p>
<p>class Albero:<br />
def __init__(self, Contenuto, Sinistra=None, Destra=None):<br />
self.Contenuto = Contenuto<br />
self.Sinistra  = Sinistra<br />
self.Destra = Destra<br />
def __str__(self):<br />
return str(self.Contenuto)</p>
<p>Il Contenuto può essere di tipo qualsiasi ma sia Sinistra che Destra<br />
devono essere nodi di un albero. Sinistra e Destra sono opzionali ed<br />
il loro valore di default è None, significando con questo che non sono<br />
linkati ad altri nodi.</p>
<p>Come per gli altri nodi che abbiamo visto precedentemente, la stampa<br />
di un nodo dell&#8217;albero mostra soltanto il contenuto del nodo stesso.</p>
<p>Un modo per costruire un albero è quello di partire dal basso verso<br />
l&#8217;alto, allocando per primi i nodi figli:</p>
<p>FiglioSinistra = Albero(2)<br />
FiglioDestra = Albero(3)</p>
<p>Poi creiamo il nodo genitore collegandolo ai figli:</p>
<p>Albero = Albero(1, FiglioSinistra, FiglioDestra);</p>
<p>Possiamo anche scrivere in modo più conciso questo codice invocando un<br />
costruttore annidato:</p>
<p>&gt;&gt;&gt; Albero = Albero(1, Albero(2), Albero(3))</p>
<p>In ogni caso il risultato è l&#8217;albero presentato graficamente<br />
all&#8217;inizio del capitolo.</p>
<p>20.2 Attraversamento degli alberi</p>
<p>Ogni volta che vedi una nuova struttura la tua prima domanda dovrebbe<br />
essere &#8220;come posso attraversarla?&#8221;. Il modo più intuitivo per<br />
attraversare un albero è quello di usare un algoritmo ricorsivo.</p>
<p>Per fare un esempio, se il nostro albero contiene interi questa<br />
funzione ne restituisce la somma:</p>
<p>def Totale(Albero):<br />
if Albero == None: return 0<br />
return Albero.Contenuto + Totale(Albero.Sinistra) + \<br />
Totale(Albero.Destra)</p>
<p>Il caso base è l&#8217;albero vuoto che non ha contenuto e che quindi ha<br />
valore 0. Il passo successivo chiama due funzioni ricorsive per<br />
calcolare la somma dei rami figli. Quando la serie di chiamate<br />
ricorsiva è completa la funzione ritorna il totale.</p>
<p>20.3 Albero di espressioni</p>
<p>Un albero è un modo naturale per rappresentare una struttura di<br />
espressioni e a differenza di altre notazioni può rappresentare la<br />
loro elaborazione in modo non ambiguo (l&#8217;espressione infissa 1 + 2 * 3<br />
è ambigua a meno che non si sappia che la moltiplicazione deve essere<br />
elaborata prima dell&#8217;addizione).</p>
<p>Ecco l&#8217;albero che rappresenta questa espressione:</p>
<p>[i_tree2.png]</p>
<p>I nodi dell&#8217;albero possono essere operandi come 1 e 2 o operatori come<br />
+ e *. Gli operandi sono i nodi foglia, e i nodi operatore contengono<br />
i riferimenti ai rispettivi operandi. È importante notare che tutte<br />
queste operazioni sono binarie nel senso che hanno esattamente due<br />
operandi.</p>
<p>Possiamo costruire alberi come questo:</p>
<p>&gt;&gt;&gt; Albero = Albero(&#8216;+&#8217;, Albero(1), Albero(&#8216;*&#8217;, Albero(2), \<br />
Albero(3)))</p>
<p>Guardando la figura non c&#8217;è assolutamente alcun problema nel<br />
determinare l&#8217;ordine delle operazioni: la moltiplicazione deve essere<br />
eseguita per prima per ottenere un risultato necessario all&#8217;addizione.</p>
<p>Gli alberi di espressioni hanno molti usi tra i quali possiamo citare<br />
la rappresentazione di espressioni matematiche postfisse e infisse<br />
(come abbiamo appena visto), e le operazioni di parsing,<br />
ottimizzazione e traduzione dei programmi nei compilatori.</p>
<p>20.4 Attraversamento di un albero</p>
<p>Potremmo attraversare un espressione ad albero e stampare il suo<br />
contenuto con:</p>
<p>def StampaAlberoPre(Albero):<br />
if Albero == None: return<br />
print Albero.Contenuto,<br />
StampaAlberoPre(Albero.Sinistra)<br />
StampaAlberoPre(Albero.Destra)</p>
<p>Per stampare questo albero abbiamo deciso di stamparne la radice, poi<br />
l&#8217;intero ramo di sinistra e poi quello di destra. Questo modo di<br />
attraversare l&#8217;albero è detto con preordine perché la radice appare<br />
sempre prima del contenuto dei figli. La stampa nel nostro caso è:</p>
<p>&gt;&gt;&gt; Albero = Albero(&#8216;+&#8217;, Albero(1), Albero(&#8216;*&#8217;, Albero(2), \<br />
Albero(3)))<br />
&gt;&gt;&gt; StampaAlberoPre(Albero)<br />
+ 1 * 2 3</p>
<p>Questo formato di stampa è diverso sia da quello che ci saremmo<br />
aspettati dalla notazione postfissa sia da quella infissa: si tratta<br />
infatti di una notazione chiamata prefissa nella quale gli operatori<br />
compaiono prima dei loro operandi.</p>
<p>Avrai già capito che cambiando l&#8217;ordine di attraversamento dell&#8217;albero<br />
sarà possibile ricavare le altre notazioni equivalenti. Se stampiamo<br />
prima i rami e poi il nodo radice otteniamo:</p>
<p>def StampaAlberoPost(Albero):<br />
if Albero == None: return<br />
StampaAlberoPost(Albero.Sinistra)<br />
StampaAlberoPost(Albero.Destra)<br />
print Albero.Contenuto,</p>
<p>Il risultato è 1 2 3 * + in notazione postfissa. Questo tipo di<br />
attraversamento è chiamato postordine.</p>
<p>L&#8217;ultimo caso da considerare è l&#8217;attraversamento dell&#8217;albero con<br />
inordine, dove stampiamo il ramo sinistro, poi la radice ed infine il<br />
ramo destro:</p>
<p>def StampaAlberoIn(Albero):<br />
if Albero == None: return<br />
StampaAlberoIn(Albero.Sinistra)<br />
print Albero.Contenuto,<br />
StampaAlberoIn(Albero.Destra)</p>
<p>Il risultato è 1 + 2 * 3 in notazione infissa.</p>
<p>Ad essere onesti dovremmo menzionare una complicazione molto<br />
importante sulla quale abbiamo sorvolato. Talvolta è necessario l&#8217;uso<br />
delle parentesi per conservare l&#8217;ordine delle operazioni nelle<br />
espressioni infisse, così che un attraversamento con inordine non è<br />
sufficiente a generare un&#8217;espressione infissa corretta.</p>
<p>Ciononostante, e con poche modifiche, l&#8217;albero delle espressioni e tre<br />
diversi attraversamenti ricorsivi ci hanno permesso di tradurre<br />
diverse espressioni da una notazione all&#8217;altra.</p>
<p>Esercizio: modifica StampaAlberoIn così da mettere un paio di<br />
parentesi che racchiuda ogni coppia di operandi ed il loro<br />
operatore. Il risultato può essere considerato a questo punto<br />
corretto e non ambiguo? Sono sempre necessarie le parentesi?</p>
<p>Se attraversiamo con inordine e teniamo traccia di quale livello<br />
dell&#8217;albero ci troviamo possiamo generare una rappresentazione grafica<br />
dell&#8217;albero:</p>
<p>def StampaAlberoIndentato(Albero, Livello=0):<br />
if Albero == None: return<br />
StampaAlberoIndentato(Albero.Destra, Livello+1)<br />
print &#8216;  &#8216;*Livello + str(Albero.Contenuto)<br />
StampaAlberoIndentato(Albero.Sinistra, Livello+1)</p>
<p>Il parametro Livello tiene traccia di dove ci troviamo nell&#8217;albero e<br />
per default vale inizialmente 0. Ogni volta che effettuiamo una<br />
chiamata ricorsiva passiamo Livello+1 perché il livello del figlio è<br />
sempre più grande di 1 rispetto a quello del genitore. Ogni elemento è<br />
indentato di due spazi per ogni livello.</p>
<p>Il risultato del nostro albero di esempio è:</p>
<p>&gt;&gt;&gt; StampaAlberoIndentato(Albero)<br />
3<br />
*<br />
2<br />
+<br />
1</p>
<p>Guardando la figura dopo aver girato il foglio vedrai una versione<br />
semplificata della figura originale.</p>
<p>20.5 Costruire un albero di espressione</p>
<p>In questa sezione effettueremo il parsing di un&#8217;espressione infissa e<br />
costruiremo il corrispondente albero. L&#8217;espressione (3+7)*9 produce<br />
questo diagramma, dove non sono stati indicati i nomi degli attributi:</p>
<p>[tree3.png]</p>
<p>Il parser che scriveremo dovrà riuscire a gestire espressioni<br />
contenenti numeri, parentesi e gli operatori + e *. Partiamo dal<br />
presupposto che la stringa da analizzare sia già stata spezzata in<br />
token che nel nostro caso sono elementi di una lista:</p>
<p>['(', 3, '+', 7, ')', '*', 9, 'end']</p>
<p>Il token end è stato aggiunto per fare il modo che il parser non<br />
continui la lettura al termine della lista.</p>
<p>Esercizio: scrivi una funzione che accetta un&#8217;espressione e la<br />
converte in una lista di token.</p>
<p>La prima funzione che scriveremo è ControllaToken che prende come<br />
parametri una lista di token e un token atteso: dopo aver confrontato<br />
il token atteso con il primo elemento della lista, se i due coincidono<br />
l&#8217;elemento della lista viene rimosso e viene ritornato il valore vero;<br />
in caso contrario viene ritornato falso.</p>
<p>def ControllaToken(ListaToken, TokenAtteso):<br />
if ListaToken[0] == TokenAtteso:<br />
del ListaToken[0]<br />
return 1<br />
else:<br />
return 0</p>
<p>Dato che ListaToken si riferisce ad un oggetto mutabile i cambiamenti<br />
fatti sono visibili da qualsiasi altra variabile che si riferisce allo<br />
stesso oggetto.</p>
<p>La prossima funzione, ControllaNumero, gestisce gli operandi: se il<br />
prossimo elemento in ListaToken è un numero ControllaNumero lo rimuove<br />
dalla lista e ritorna un nodo foglia contenente il numero; in caso<br />
contrario viene ritornato None:</p>
<p>def ControllaNumero(ListaToken):<br />
x = ListaToken[0]<br />
if type(x) != type(0): return None<br />
del ListaToken[0]<br />
return Albero(x, None, None)</p>
<p>Prima di continuare è buona cosa testare isolatamente ControllaNumero.<br />
Assegniamo una lista di numeri a ListaToken, ne estraiamo il primo,<br />
stampiamo il risultato e ciò che rimane della lista di token:</p>
<p>&gt;&gt;&gt; Lista = [9, 11, 'end']<br />
&gt;&gt;&gt; x = ControllaNumero(Lista)<br />
&gt;&gt;&gt; StampaAlberoPost(x)<br />
9<br />
&gt;&gt;&gt; print Lista<br />
[11, 'end']</p>
<p>Il prossimo metodo di cui avremo bisogno è EsprProdotto che costruisce<br />
un albero di espressione per le moltiplicazioni del tipo 3*7.</p>
<p>Ecco una versione di EsprProdotto che gestisce prodotti semplici:</p>
<p>def EsprProdotto(ListaToken):<br />
a = ControllaNumero(ListaToken)<br />
if ControllaToken(ListaToken, &#8216;*&#8217;):<br />
b = ControllaNumero(ListaToken)<br />
return Albero(&#8216;*&#8217;, a, b)<br />
else:<br />
return a</p>
<p>Se ControllaNumero ha successo e ritorna un nodo assegniamo il primo<br />
operando ad a. Se il carattere successivo è * ricaviamo il secondo<br />
numero e costruiamo un albero con a, b e l&#8217;operatore moltiplicazione.<br />
Se il secondo carattere è qualcos&#8217;altro ritorniamo il nodo foglia con<br />
contenuto pari ad a.</p>
<p>Ecco un paio di esempi:</p>
<p>&gt;&gt;&gt; ListaToken = [9, '*', 11, 'end']<br />
&gt;&gt;&gt; Albero = EsprProdotto(ListaToken)<br />
&gt;&gt;&gt; StampaAlberoPost(Albero)<br />
9 11 *</p>
<p>&gt;&gt;&gt; ListaToken = [9, '+', 11, 'end']<br />
&gt;&gt;&gt; Albero = EsprProdotto(ListaToken)<br />
&gt;&gt;&gt; StampaAlberoPost(Albero)<br />
9</p>
<p>Il secondo esempio mostra che noi consideriamo un singolo operando<br />
come una moltiplicazione valida. Questa definizione di &#8220;prodotto&#8221; non<br />
è proprio intuitiva, ma risulta esserci molto utile in questo caso.</p>
<p>Ora vediamo di gestire i prodotti composti, come in 3*5*13. Tratteremo<br />
questa espressione come prodotto di prodotti, e cioè 3*(5*13).<br />
L&#8217;albero risultante è:</p>
<p>[tree4.png]</p>
<p>Con un piccolo cambiamento in EsprProdotto possiamo gestire prodotti<br />
arbitrariamente lunghi:</p>
<p>def EsprProdotto(ListaToken):<br />
a = ControllaNumero(ListaToken)<br />
if ControllaToken(ListaToken, &#8216;*&#8217;):<br />
b = EsprProdotto(ListaToken)       # questa linea e&#8217; cambiata<br />
return Albero(&#8216;*&#8217;, a, b)<br />
else:<br />
return a</p>
<p>In altre parole un prodotto può essere o un valore singolo o un albero<br />
con * alla radice, un numero a sinistra e un prodotto alla destra.<br />
Ormai dovresti cominciare a sentirti a tuo agio con questa definizione<br />
ricorsiva.</p>
<p>Testiamo la nuova versione con un prodotto composto:</p>
<p>&gt;&gt;&gt; ListaToken = [2, '*', 3, '*', 5 , '*', 7, 'end']<br />
&gt;&gt;&gt; Albero = EsprProdotto(ListaToken)<br />
&gt;&gt;&gt; StampaAlberoPost(Albero)<br />
2 3 5 7 * * *</p>
<p>Continuiamo con la nostra implementazione andando a gestire le somme.<br />
Ancora una volta useremo una definizione di somma che non è del tutto<br />
intuitiva: una somma può essere un albero con + alla radice, un<br />
prodotto a sinistra e una somma a destra. Inoltre consideriamo come<br />
&#8220;somma&#8221; anche un prodotto.</p>
<p>Se non riesci a comprenderne il significato, è possibile immaginare<br />
qualsiasi espressione priva di parentesi (ricorda che stiamo lavorando<br />
solo su addizioni e moltiplicazioni) come somme di prodotti. Questa<br />
proprietà rappresenta la base del nostro algoritmo di parsing.</p>
<p>EsprSomma prova a costruire un albero con un prodotto a sinistra e una<br />
somma a destra; nel caso non riesca a trovare un operatore +<br />
restituisce il prodotto.</p>
<p>def EsprSomma(ListaToken):<br />
a = EsprProdotto(ListaToken)<br />
if ControllaToken(ListaToken, &#8216;+&#8217;):<br />
b = EsprSomma(ListaToken)<br />
return Albero(&#8216;+&#8217;, a, b)<br />
else:<br />
return a</p>
<p>Proviamo con 9 * 11 + 5 * 7:</p>
<p>&gt;&gt;&gt; ListaToken = [9, '*', 11, '+', 5, '*', 7, 'end']<br />
&gt;&gt;&gt; Albero = EsprSomma(ListaToken)<br />
&gt;&gt;&gt; StampaAlberoPost(Albero)<br />
9 11 * 5 7 * +</p>
<p>Ora ci mancano solo le parentesi. Dovunque in un&#8217;espressione compaia<br />
un numero, lì può essere racchiusa un&#8217;intera somma tra parentesi.<br />
Dobbiamo solo modificare ControllaNumero per gestire le<br />
sub-espressioni:</p>
<p>def ControllaNumero(ListaToken):<br />
if ControllaToken(ListaToken, &#8216;(&#8216;):<br />
x = EsprSomma(ListaToken)         # ricava la sub-espressione<br />
ControllaToken(ListaToken, &#8216;)&#8217;)   # rimuove la parentesi<br />
# chiusa<br />
return x<br />
else:<br />
x = ListaToken[0]<br />
if type(x) != type(0): return None<br />
ListaToken[0:1] = []<br />
return Albero(x, None, None)</p>
<p>Testiamo questa nuova funzione con 9 * (11 + 5) * 7:</p>
<p>&gt;&gt;&gt; ListaToken = [9, '*', '(', 11, '+', 5, ')', '*', 7, 'end']<br />
&gt;&gt;&gt; Albero = EsprSomma(ListaToken)<br />
&gt;&gt;&gt; StampaAlberoPost(Albero)<br />
9 11 5 + 7 * *</p>
<p>Il parser ha gestito correttamente le parentesi e l&#8217;addizione viene<br />
eseguita prima della moltiplicazione.</p>
<p>Nella versione finale del programma è una buona idea dare a<br />
ControllaNumero un nuovo nome più coerente con il suo nuovo ruolo.</p>
<p>20.6 Gestione degli errori</p>
<p>Le espressioni che dobbiamo passare al parser devono essere ben<br />
formate. Se abbiamo raggiunto la fine di una sub-espressione ci<br />
aspettiamo una parentesi chiusa: nel caso questa non sia presente<br />
sarebbe il caso di gestire questa condizione d&#8217;errore.</p>
<p>def ControllaNumero(ListaToken):<br />
if ControllaToken(ListaToken, &#8216;(&#8216;):<br />
x = EsprSomma(ListaToken)<br />
if not ControllaToken(ListaToken, &#8216;)&#8217;):<br />
raise &#8216;BadExpressionError&#8217;, &#8216;manca la parentesi&#8217;<br />
return x<br />
else:<br />
# omettiamo il resto della funzione</p>
<p>L&#8217;istruzione raise crea un&#8217;eccezione: in questo caso abbiamo creato un<br />
nuovo tipo di errore chiamato BadExpressionError. Se la funzione che<br />
chiama ControllaNumero o una delle altre funzioni indicate in traccia<br />
al momento dell&#8217;errore gestisce le eccezioni allora il programma può<br />
continuare; in caso contrario Python mostra il messaggio di errore e<br />
si interrompe.</p>
<p>Esercizio: trova altri posti in queste funzioni in cui possono<br />
verificarsi errori e aggiungi le istruzioni raise appropriate.<br />
Testa successivamente il tuo codice passando alle funzioni delle<br />
espressioni errate.</p>
<p>20.7 L&#8217;albero degli animali</p>
<p>In questa sezione svilupperemo un piccolo programma che usa un albero<br />
per rappresentare un sistema di conoscenze e aumentando la sua<br />
ampiezza grazie all&#8217;interazione con l&#8217;operatore.</p>
<p>Il programma interagisce con l&#8217;operatore per creare un albero di<br />
domande e di nomi di animali. Ecco un esempio del suo funzionamento:</p>
<p>Stai pensando ad un animale? s<br />
E&#8217; un uccello? n<br />
Qual e&#8217; il nome dell&#8217;animale? cane<br />
Che domanda permette di distinguere tra un cane e un \<br />
uccello? Puo&#8217; volare<br />
Se l&#8217;animale fosse un cane quale sarebbe la risposta? n<br />
Stai pensando ad un animale? s<br />
Puo&#8217; volare? n<br />
E&#8217; un cane? n<br />
Qual e&#8217; il nome dell&#8217;animale? gatto<br />
Che domanda permette di distinguere tra un gatto e un\<br />
cane? Abbaia<br />
Se l&#8217;animale fosse un gatto quale sarebbe la risposta? n<br />
Stai pensando ad un animale? s<br />
Puo&#8217; volare? n<br />
Abbaia? s<br />
E&#8217; un cane? s<br />
Ho indovinato!<br />
Stai pensando ad un animale? n</p>
<p>Ecco un albero costruito da questo dialogo:</p>
<p>[i_tree5.png]</p>
<p>All&#8217;inizio di ogni round il programma parte alla radice dell&#8217;albero e<br />
pone la prima domanda. A seconda della risposta si muove a destra o a<br />
sinistra lungo l&#8217;albero e continua fino a raggiungere una foglia. A<br />
questo punto tira a indovinare: se la sua ipotesi non è corretta<br />
chiede il nome dell&#8217;animale pensato dall&#8217;operatore e una domanda per<br />
poterlo distinguere dall&#8217;animale trovato nel nodo foglia. Poi aggiunge<br />
il nuovo animale come nodo all&#8217;albero, assieme alla nuova domanda.</p>
<p>Ecco il codice:</p>
<p>def Animale():<br />
# parte con una lista composta di un solo elemento<br />
Radice = Albero(&#8220;uccello&#8221;)<br />
# continua finche&#8217; l&#8217;operatore non abbandona<br />
while 1:<br />
print<br />
if not RispostaAffermativa(&#8220;Stai pensando ad un \<br />
animale? &#8220;): break<br />
# percorre l&#8217;albero<br />
SottoAlbero = Radice<br />
while SottoAlbero.RamoSinistro() != None:<br />
Messaggio = SottoAlbero.OttieniContenuto() + &#8220;? &#8220;<br />
if RispostaAffermativa(Messaggio):<br />
SottoAlbero = SottoAlbero.RamoDestro()<br />
else:<br />
SottoAlbero = SottoAlbero.RamoSinistro()<br />
# prova a indovinare<br />
Ipotesi = SottoAlbero.OttieniContenuto()<br />
Messaggio = &#8220;E&#8217; un &#8221; + Ipotesi + &#8220;? &#8220;<br />
if RispostaAffermativa(Messaggio):<br />
print &#8220;Ho indovinato!&#8221;<br />
continue<br />
# ottiene nuove informazioni<br />
Messaggio = &#8220;Qual e&#8217; il nome dell&#8217;animale? &#8220;<br />
Animale  = raw_input(Messaggio)<br />
Messaggio  = &#8220;Che domanda permette di distinguere tra \<br />
un %s e un %s? &#8220;<br />
Domanda = raw_input(Messaggio % (Animale, Ipotesi))<br />
# aggiunge le nuove informazioni all&#8217;albero<br />
SottoAlbero.SettaContenuto(Domanda)<br />
Messaggio = &#8220;Se l&#8217;animale fosse un %s quale sarebbe la \<br />
risposta? &#8220;<br />
if RispostaAffermativa(Messaggio % Animale):<br />
SottoAlbero.SettaRamoSinistro(Albero(Ipotesi))<br />
SottoAlbero.SettaRamoDestro(Albero(Animale))<br />
else:<br />
SottoAlbero.SettaRamoSinistro(Albero(Animale))<br />
SottoAlbero.SettaRamoDestro(Albero(Ipotesi))</p>
<p>La funzione RispostaAffermativa è solo un&#8217;aiutante e serve a stampare<br />
un messaggio attendendo la risposta dall&#8217;operatore. Se la risposta<br />
inizia con s o S la funzione ritorna vero:</p>
<p>def RispostaAffermativa(Domanda):<br />
from string import lower<br />
Risposta = lower(raw_input(Domanda))<br />
return (Risposta[0] == &#8216;s&#8217;)</p>
<p>La condizione del ciclo esterno è 1 e questo significa che il ciclo<br />
verrà eseguito finchè non si incontra un&#8217;istruzione break, nel caso<br />
l&#8217;operatore non stia pensando ad un animale.</p>
<p>Il ciclo while interno serve a percorrere l&#8217;albero dall&#8217;alto in basso,<br />
guidato dalle risposte dell&#8217;operatore.</p>
<p>Se dopo aver raggiunto un nodo foglia ci troviamo a dover inserire un<br />
nuovo animale (con la rispettiva domanda per distinguerlo da quello<br />
rappresentato dal nodo foglia), viene effettuata una serie di<br />
operazioni:<br />
* {viene sostituito il contenuto del nodo foglia con la domanda<br />
appena inserita<br />
* {il nodo originale (che era il nodo foglia) col rispettivo<br />
contenuto viene aggiunto come figlio del nodo foglia<br />
* {il nodo rappresentato dal nuovo animale viene aggiunto come<br />
figlio allo stesso ex-nodo foglia .</p>
<p>Un &#8220;piccolo&#8221; problema con questo programma è che non appena termina la<br />
sua esecuzione tutto quello che gli abbiamo insegnato viene<br />
dimenticato&#8230;</p>
<p>Esercizio: pensa ai vari modi in cui potresti salvare l&#8217;albero su<br />
file e poi implementa quello che ritieni sia il più semplice.</p>
<p>20.8 Glossario</p>
<p>Albero binario<br />
albero in cui ogni nodo si riferisce a zero, uno o due nodi<br />
dipendenti.</p>
<p>Nodo radice<br />
nodo senza genitori in un albero.</p>
<p>Nodo foglia<br />
nodo senza figli in un albero.</p>
<p>Nodo genitore<br />
nodo che si riferisce ad un dato nodo.</p>
<p>Nodo figlio<br />
uno dei nodi cui si riferisce un altro nodo.</p>
<p>Nodi fratelli<br />
nodi che hanno uno stesso genitore.</p>
<p>Livello<br />
insieme dei nodi equidistanti dalla radice.</p>
<p>Operatore binario<br />
operatore che prende due operandi.</p>
<p>Sub-espressione<br />
espressione tra parentesi che agisce come singolo operando in<br />
una espressione più grande.</p>
<p>Preordine<br />
modo di attraversamento di un albero in cui si visita ogni nodo<br />
prima dei suoi figli.</p>
<p>Notazione prefissa<br />
notazione matematica dove ogni operatore compare prima dei suoi<br />
operandi.</p>
<p>Postordine<br />
modo di attraversamento di un albero in cui si visitano i figli<br />
di un nodo prima del nodo stesso.</p>
<p>Inordine<br />
modo di attraversamento di un albero in cui si visita prima il<br />
figlio a sinistra, poi la radice ed infine il figlio a destra.</p>
<p>Appendice A</p>
<p>Debug</p>
<p>In un programma possono manifestarsi diversi tipi di errore ed è<br />
sempre utile saperli distinguere per poterli rimuovere velocemente:<br />
* Gli errori di sintassi sono prodotti da Python durante la fase di<br />
traduzione del codice sorgente prima dell&#8217;esecuzione. Di solito<br />
indicano che c&#8217;è qualcosa di sbagliato nella sintassi del<br />
programma. Esempio: omettere i due punti alla fine dell&#8217;istruzione<br />
def porta al messaggio SyntaxError: invalid syntax.<br />
* Gli errori in esecuzione (runtime) sono errori che si verificano<br />
mentre il programma sta lavorando. La maggior parte delle volte i<br />
messaggi di errore indicano quale sia la causa dell&#8217;errore e quali<br />
le funzioni in esecuzione nel momento in cui si è verificato.<br />
Esempio: una ricorsione infinita causa un errore in esecuzione<br />
quando si raggiunge il massimo livello di ricorsione ammesso.<br />
* Gli errori di semantica sono i più difficili da rintracciare dato<br />
che il programma viene eseguito ma non porta a termine le<br />
operazioni corrette. Esempio: un&#8217;espressione può non essere<br />
valutata nell&#8217;ordine corretto tanto da portare ad un risultato<br />
inaspettato.</p>
<p>Il primo passo per rimuovere un errore è capire con che tipo di errore<br />
hai a che fare. Sebbene le sezioni seguenti siano organizzate in base<br />
al tipo di errore alcune tecniche sono applicabili a più di una<br />
situazione.</p>
<p>Errori di sintassi</p>
<p>Gli errori di sintassi sono facili da eliminare quando hai capito<br />
dov&#8217;è il problema. Sfortunatamente i messaggi di errore non sono<br />
sempre utili: i messaggi più comuni sono SyntaxError: invalid syntax<br />
(sintassi non valida) e SyntaxError: invalid token (token non valido)<br />
che di per sé non sono di grande aiuto.</p>
<p>Il messaggio fornisce indicazioni sulla riga di programma dove il<br />
problema si verifica, anche se questa riga in realtà è il punto in cui<br />
Python si è accorto dell&#8217;errore e non necessariamente dove questo si<br />
trova. Molto spesso l&#8217;errore è nella riga precedente rispetto a quella<br />
indicata dal messaggio.</p>
<p>Se stai scrivendo in modo incrementale il tuo programma è facile<br />
indicare immediatamente dove risiede il problema dato che deve<br />
trovarsi nelle ultime righe che hai aggiunto.</p>
<p>Se stai copiando il codice da un libro controlla accuratamente se<br />
l&#8217;originale è uguale a ciò che hai scritto. Controlla ogni carattere<br />
senza dimenticare che il codice riportato dal libro potrebbe anche<br />
essere sbagliato. Se vedi qualcosa che sembra un errore di sintassi,<br />
probabilmente lo è.</p>
<p>Ecco qualche sistema per evitare gli errori di sintassi più comuni:<br />
1. Controlla di non usare una parola riservata di Python come nome di<br />
variabile.<br />
2. Controlla di aver messo i due punti alla fine dell&#8217;intestazione di<br />
ogni istruzione composta, includendo le istruzioni for, while, if<br />
e def.<br />
3. Controlla che l&#8217;indentazione sia consistente: puoi indentare<br />
indifferentemente con spazi o tabulazioni ma è buona norma non<br />
usarli contemporaneamente. Ogni livello dovrebbe essere indentato<br />
della stessa quantità di spazi: l&#8217;uso di due spazi per livello è<br />
abbastanza consolidato all&#8217;interno della comunità Python.<br />
4. Controlla che i delimitatori delle stringhe siano appaiati<br />
correttamente.<br />
5. Se hai stringhe su righe multiple (delimitate da virgolette o<br />
apici tripli) controlla di averle terminate in modo appropriato.<br />
Una stringa non terminata può causare un errore invalid token alla<br />
fine del programma o può trattare il resto del programma come<br />
fosse parte della stringa. In casi particolari potrebbe non essere<br />
neanche mostrato un messaggio d&#8217;errore.<br />
6. Una parentesi non chiusa ((, { o [) costringe Python a cercare<br />
nelle righe successive credendole parte dell'istruzione corrente.<br />
Generalmente un errore di questo tipo viene individuato alla riga<br />
seguente.<br />
7. Controlla che non sia presente un = invece del == all'interno di<br />
una condizione.</p>
<p>Se malgrado i controlli non hai ottenuto risultati passa alla sezione<br />
seguente.</p>
<p>Non riesco a far funzionare il programma indipendentemente da ciò che<br />
faccio</p>
<p>Se il compilatore si ostina a dire che c'è un errore e tu non lo vedi<br />
probabilmente state guardando due diversi pezzi di codice. Controlla<br />
se il programma su cui stai lavorando è lo stesso che Python cerca di<br />
eseguire. Se non sei sicuro introduci un errore di sintassi proprio<br />
all'inizio ed eseguilo di nuovo: se il compilatore non vede il nuovo<br />
errore di sintassi con ogni probabilità state guardando due cose<br />
diverse.</p>
<p>Se questo accade, un approccio standard è quello di ricominciare da<br />
zero con un nuovo programma tipo "Hello, World!" e controllare che<br />
questo venga eseguito. Poi gradualmente aggiungi pezzi di codice fino<br />
ad arrivare a quello definitivo.</p>
<p>Errori in esecuzione</p>
<p>Quando il tuo programma è sintatticamente corretto Python lo può<br />
importare ed eseguire. Cosa potrebbe andare storto?</p>
<p>Il mio programma non fa assolutamente niente</p>
<p>Questo problema è comune quando il tuo codice consiste di classi e<br />
funzioni ma non c'è del codice che inizia l'esecuzione con una<br />
chiamata. Questo può essere un comportamento voluto quando il tuo<br />
codice deve essere importato in un altro modulo per fornire classi e<br />
funzioni.</p>
<p>Se questo non è intenzionale controlla di invocare una funzione per<br />
iniziare l'esecuzione o eseguila direttamente dal prompt interattivo.<br />
Vedi anche la sezione "Flusso di esecuzione" in seguito.</p>
<p>Il mio programma si blocca</p>
<p>Se il programma si ferma e sembra di essere bloccato senza fare nulla<br />
diciamo che è "in blocco". Spesso questo significa che il flusso del<br />
programma è all'interno di un ciclo o di una ricorsione infiniti.<br />
* Se i tuoi sospetti cadono su un ciclo in particolare aggiungi una<br />
istruzione print immediatamente prima di entrare nel ciclo<br />
("entrata nel ciclo") ed una subito dopo l'uscita ("uscita dal<br />
ciclo"). Esegui il programma: se leggi il primo messaggio ma non<br />
il secondo sei in un loop infinito. Vai alla sezione "Ciclo<br />
infinito" descritta in seguito.<br />
* La maggior parte delle volte una ricorsione infinita permetterà al<br />
programma di lavorare per un po' e poi produrrà un messaggio<br />
d'errore "RuntimeError: Maximum recursion depth exceeded". Vedi a<br />
riguardo la sezione "Ricorsione infinita". Se non ottieni questo<br />
tipo di errore ma sospetti che ci sia qualche problema con un<br />
metodo o una funzione ricorsivi puoi sempre usare le tecniche<br />
descritte nella sezione "Ricorsione infinita".<br />
* Se questi passi non hanno dato risultati inizia a controllare<br />
altri cicli e funzioni ricorsive.<br />
* Se ancora non ottieni risultati è possibile che ti stia sfuggendo<br />
come si evolve il flusso del programma. Vedi la sezione "Flusso di<br />
esecuzione" in seguito.</p>
<p>Ciclo infinito</p>
<p>Quando hai a che fare con cicli sospetti puoi sempre aggiungere alla<br />
fine del corpo del ciclo un'istruzione print per stampare i valori<br />
delle variabili usate nella condizione ed il valore della condizione.</p>
<p>Per esempio:</p>
<p>while x &gt; 0 and y &lt; 0 :<br />
# fai qualcosa con x<br />
# fai qualcosa con y<br />
print  "x: ", x<br />
print  "y: ", y<br />
print  "condizione: ", (x &gt; 0 and y &lt; 0)</p>
<p>Quando esegui il programma sono stampate tre righe ogni volta che<br />
viene reiterato il ciclo. La condizione d'uscita sarà falsa solo<br />
nell'ultima esecuzione. Se il ciclo continua ad essere eseguito potrai<br />
controllare i valori di x e y e forse potrai capire perché non vengono<br />
aggiornati correttamente.</p>
<p>Ricorsione infinita</p>
<p>La maggior parte delle volte una ricorsione infinita porterà<br />
all'esaurimento della memoria disponibile: il programma funzionerà<br />
finché ci sarà memoria disponibile, poi verrà mostrato un messaggio<br />
d'errore Maximum recursion depth exceeded.</p>
<p>Se sospetti che una funzione o un metodo stiano causando una<br />
ricorsione infinita, inizia a controllare il caso base: deve sempre<br />
essere presente una condizione all'interno della funzione che possa<br />
ritornare senza effettuare un'ulteriore chiamata alla funzione stessa.<br />
Se il caso base non è presente è il caso di ripensare l'algoritmo.</p>
<p>Se è presente il caso base ma sembra che il flusso di programma non lo<br />
raggiunga mai aggiungi un'istruzione print all'inizio della funzione o<br />
metodo per stamparne i parametri. Se durante l'esecuzione i parametri<br />
non si muovono verso il caso base probabilmente significa che la<br />
ricorsione non funziona.</p>
<p>Flusso di esecuzione</p>
<p>Se non sei sicuro che il flusso di esecuzione si stia muovendo lungo<br />
il programma aggiungi un'istruzione print all'inizio di ogni funzione<br />
per stampare un messaggio del tipo "entro nella funzione xxx" dove xxx<br />
è il nome della funzione. Quando il programma è in esecuzione avrai<br />
una traccia del suo flusso.</p>
<p>Quando eseguo il programma ottengo un'eccezione</p>
<p>Se qualcosa va storto durante l'esecuzione Python stampa un messaggio<br />
che include il nome dell'eccezione, la linea di programma dove si è<br />
verificato il problema e una traccia.</p>
<p>La traccia identifica la funzione che si stava eseguendo al momento<br />
dell'errore, la funzione che l'aveva chiamata, la funzione che aveva<br />
chiamato quest'ultima e così a ritroso fino ad arrivare al livello<br />
superiore. Mostra cioè il cammino che ha portato all'interno della<br />
funzione malfunzionante. Come ulteriori informazioni sono anche<br />
indicate le linee di programma dove ciascuna funzione viene chiamata.</p>
<p>Il primo passo è quello di esaminare il posto nel programma dove<br />
l'errore si è verificato e cercare di capire cos'è successo. Questi<br />
sono i più comuni errori in esecuzione:</p>
<p>NameError<br />
stai provando ad usare una variabile che non esiste in questo<br />
ambiente. Ricorda che le variabili sono locali e non puoi<br />
riferirti ad esse al di fuori della funzione dove sono state<br />
definite.</p>
<p>TypeError<br />
ci sono parecchie possibili cause:</p>
<p>+ Stai cercando di usare un valore in maniera impropria, per<br />
esempio riferendoti agli elementi di una lista usando un<br />
indice che non è intero.<br />
+ C'è una discrepanza tra gli elementi di una stringa di<br />
formato e i valori passati per la conversione. Questo può<br />
succedere sia se il numero degli elementi è diverso sia se il<br />
tipo richiede una conversione non valida.<br />
+ Stai passando un numero di argomenti errato ad una funzione o<br />
a un metodo. Per i metodi controlla la definizione del metodo<br />
e che il primo parametro sia self. Poi guarda all'invocazione<br />
del metodo; controlla se stai invocando il metodo su un<br />
oggetto del tipo giusto e se stai fornendo in modo corretto<br />
gli altri argomenti.</p>
<p>KeyError<br />
stai cercando di accedere ad un elemento di un dizionario<br />
usando una chiave non conosciuta dal dizionario.</p>
<p>AttributeError<br />
stai provando ad accedere ad un attributo o metodo che non<br />
esiste.</p>
<p>IndexError<br />
stai usando un indice troppo grande per accedere ad una lista,<br />
ad una stringa o ad una tupla. Prima della posizione<br />
dell'errore aggiungi un'istruzione print per mostrare il valore<br />
dell'indice e la lunghezza dell'array. L'array è della<br />
lunghezza corretta? L'indice ha il valore corretto?</p>
<p>Ho aggiunto così tante istruzioni print da essere sommerso dalle stampe</p>
<p>Uno dei problemi con l'uso dell'istruzione print durante il debug è<br />
che puoi rimanere letteralmente sommerso da una valanga di messaggi.<br />
Ci sono due modi per procedere: semplificare le stampe o semplificare<br />
il programma.</p>
<p>Per semplificare le stampe puoi rimuovere o commentare le istruzioni<br />
print che non servono più, combinarle o formattare la stampa per<br />
ottenere una forma più semplice da leggere.</p>
<p>Per semplificare il programma ci sono parecchie cose che puoi fare.<br />
Prima di tutto riduci il programma per farlo lavorare su un piccolo<br />
insieme di dati. Se stai ordinando un array usa un array piccolo. Se<br />
il programma accetta un inserimento di dati dall'operatore cerca il<br />
più piccolo pacchetto di dati che genera il problema.</p>
<p>In secondo luogo ripulisci il programma. Rimuovi il codice morto e<br />
riorganizza il programma per renderlo il più leggibile possibile. Se<br />
sospetti che il problema risieda in una parte profondamente annidata<br />
del codice prova a riscrivere quella parte in modo più semplice. Se<br />
sospetti di una funzione complessa prova a dividerla in funzioni più<br />
piccole da testare separatamente.</p>
<p>Spesso il solo processo di trovare un insieme di dati che causa il<br />
problema ti porta a scoprirne la causa. Se trovi che il programma<br />
funziona in una situazione ma non in un'altra questo ti dà un notevole<br />
indizio di cosa stia succedendo.</p>
<p>Riscrivere un pezzo di codice può aiutarti a trovare piccoli bug<br />
difficili da individuare soprattutto se fai un cambiamento nel codice<br />
che credi non vada ad influire nel resto del programma e invece lo fa.</p>
<p>Errori di semantica</p>
<p>Gli errori di semantica sono i più difficili da scovare perché il<br />
compilatore e l'interprete non forniscono informazioni riguardo che<br />
cosa ci sia di sbagliato nel tuo programma. L'unica cosa che sai è ciò<br />
che il programma dovrebbe fare e che questo non è ciò che il programma<br />
effettivamente fa.</p>
<p>Il primo passo è quello di comprendere la connessione tra il codice ed<br />
il comportamento che stai osservando, con la conseguente creazione di<br />
ipotesi per giustificare ciò che vedi. Una delle cose che rendono il<br />
tutto così difficile è il fatto che il computer sia così veloce.</p>
<p>Spesso ti capiterà di desiderare di poter rallentare il programma fino<br />
ad una velocità più "umana" ed effettivamente questo è ciò che fanno<br />
alcuni programmi appositamente studiati per il debug. Il tempo<br />
trascorso a inserire qualche istruzione print ben piazzata è comunque<br />
breve se confrontato con tutta la procedura che occorre mettere in<br />
atto per configurare opportunamente il debugger, per inserire ed<br />
eliminare i punti di interruzione nel programma e per verificare passo<br />
per passo tutta l'esecuzione del programma.</p>
<p>Il mio programma non funziona</p>
<p>Dovresti farti qualche domanda:<br />
* C'è qualcosa che il programma dovrebbe fare e sembra non venga<br />
fatto? Trova la sezione del codice incaricato di eseguire quella<br />
particolare funzione e verifica che questo venga eseguito quando<br />
ci si aspetta.<br />
* Succede qualcosa che non dovrebbe accadere? Trova il pezzo di<br />
codice che esegue quella particolare funzione e controlla se esso<br />
viene eseguito quando non dovrebbe.<br />
* C'è una sezione del codice che non fa quello che ci si aspetta?<br />
Controlla questo codice verificando di aver ben capito cosa fa,<br />
soprattutto se fa uso di altri moduli Python. Leggi la<br />
documentazione per le funzioni che invochi. Prova a testarle una<br />
ad una creando piccoli esempi e controllando i risultati.</p>
<p>Per poter programmare devi avere un modello mentale di come il<br />
programma lavora: se ottieni un programma che non si comporta come<br />
desideri spesso la colpa non è nel programma in sé ma nel tuo modello<br />
mentale.</p>
<p>Il modo migliore per correggere il tuo modello mentale è quello di<br />
spezzare i suoi componenti (di solito le funzioni ed i metodi) e<br />
testare ogni componente in modo indipendente. Quando hai trovato la<br />
discrepanza tra il tuo modello e la realtà puoi risolvere il problema.</p>
<p>Dovresti costruire e testare i componenti man mano che sviluppi il<br />
programma così ti troveresti, in caso di problemi, soltanto con<br />
piccole parti di codice da controllare.</p>
<p>Ho un'espressione piuttosto lunga che non fa ciò che dovrebbe</p>
<p>Scrivere espressioni complesse va bene finché queste sono leggibili ma<br />
ricorda che possono rendere problematico il debug. È sempre una buona<br />
norma spezzare un'espressione complessa in una serie di assegnazioni<br />
anche usando variabili temporanee.</p>
<p>Per esempio:</p>
<p>self.Mano[i].AggiungeCarta \<br />
(self.Mano[self.TrovaVicino(i)].ProssimaCarta())</p>
<p>Può essere riscritta come:</p>
<p>Vicino = self.TrovaVicino(i)<br />
Prossima = self.Mano[Vicino].ProssimaCarta()<br />
self.Mano[i].AggiungeCarta (Prossima)</p>
<p>La versione esplicita è più semplice da leggere poiché i nomi delle<br />
variabili forniscono una documentazione aggiuntiva ed è anche più<br />
semplice da controllare in fase di debug dato che puoi stampare i<br />
valori delle variabili intermedie.</p>
<p>Un altro problema collegato alle espressioni complesse è che talvolta<br />
l&#8217;ordine di valutazione può non essere ciò che ci si aspetta. Per<br />
tradurre l&#8217;espressione</p>
<p>x<br />
_________________________________________________________________</p>
<p>2 pi</p>
<p>in Python devi scrivere:</p>
<p>y = x / 2 * math.pi;</p>
<p>Questo non è corretto perché moltiplicazione e divisione hanno la<br />
stessa precedenza e sono valutate da sinistra a destra. Così questa<br />
espressione viene valutata x pi/2.</p>
<p>Un buon sistema per effettuare il debug delle espressioni è aggiungere<br />
le parentesi per rendere esplicita la valutazione:</p>
<p>y = x/(2*math.pi);</p>
<p>Quando non sei sicuro dell&#8217;ordine di valutazione usa le parentesi. Non<br />
solo il programma sarà corretto ma sarà anche più leggibile da parte<br />
di chi non ha memorizzato le regole di precedenza.</p>
<p>Ho una funzione o un metodo che non restituisce ciò che mi aspetto</p>
<p>Se hai un&#8217;istruzione return con un&#8217;espressione complessa non hai modo<br />
di stampare il valore restituito da una funzione. Anche stavolta è il<br />
caso di usare una variabile temporanea. Invece di:</p>
<p>return self.Mano[i].TrisRimossi()</p>
<p>puoi scrivere:</p>
<p>Conteggio = self.Mano[i].TrisRimossi()<br />
return Conteggio</p>
<p>Ora hai modo di stampare il valore di Conteggio prima di ritornare<br />
dalla funzione.</p>
<p>Sono bloccato e ho bisogno di aiuto!</p>
<p>Prova innanzitutto a staccarti dal computer per qualche minuto. I<br />
computer emettono onde elettromagnetiche che influenzano il cervello<br />
causando un bel po&#8217; di effetti spiacevoli:<br />
* Frustrazione e/o rabbia.<br />
* Superstizione (&#8220;il computer mi odia&#8221;) e pensieri poco ortodossi<br />
(&#8220;il programma funzionava quando indossavo il mio cappello al<br />
contrario&#8221;).<br />
* Programmazione &#8220;casuale&#8221; (il tentativo poco probabile di scrivere<br />
il programma corretto scrivendo un gran numero di programmi<br />
assolutamente casuali)</p>
<p>Se credi di soffrire di qualcuno di questi sintomi alzati e vatti a<br />
fare quattro passi. Quando ti sei calmato torna a pensare al<br />
programma. Cosa sta facendo? Quali sono le possibili cause? Quand&#8217;è<br />
stata l&#8217;ultima volta che si è comportato a dovere? Cos&#8217;è stato fatto<br />
in seguito?</p>
<p>Talvolta è necessario parecchio tempo per trovare un bug ed è molto<br />
più efficace una ricerca fatta dopo aver lasciato sgombra la mente per<br />
qualche tempo. Alcuni tra i posti migliori per trovare i bug (senza<br />
bisogno di un computer!) sono i treni, le docce ed il letto, appena<br />
prima di addormentarsi.</p>
<p>Se il problema si verifica in ufficio di venerdì pomeriggio fai finta<br />
di lavorarci, ma pensa ad altro: non fare modifiche che potresti<br />
rimpiangere il lunedì mattina&#8230;</p>
<p>Niente scherzi: ho veramente bisogno di aiuto</p>
<p>Capita. Anche i migliori programmatori a volte rimangono bloccati:<br />
qualche volta lavori su un programma così a lungo che non riesci più a<br />
vedere l&#8217;errore. due occhi &#8220;freschi&#8221; sono ciò che ci vuole.</p>
<p>Prima di tirare dentro qualcun altro nella caccia al bug devi essere<br />
sicuro di aver esaurito ogni possibile tecnica qui descritta. Il tuo<br />
programma dovrebbe essere il più semplice possibile e dovresti<br />
lavorare sul più piccolo insieme di dati che causa il problema.<br />
Dovresti avere una serie di istruzioni print nei posti appropriati con<br />
una stampa comprensibile dei rispettivi valori di controllo. Dovresti<br />
aver capito il problema tanto da poterlo esprimere in modo conciso.</p>
<p>Quando chiedi l&#8217;aiuto di qualcuno ricorda di dare tutte le<br />
informazioni di cui può aver bisogno:<br />
* Se c&#8217;è un messaggio d&#8217;errore che cosa e che parte del programma<br />
indica?<br />
* Cos&#8217;è stata l&#8217;ultima cosa che hai fatto prima che si verificasse<br />
l&#8217;errore? Quali sono le ultime righe di codice che hai scritto o<br />
quali sono i nuovi dati che fanno fallire il test?<br />
* Cosa hai già provato e che ipotesi hai già escluso con le tue<br />
prove?</p>
<p>Quando hai trovato il bug fermati un secondo e cerca di capire come<br />
avresti potuto trovarlo più in fretta. La prossima volta che<br />
riscontrerai un comportamento simile, questo sarà molto utile.</p>
<p>Appendice B</p>
<p>Creazione di un nuovo tipo di dato</p>
<p>La programmazione orientata agli oggetti permette al programmatore di<br />
creare nuovi tipi di dato che si comportano come quelli predefiniti.<br />
Esploreremo questa capacità costruendo una classe Frazione che possa<br />
lavorare come i tipi di dato numerico predefiniti (intero, intero<br />
lungo e virgola mobile).</p>
<p>Le frazioni, conosciute anche come numeri razionali, sono numeri che<br />
si possono esprimere come rapporto tra numeri interi, come nel caso di<br />
5/6. Il numero superiore si chiama numeratore, quello inferiore<br />
denominatore.</p>
<p>Iniziamo con una definizione della classe Frazione con un metodo di<br />
inizializzazione che fornisce un numeratore ed un denominatore interi.</p>
<p>class Frazione:<br />
def __init__(self, Numeratore, Denominatore=1):<br />
self.Numeratore = Numeratore<br />
self.Denominatore = Denominatore</p>
<p>Il denominatore è opzionale: se la frazione è creata (istanziata) con<br />
un solo parametro rappresenta un intero così che se il numeratore è n<br />
costruiremo la frazione n/1.</p>
<p>Il prossimo passo è quello di scrivere il metodo __str__ per stampare<br />
le frazioni in un modo che sia comprensibile. La forma<br />
&#8220;numeratore/denominatore&#8221; è probabilmente quella più &#8220;naturale&#8221;:</p>
<p>class Frazione:<br />
&#8230;<br />
def __str__(self):<br />
return &#8220;%d/%d&#8221; % (self.Numeratore, self.Denominatore)</p>
<p>Per testare ciò che abbiamo fatto finora scriviamo tutto in un file<br />
chiamato frazione.py (o qualcosa di simile; l&#8217;importante è che per te<br />
abbia un nome che ti permetta di rintracciarlo in seguito) e lo<br />
importiamo nell&#8217;interprete Python. Poi passiamo a creare un oggetto<br />
Frazione e a stamparlo:</p>
<p>&gt;&gt;&gt; from Frazione import Frazione<br />
&gt;&gt;&gt; f = Frazione(5,6)<br />
&gt;&gt;&gt; print &#8220;La frazione e&#8217;&#8221;, f<br />
La frazione e&#8217; 5/6</p>
<p>Come abbiamo già visto il comando print invoca il metodo __str__<br />
implicitamente.</p>
<p>Moltiplicazione di frazioni</p>
<p>Ci interessa poter applicare le consuete operazioni matematiche a<br />
operandi di tipo Frazione. Per farlo procediamo con la ridefinizione<br />
degli operatori matematici quali l&#8217;addizione, la sottrazione, la<br />
moltiplicazione e la divisione.</p>
<p>Iniziamo dalla moltiplicazione perché è la più semplice da<br />
implementare. Il risultato della moltiplicazione di due frazioni è una<br />
frazione che ha come numeratore il prodotto dei due numeratori, e come<br />
denominatore il prodotto dei denominatori. __mul__ è il nome usato da<br />
Python per indicare l&#8217;operatore *:</p>
<p>class Frazione:<br />
&#8230;<br />
def __mul__(self, Altro):<br />
return Frazione(self.Numeratore * Altro.Numeratore,<br />
self.Denominatore * Altro.Denominatore)</p>
<p>Possiamo testare subito questo metodo calcolando il prodotto di due<br />
frazioni:</p>
<p>&gt;&gt;&gt; print Frazione(5,6) * Frazione(3,4)<br />
15/24</p>
<p>Funziona, ma possiamo fare di meglio. Possiamo infatti estendere il<br />
metodo per gestire la moltiplicazione di una frazione per un intero,<br />
usando la funzione type per controllare se Altro è un intero. In<br />
questo caso prima di procedere con la moltiplicazione lo si convertirà<br />
in frazione:</p>
<p>class Frazione:<br />
&#8230;<br />
def __mul__(self, Altro):<br />
if type(Altro) == type(5):<br />
Altro = Frazione(Altro)<br />
return Frazione(self.Numeratore * Altro.Numeratore,<br />
self.Denominatore * Altro.Denominatore)</p>
<p>La moltiplicazione tra frazioni e interi ora funziona, ma solo se la<br />
frazione compare alla sinistra dell&#8217;operatore:</p>
<p>&gt;&gt;&gt; print Frazione(5,6) * 4<br />
20/6<br />
&gt;&gt;&gt; print 4 * Frazione(5,6)<br />
TypeError: unsupported operand type(s) for *: &#8216;int&#8217; and &#8216;instance</p>
<p>Per valutare un operatore binario come la moltiplicazione Python<br />
controlla l&#8217;operando di sinistra per vedere se questo fornisce un<br />
metodo __mul__ che supporta il tipo del secondo operando. Nel nostro<br />
caso l&#8217;operatore moltiplicazione predefinito per gli interi non<br />
supporta le frazioni (com&#8217;è giusto, dato che abbiamo appena inventato<br />
noi la classe Frazione).</p>
<p>Se il controllo non ha successo Python passa a controllare l&#8217;operando<br />
di destra per vedere se è stato definito un metodo __rmul__ che<br />
supporta il tipo di dato dell&#8217;operatore di sinistra. Visto che non<br />
abbiamo ancora scritto __rmul__ il controllo fallisce e viene mostrato<br />
il messaggio di errore.</p>
<p>Esiste comunque un metodo molto semplice per scrivere __rmul__:</p>
<p>class Frazione:<br />
&#8230;<br />
__rmul__ = __mul__</p>
<p>Con questa assegnazione diciamo che il metodo __rmul__ è lo stesso di<br />
__mul__, così che per valutare 4 * Fraction(5,6) Python invoca<br />
__rmul__ sull&#8217;oggetto Frazione e passa 4 come parametro:</p>
<p>&gt;&gt;&gt; print 4 * Frazione(5,6)<br />
20/6</p>
<p>Dato che __rmul__ è lo stesso di __mul__ e che quest&#8217;ultimo accetta<br />
parametri interi è tutto a posto.</p>
<p>Addizione tra frazioni</p>
<p>L&#8217;addizione è più complessa della moltiplicazione ma non troppo: la<br />
somma di a/b e c/d è infatti la frazione (a*d+c*b)/b*d.</p>
<p>Usando il codice della moltiplicazione come modello possiamo scrivere<br />
__add__ e __radd__:</p>
<p>class Frazione:<br />
&#8230;<br />
def __add__(self, Altro):<br />
if type(Altro) == type(5):<br />
Altro = Frazione(Altro)<br />
return Fraction(self.Numeratore   * Altro.Denominatore +<br />
self.Denominatore * Altro.Numeratore,<br />
self.Denominatore * Altro.Denominatore)<br />
__radd__ = __add__</p>
<p>Possiamo testare questi metodi con frazioni e interi:</p>
<p>&gt;&gt;&gt; print Frazione(5,6) + Frazione(5,6)<br />
60/36<br />
&gt;&gt;&gt; print Frazione(5,6) + 3<br />
23/6<br />
&gt;&gt;&gt; print 2 + Frazione(5,6)<br />
17/6</p>
<p>I primi due esempi invocano __add__; l&#8217;ultimo __radd__.</p>
<p>Algoritmo di Euclide</p>
<p>Nell&#8217;esempio precedente abbiamo calcolato la somma 5/6 + 5/6 e<br />
ottenuto 60/36. Il risultato è corretto ma quella ottenuta non è la<br />
sua migliore rappresentazione. Per ridurre la frazione ai suoi termini<br />
più semplici dobbiamo dividere il numeratore ed il numeratore per il<br />
loro massimo comune divisore (MCD) che è 12. Il risultato diventa<br />
quindi 5/3.</p>
<p>In generale quando creiamo e gestiamo un oggetto Frazione dovremmo<br />
sempre dividere numeratore e denominatore per il loro MCD. Nel caso di<br />
una frazione già ridotta il MCD è 1.</p>
<p>Euclide di Alessandria (circa 325&#8211;265 A.C.) inventò un algoritmo per<br />
calcolare il massimo comune divisore tra due numeri interi m e n:</p>
<p>Se n divide perfettamente m allora il MCD è n. In caso contrario il<br />
MCD è il MCD tra n ed il resto della divisione di m diviso per n.</p>
<p>Questa definizione ricorsiva può essere espressa in modo conciso con<br />
una funzione:</p>
<p>def MCD(m, n):<br />
if m % n == 0:<br />
return n<br />
else:<br />
return MCD(n, m%n)</p>
<p>Nella prima riga del corpo usiamo l&#8217;operatore modulo per controllare<br />
la divisibilità. Nell&#8217;ultima riga lo usiamo per calcolare il resto<br />
della divisione.</p>
<p>Dato che tutte le operazioni che abbiamo scritto finora creano un<br />
nuovo oggetto Frazione come risultato potremmo inserire la riduzione<br />
nel metodo di inizializzazione:</p>
<p>class Frazione:<br />
def __init__(self, Numeratore, Denominatore=1):<br />
mcd = MCD(numeratore, Denominatore)<br />
self.Numeratore   = Numeratore / mcd<br />
self.Denominatore = Denominatore / mcd</p>
<p>Quando creiamo una nuova Frazione questa sarà immediatamente ridotta<br />
alla sua forma più semplice:</p>
<p>&gt;&gt;&gt; Frazione(100,-36)<br />
-25/9</p>
<p>Una bella caratteristica di MCD è che se la frazione è negativa il<br />
segno meno è sempre spostato automaticamente al numeratore.</p>
<p>Confronto di frazioni</p>
<p>Supponiamo di dover confrontare due oggetti di tipo Frazione, a e b<br />
valutando a == b. L&#8217;implementazione standard di == ritorna vero solo<br />
se a e b sono lo stesso oggetto, effettuando un confronto debole.</p>
<p>Nel nostro caso vogliamo probabilmente ritornare vero se a e b hanno<br />
lo stesso valore e cioè fare un confronto forte. Ne abbiamo già<br />
parlato nella sezione 12.4.</p>
<p>Dobbiamo quindi insegnare alle frazioni come confrontarsi tra di loro.<br />
Come abbiamo visto nella sezione 15.4, possiamo ridefinire tutti gli<br />
operatori di confronto in una volta sola fornendo un nuovo metodo<br />
__cmp__.</p>
<p>Per convenzione il metodo __cmp__ ritorna un numero negativo se self è<br />
minore di Altro, zero se sono uguali e un numero positivo se self è<br />
più grande di Altro.</p>
<p>Il modo più semplice per confrontare due frazioni è la moltiplicazione<br />
incrociata: se a/b &gt; c/d allora ad &gt; bc. Con questo in mente ecco<br />
quindi il codice per __cmp__:</p>
<p>class Frazione:<br />
&#8230;<br />
def __cmp__(self, Altro):<br />
Differenza = (self.Numeratore  * Altro.Denominatore -<br />
Altro.Numeratore * self.Denominatore)<br />
return Differenza</p>
<p>Se self è più grande di Altro allora Differenza è positiva. Se Altro è<br />
maggiore allora Differenza è negativa. Se sono uguali Differenza è<br />
zero.</p>
<p>Proseguiamo</p>
<p>Logicamente non abbiamo ancora finito. Dobbiamo ancora implementare la<br />
sottrazione ridefinendo __sub__ e la divisione con il corrispondente<br />
metodo __div__.</p>
<p>Un modo per gestire queste operazioni è quello di implementare la<br />
negazione ridefinendo __neg__ e l&#8217;inversione con __invert__: possiamo<br />
infatti sottrarre sommando al primo operando la negazione del secondo,<br />
e dividere moltiplicando il primo operando per l&#8217;inverso del secondo.<br />
Poi dobbiamo fornire __rsub__ e __rdiv__.</p>
<p>Purtroppo non possiamo usare la scorciatoia già vista nel caso di<br />
addizione e moltiplicazione dato che sottrazione e divisione non sono<br />
commutative. Non possiamo semplicemente assegnare __rsub__ e __rdiv__<br />
a lle corrispondenti __sub__ e __div__, dato che in queste operazioni<br />
l&#8217;ordine degli operandi fa la differenza&#8230;</p>
<p>Per gestire la negazione unaria, che non è altro che l&#8217;uso del segno<br />
meno con un singolo operando (da qui il termine &#8220;unaria&#8221; usato nella<br />
definizione), sarà necessario ridefinire il metodo __neg__.</p>
<p>Potremmo anche calcolare le potenze ridefinendo __pow__ ma<br />
l&#8217;implementazione in questo caso è un po&#8217; complessa: se l&#8217;esponente<br />
non è un intero, infatti, può non essere possibile rappresentare il<br />
risultato come Frazione. Per fare un esempio, Frazione(2) **<br />
Frazione(1,2) non è nient&#8217;altro che la radice di 2 che non è un numero<br />
razionale e quindi non può essere rappresentato come frazione. Questo<br />
è il motivo per cui non è così facile scrivere una versione generale<br />
di __pow__.</p>
<p>C&#8217;è un&#8217;altra estensione della classe Frazione che potrebbe rivelarsi<br />
utile: finora siamo partiti dal presupposto che numeratore e<br />
denominatore sono interi, ma nulla ci vieta di usare interi lunghi.</p>
<p>Esercizio: completa l&#8217;implementazione della classe Frazione per<br />
gestire sottrazione, divisione ed elevamento a potenza, con interi<br />
lunghi al numeratore e denominatore.</p>
<p>Glossario</p>
<p>Massimo comune divisore(MCD)<br />
il più grande numero positivo intero che divide senza resto sia<br />
il numeratore che il denominatore di una frazione.</p>
<p>Riduzione<br />
trasformazione di una frazione nella sua forma più semplice,<br />
grazie alla divisione di numeratore e denominatore per il loro<br />
MCD.</p>
<p>Negazione unaria<br />
operazione che calcola un inverso additivo, solitamente<br />
indicato da un segno meno anteposto ad un numero. È chiamata<br />
&#8220;unaria&#8221; perché agisce su un unico operando, a differenza di<br />
altri operatori, quali la sottrazione &#8220;binaria&#8221;, che agiscono<br />
su due operandi.</p>
<p>Appendice C</p>
<p>Listati dei programmi</p>
<p>class Punto</p>
<p>class Punto:<br />
def __init__(self, x=0, y=0):<br />
self.x = x<br />
self.y = y<br />
def __str__(self):<br />
return &#8216;(&#8216; + str(self.x) + &#8216;, &#8216; + str(self.y) + &#8216;)&#8217;<br />
def __add__(self, AltroPunto):<br />
return Punto(self.x + AltroPunto.x, self.y + AltroPunto.y)<br />
def __sub__(self, AltroPunto):<br />
return Punto(self.x &#8211; AltroPunto.x, self.y &#8211; AltroPunto.y)<br />
def __mul__(self, AltroPunto):<br />
return self.x * AltroPunto.x + self.y * AltroPunto.y<br />
def __rmul__(self, AltroPunto):<br />
return Punto(AltroPunto * self.x,  AltroPunto * self.y)<br />
def reverse(self):<br />
self.x , self.y = self.y, self.x<br />
def DirittoERovescio(Stringa):<br />
import copy<br />
Rovescio = copy.copy(Stringa)<br />
Rovescio.reverse()<br />
print str(Stringa) + str(Rovescio)</p>
<p>class Tempo</p>
<p># &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
# Versione funzionale<br />
# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
#   t = Tempo(3,14)<br />
#   s = Tempo(8,12,15)<br />
#   print SommaTempi(s, t)<br />
def ConverteInSecondi(Orario):<br />
Minuti = Orario.Ore * 60 + Orario.Minuti<br />
Secondi = Minuti * 60 + Orario.Secondi<br />
return Secondi<br />
def ConverteInTempo(Secondi):<br />
Orario = Tempo()<br />
Orario.Ore = Secondi / 3600<br />
Secondi = Secondi &#8211; Orario.Ore * 3600<br />
Orario.Minuti = Secondi / 60<br />
Secondi = Secondi &#8211; Orario.Minuti * 60<br />
Orario.Secondi = Secondi<br />
return Orario<br />
def SommaTempi(Tempo1, Tempo2):<br />
Secondi = ConverteInSecondi(Tempo1) + ConverteInSecondi(Tempo2)<br />
return ConverteInTempo(Secondi)<br />
# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
# Versione a oggetti<br />
# &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
# con modifica di uno degli oggetti:<br />
#   t = Tempo(3,14)<br />
#   s = Tempo(8,12,15)<br />
#   t.AggiungiTempo(s)<br />
#   print t<br />
# in alternativa, senza modificare t:<br />
#   a = Tempo()<br />
#   a.SommaTempi(t, s)<br />
#   print a<br />
class Tempo:<br />
def __init__(self, Ore=0, Minuti=0, Secondi=0):<br />
self.Ore = Ore<br />
self.Minuti = Minuti<br />
self.Secondi = Secondi<br />
def __str__(self):<br />
return str(self.Ore) + &#8220;:&#8221; + str(self.Minuti) + &#8220;:&#8221; + \<br />
str(self.Secondi)<br />
def Incremento(self, Secondi):<br />
Secondi = Secondi + self.Secondi + self.Minuti*60 + \<br />
self.Ore*3600<br />
self.Ore = Secondi / 3600<br />
Secondi = Secondi % 3600<br />
self.Minuti = Secondi / 60<br />
Secondi = Secondi % 60<br />
self.Secondi = Secondi<br />
def ConverteInSecondi(self):<br />
Minuti = self.Ore * 60 + self.Minuti<br />
Secondi = Minuti * 60 + self.Secondi<br />
return Secondi<br />
def ConverteInTempo(self, Secondi):<br />
self.Ore = Secondi / 3600<br />
Secondi = Secondi &#8211; self.Ore * 3600<br />
self.Minuti = Secondi / 60<br />
Secondi = Secondi &#8211; self.Minuti * 60<br />
self.Secondi = Secondi<br />
def AggiungiTempo(self, Tempo2):<br />
Secondi = self.ConverteInSecondi() + \<br />
Tempo2.ConverteInSecondi()<br />
self.ConverteInTempo(Secondi)  # l&#8217;oggetto self e&#8217; stato<br />
# modificato!<br />
def SommaTempi(self, Tempo1, Tempo2):<br />
Secondi = Tempo1.ConverteInSecondi() + \<br />
Tempo2.ConverteInSecondi()<br />
self.ConverteInTempo(Secondi)</p>
<p>Carte, mazzi e giochi</p>
<p>import random<br />
class Carta:<br />
ListaSemi = ["Fiori", "Quadri", "Cuori", "Picche"]<br />
ListaRanghi = ["impossibile", "Asso", "2", "3", "4", "5", "6",<br />
"7", "8", "9", "10", "Jack", "Regina", "Re"]<br />
def __init__(self, Seme=0, Rango=0):<br />
self.Seme = Seme<br />
self.Rango = Rango<br />
def __str__(self):<br />
return (self.ListaRanghi[self.Rango] + &#8221; di &#8221; + \<br />
self.ListaSemi[self.Seme])<br />
def __cmp__(self, Altro):<br />
# controlla il seme<br />
if self.Seme &gt; Altro.Seme: return 1<br />
if self.Seme &lt; Altro.Seme: return -1<br />
# se i semi sono uguali controlla il rango<br />
if self.Rango &gt; Altro.Rango: return 1<br />
if self.Rango &lt; Altro.Rango: return -1<br />
# se anche i ranghi sono uguali le carte sono uguali!<br />
return 0<br />
class Mazzo:<br />
def __init__(self):<br />
self.Carte = []<br />
for Seme in range(4):<br />
for Rango in range(1, 14):<br />
self.Carte.append(Carta(Seme, Rango))<br />
def StampaMazzo(self):<br />
for Carta in self.Carte:<br />
print Carta<br />
def __str__(self):<br />
s = &#8220;&#8221;<br />
for i in range(len(self.Carte)):<br />
s = s + &#8221; &#8220;*i + str(self.Carte[i]) + &#8220;\n&#8221;<br />
return s<br />
def Mischia(self):<br />
import random<br />
NumCarte = len(self.Carte)<br />
for i in range(NumCarte):<br />
j = random.randrange(i, NumCarte)<br />
self.Carte[i], self.Carte[j] = self.Carte[j], self.Carte[i]<br />
def RimuoviCarta(self, Carta):<br />
if Carta in self.Carte:<br />
self.Carte.remove(Carta)<br />
return 1<br />
else:<br />
return 0<br />
def PrimaCarta(self):<br />
return self.Carte.pop()<br />
def EVuoto(self):<br />
return (len(self.Carte) == 0)<br />
def Distribuisci(self, ListaMani, NumCarte=999):<br />
NumMani = len(ListaMani)<br />
for i in range(NumCarte):<br />
if self.EVuoto(): break         # si ferma se non ci sono<br />
# piu` carte<br />
Carta = self.PrimaCarta()       # prende la carta<br />
# superiore del mazzo<br />
Mano = ListaMani[i % NumMani]   # di chi e` il prossimo<br />
# turno?<br />
Mano.AggiungiCarta(Carta)       # aggiungi la carta<br />
# alla mano<br />
class Mano(Mazzo):<br />
def __init__(self, Nome=&#8221;"):<br />
self.Carte = []<br />
self.Nome = Nome<br />
def AggiungiCarta(self,Carta) :<br />
self.Carte.append(Carta)<br />
def __str__(self):<br />
s = &#8220;La mano di &#8221; + self.Nome<br />
if self.EVuoto():<br />
s = s + &#8221; e&#8217; vuota\n&#8221;<br />
else:<br />
s = s + &#8221; contiene queste carte:\n&#8221;<br />
return s + Mazzo.__str__(self)<br />
class GiocoCarte:<br />
def __init__(self):<br />
self.Mazzo = Mazzo()<br />
self.Mazzo.Mischia()<br />
class ManoOldMaid(Mano):<br />
def RimuoviCoppie(self):<br />
Conteggio = 0<br />
CarteOriginali = self.Carte[:]<br />
for CartaOrig in CarteOriginali:<br />
CartaDaCercare = Carta(3-CartaOrig.Seme, CartaOrig.Rango)<br />
if CartaDaCercare in self.Carte:<br />
self.Carte.remove(CartaOrig)<br />
self.Carte.remove(CartaDaCercare)<br />
print &#8220;Mano di %s: %s elimina %s&#8221; % \<br />
(self.Nome,CartaOrig,CartaDaCercare)<br />
Conteggio = Conteggio + 1<br />
return Conteggio<br />
class GiocoOldMaid(GiocoCarte):<br />
def Partita(self, Nomi):<br />
# rimozione della regina di fiori<br />
self.Mazzo.RimuoviCarta(Carta(0,12))<br />
# creazione di una mano per ogni giocatore<br />
self.Mani = []<br />
for Nome in Nomi:<br />
self.Mani.append(ManoOldMaid(Nome))<br />
# distribuzione delle carte<br />
self.Mazzo.Distribuisci(self.Mani)<br />
print &#8220;&#8212;&#8212;&#8212;- Le carte sono state distribuite&#8221;<br />
self.StampaMani()<br />
# toglie le coppie iniziali<br />
NumCoppie = self.RimuoveTutteLeCoppie()<br />
print &#8220;&#8212;&#8212;&#8212;- Coppie scartate, inizia la partita&#8221;<br />
self.StampaMani()<br />
# gioca finche&#8217; sono state fatte 50 coppie<br />
Turno = 0<br />
NumMani = len(self.Mani)<br />
while NumCoppie &lt; 25:<br />
NumCoppie = NumCoppie + self.GiocaUnTurno(Turno)<br />
Turno = (Turno + 1) % NumMani<br />
print &#8220;&#8212;&#8212;&#8212;- La partita e&#8217; finita&#8221;<br />
self.StampaMani()<br />
def RimuoveTutteLeCoppie(self):<br />
Conteggio = 0<br />
for Mano in self.Mani:<br />
Conteggio = Conteggio + Mano.RimuoveCoppie()<br />
return Conteggio<br />
def GiocaUnTurno(self, Giocatore):<br />
if self.Mani[Giocatore].EVuoto():<br />
return 0<br />
Vicino = self.TrovaVicino(Giocatore)<br />
CartaScelta = self.Mani[Vicino].SceltaCarta()<br />
self.Mani[Giocatore].AggiungeCarta(CartaScelta)<br />
print &#8220;Mano di&#8221;, self.Mani[Giocatore].Nome, &#8220;: scelta&#8221;, \<br />
CartaScelta<br />
Conteggio = self.Mani[Giocatore].RimuoveCoppie()<br />
self.Mani[Giocatore].Mischia()<br />
return Conteggio<br />
def TrovaVicino(self, Giocatore):<br />
NumMani = len(self.Mani)<br />
for Prossimo in range(1,NumMani):<br />
Vicino = (Giocatore + Prossimo) % NumMani<br />
if not self.Mani[Vicino].EVuoto():<br />
return Vicino</p>
<p>Liste linkate</p>
<p>def StampaLista(Nodo):<br />
while Nodo:<br />
print Nodo,<br />
Nodo = Nodo.ProssimoNodo<br />
print<br />
def StampaInversa(Lista):<br />
if Lista == None: return<br />
Testa = Lista<br />
Coda = Lista.ProssimoNodo<br />
StampaInversa(Coda)<br />
print Testa,<br />
def StampaInversaFormato(Lista) :<br />
print &#8220;[",<br />
if Lista != None :<br />
Testa = Lista<br />
Coda = Lista.ProssimoNodo<br />
StampaInversa(Coda)<br />
print Testa,<br />
print "]&#8220;,<br />
def RimuoviSecondo(Lista):<br />
if Lista == None: return<br />
Primo = Lista<br />
Secondo = Lista.ProssimoNodo<br />
# il primo nodo deve riferirsi al terzo<br />
Primo.ProssimoNodo = Secondo.ProssimoNodo<br />
# separa il secondo nodo dal resto della lista<br />
Secondo.ProssimoNodo = None<br />
return Secondo<br />
class Nodo:<br />
def __init__(self, Contenuto=None, ProssimoNodo=None):<br />
self.Contenuto = Contenuto<br />
self.ProssimoNodo  = ProssimoNodo<br />
def __str__(self):<br />
return str(self.Contenuto)<br />
def StampaInversa(self):<br />
if self.ProssimoNodo != None:<br />
Coda = self.ProssimoNodo<br />
Coda.StampaInversa()<br />
print self.Contenuto,<br />
class ListaLinkata:<br />
def __init__(self) :<br />
self.Lunghezza = 0<br />
self.Testa = None<br />
def StampaInversa(self):<br />
print &#8220;[",<br />
if self.Testa != None:<br />
self.Testa.StampaInversa()<br />
print "]&#8220;,<br />
def AggiuntaPrimo(self, Contenuto):<br />
NodoAggiunto = Nodo(Contenuto)<br />
NodoAggiunto.ProssimoNodo = self.Testa<br />
self.Testa = NodoAggiunto<br />
self.Lunghezza = self.Lunghezza + 1</p>
<p>class Pila</p>
<p>class Pila:<br />
def __init__(self):<br />
self.Elementi = []<br />
def Push(self, Elemento) :<br />
self.Elementi.append(Elemento)<br />
def Pop(self):<br />
return self.Elementi.pop()<br />
def EVuota(self):<br />
return (self.Elementi == [])<br />
def ValutaPostfissa(Espressione):<br />
import re<br />
ListaToken = re.split(&#8220;([^0-9])&#8221;, Espressione)<br />
Pila1 = Pila()<br />
for Token in ListaToken:<br />
if  Token == &#8221; or Token == &#8216; &#8216;:<br />
continue<br />
if  Token == &#8216;+&#8217;:<br />
Somma = Pila1.Pop() + Pila1.Pop()<br />
Pila1.Push(Somma)<br />
elif Token == &#8216;*&#8217;:<br />
Prodotto = Pila1.Pop() * Pila1.Pop()<br />
Pila1.Push(Prodotto)<br />
else:<br />
Pila1.Push(int(Token))<br />
return Pila1.Pop()</p>
<p>Alberi</p>
<p>class Albero:<br />
def __init__(self, Contenuto, Sinistra=None, Destra=None):<br />
self.Contenuto = Contenuto<br />
self.Sinistra  = Sinistra<br />
self.Destra = Destra<br />
def __str__(self):<br />
return str(self.Contenuto)<br />
def OttieniContenuto(self): return self.Contenuto<br />
def RamoDestro(self):       return self.Destra<br />
def RamoSinistro(self):     return self.Sinistra<br />
def SettaContenuto(self, Contenuto): self.Contenuto = Contenuto<br />
def SettaRamoDestro(self, Nodo):     self.Destra = Nodo<br />
def SettaRamoSinistro(self, Nodo):   self.Sinistra = Nodo<br />
def Totale(Albero):<br />
if Albero == None: return 0<br />
return Albero.Contenuto + Totale(Albero.Sinistra) + \<br />
Totale(Albero.Destra)<br />
def StampaAlberoPre(Albero):<br />
if Albero == None: return<br />
print Albero.Contenuto,<br />
StampaAlberoPre(Albero.Sinistra)<br />
StampaAlberoPre(Albero.Destra)<br />
def StampaAlberoPost(Albero):<br />
if Albero == None: return<br />
StampaAlberoPost(Albero.Sinistra)<br />
StampaAlberoPost(Albero.Destra)<br />
print Albero.Contenuto,<br />
def StampaAlberoIn(Albero):<br />
if Albero == None: return<br />
StampaAlberoIn(Albero.Sinistra)<br />
print Albero.Contenuto,<br />
StampaAlberoIn(Albero.Destra)<br />
def StampaAlberoIndentato(Albero, Livello=0):<br />
if Albero == None: return<br />
StampaAlberoIndentato(Albero.Destra, Livello+1)<br />
print &#8216;  &#8216;*Livello + str(Albero.Contenuto)<br />
StampaAlberoIndentato(Albero.Sinistra, Livello+1)<br />
def ControllaToken(ListaToken, TokenAtteso):<br />
if ListaToken[0] == TokenAtteso:<br />
del ListaToken[0]<br />
return 1<br />
else:<br />
return 0<br />
def ControllaNumero(ListaToken):<br />
if ControllaToken(ListaToken, &#8216;(&#8216;):<br />
x = EsprSomma(ListaToken)               # ricava la<br />
# sub-espressione<br />
if not ControllaToken(ListaToken, &#8216;)&#8217;): # rimuove la<br />
# parentesi chiusa<br />
raise &#8216;BadExpressionError&#8217;, &#8216;manca la parentesi&#8217;<br />
return x<br />
else:<br />
x = ListaToken[0]<br />
if type(x) != type(0): return None<br />
ListaToken[0:1] = []<br />
return Albero(x, None, None)<br />
def EsprProdotto(ListaToken):<br />
a = ControllaNumero(ListaToken)<br />
if ControllaToken(ListaToken, &#8216;*&#8217;):<br />
b = EsprProdotto(ListaToken)<br />
return Albero(&#8216;*&#8217;, a, b)<br />
else:<br />
return a<br />
def EsprSomma(ListaToken):<br />
a = EsprProdotto(ListaToken)<br />
if ControllaToken(ListaToken, &#8216;+&#8217;):<br />
b = EsprSomma(ListaToken)<br />
return Albero(&#8216;+&#8217;, a, b)<br />
else:<br />
return a</p>
<p>Indovina l&#8217;animale</p>
<p>def RispostaAffermativa(Domanda):<br />
from string import lower<br />
Risposta = lower(raw_input(Domanda))<br />
return (Risposta[0] == &#8216;s&#8217;)<br />
def Animale():<br />
# parte con una lista composta di un solo elemento<br />
Radice = Albero(&#8220;uccello&#8221;)<br />
# continua finche&#8217; l&#8217;operatore non abbandona<br />
while 1:<br />
print<br />
if not RispostaAffermativa(&#8220;Stai pensando ad un \<br />
animale? &#8220;): break<br />
# percorre l&#8217;albero<br />
SottoAlbero = Radice<br />
while SottoAlbero.RamoSinistro() != None:<br />
Messaggio = SottoAlbero.OttieniContenuto() + &#8220;? &#8220;<br />
if RispostaAffermativa(Messaggio):<br />
SottoAlbero = SottoAlbero.RamoDestro()<br />
else:<br />
SottoAlbero = SottoAlbero.RamoSinistro()<br />
# prova a indovinare<br />
Ipotesi = SottoAlbero.OttieniContenuto()<br />
Messaggio = &#8220;E&#8217; un &#8221; + Ipotesi + &#8220;? &#8220;<br />
if RispostaAffermativa(Messaggio):<br />
print &#8220;Ho indovinato!&#8221;<br />
continue<br />
# ottiene nuove informazioni<br />
Messaggio = &#8220;Qual e&#8217; il nome dell&#8217;animale? &#8220;<br />
Animale  = raw_input(Messaggio)<br />
Messaggio  = &#8220;Che domanda permette di distinguere tra un %s \<br />
e un %s? &#8220;<br />
Domanda = raw_input(Messaggio % (Animale, Ipotesi))<br />
# aggiunge le nuove informazioni all&#8217;albero<br />
SottoAlbero.SettaContenuto(Domanda)<br />
Messaggio = &#8220;Se l&#8217;animale fosse un %s quale sarebbe la \<br />
risposta? &#8220;<br />
if RispostaAffermativa(Messaggio % Animale):<br />
SottoAlbero.SettaRamoSinistro(Albero(Ipotesi))<br />
SottoAlbero.SettaRamoDestro(Albero(Animale))<br />
else:<br />
SottoAlbero.SettaRamoSinistro(Albero(Animale))<br />
SottoAlbero.SettaRamoDestro(Albero(Ipotesi))</p>
<p>class Frazione</p>
<p>def MCD(m, n):<br />
if m % n == 0:<br />
return n<br />
else:<br />
return MCD(n, m%n)<br />
class Frazione:<br />
def __init__(self, Numeratore, Denominatore=1):<br />
mcd = MCD(Numeratore, Denominatore)<br />
self.Numeratore   = Numeratore / mcd<br />
self.Denominatore = Denominatore / mcd<br />
def __str__(self):<br />
return &#8220;%d/%d&#8221; % (self.Numeratore, self.Denominatore)<br />
def __mul__(self, Altro):<br />
if type(Altro) == type(1):<br />
Altro = Frazione(Altro)<br />
return Frazione(self.Numeratore * Altro.Numeratore, \<br />
self.Denominatore * Altro.Denominatore)<br />
__rmul__ = __mul__<br />
def __add__(self, Altro):<br />
if type(Altro) == type(5):<br />
Altro = Frazione(Altro)<br />
return Frazione(self.Numeratore   * Altro.Denominatore +<br />
self.Denominatore * Altro.Numeratore,<br />
self.Denominatore * Altro.Denominatore)<br />
__radd__ = __add__<br />
def __cmp__(self, Altro):<br />
Differenza = (self.Numeratore  * Altro.Denominatore &#8211; \<br />
Altro.Numeratore * self.Denominatore)<br />
return Differenza<br />
def __repr__(self):<br />
return self.__str__()</p>
<p>Appendice D</p>
<p>Altro materiale</p>
<p>Arrivati a questo punto qual è la direzione da prendere? Le<br />
possibilità sono molte e vanno dall&#8217;ampliamento della conoscenza<br />
dell&#8217;informatica in generale, all&#8217;applicazione di Python in campi<br />
specifici.</p>
<p>Gli esempi proposti in questo libro sono stati deliberatamente<br />
semplici ma non hanno mostrato appieno quelle che sono le capacità più<br />
entusiasmanti del linguaggio. Ecco un campionario di estensioni di<br />
Python e di suggerimenti per progetti che le usano.<br />
* La programmazione dell&#8217;interfaccia grafica (detta anche &#8220;GUI&#8221;,<br />
graphical user interface) permette al tuo programma di interagire<br />
con l&#8217;operatore sotto forma di ambiente grafico. Il primo<br />
pacchetto grafico nato per Python è stato Tkinter, basato sui<br />
linguaggi di scripting Tcl e Tk di Jon Ousterhout. Tkinter è<br />
sempre presente nelle distribuzioni di Python.<br />
Un&#8217;altra piattaforma piuttosto conosciuta è wxPython. Questa è<br />
essenzialmente una maschera per facilitare l&#8217;uso di wxWindows, un<br />
pacchetto scritto in C++ che implementa un sistema a finestre<br />
usando un&#8217;interfaccia nativa in ambiente Windows e Unix (Linux<br />
incluso). Le finestre ed i controlli in wxPython tendono ad essere<br />
più semplici da programmare rispetto ai corrispondenti Tkinter.<br />
Qualsiasi tipo di programmazione con interfaccia grafica ti<br />
porterà ad un ambiente di programmazione controllato dall&#8217;evento,<br />
dove non è tanto il programmatore ma l&#8217;operatore a decidere il<br />
flusso di esecuzione. Questo stile di programmazione necessita di<br />
un po&#8217; di pratica per poter essere gestito nel modo migliore e<br />
talvolta può comportare una completa riscrittura del programma.<br />
* La programmazione Web integra Python con Internet. Possiamo, per<br />
esempio, costruire programmi web client che aprono e leggono una<br />
pagina remota in modo abbastanza semplice, tanto che le difficoltà<br />
sono confrontabili con quelle (minime) che si possono incontrare<br />
durante l&#8217;apertura di un file su disco locale. Ci sono moduli<br />
Python che permettono l&#8217;accesso a file remoti via ftp e moduli che<br />
consentono di ricevere e spedire email. Python è ampiamente usato<br />
anche per la gestione di form di introduzione dati nei web server.<br />
* I database sono paragonabili a dei super-file dove i dati sono<br />
memorizzati secondo schemi predefiniti e sono accessibili in vari<br />
modi. Python è dotato di un certo numero di moduli per accedere a<br />
dati di diversi tipi di database, sia Open Source che commerciali.<br />
* La programmazione a thread permette di eseguire diversi flussi di<br />
programma allo stesso tempo a partire da un unico programma. Se<br />
hai presente come funziona un browser per Internet puoi farti<br />
un&#8217;idea di cosa questo significhi: in un browser vengono caricate<br />
più pagine contemporaneamente e mentre ne guardi una il<br />
caricamento delle altre prosegue in modo quasi del tutto<br />
trasparente.<br />
* Quando ci troviamo alle prese con necessità particolari ed è<br />
indispensabile una maggiore velocità di esecuzione Python può<br />
essere integrato da moduli scritti in altri linguaggi, tipo il C<br />
ed il C++. Queste estensioni formano la base dei moduli presenti<br />
nelle librerie standard di Python. Anche se le procedure per<br />
l&#8217;integrazione di questi moduli possono essere piuttosto complesse<br />
esiste uno strumento chiamato SWIG (Simplified Wrapper and<br />
Interface Generator) che permette di semplificare enormemente<br />
l&#8217;operazione.</p>
<p>Siti e libri su Python</p>
<p>Prima di procedere con le raccomandazioni degli autori, per quel che<br />
riguarda le risorse disponibili in Internet, ti consiglio di dare<br />
un&#8217;occhiata ai siti www.zonapython.it, www.python.it e<br />
python.programmazione.it che possono rappresentare un buon<br />
trampolino di lancio grazie anche (e soprattutto) al fatto di<br />
essere in lingua italiana.<br />
* L&#8217;homepage di Python, www.python.org è il luogo dove iniziare ogni<br />
ricerca: troverai aiuto, documentazione, link ad altri siti e<br />
mailing list dei SIG (Special Interest Group) alle quali puoi<br />
eventualmente associarti.<br />
* L&#8217;Open Book Project www.ibiblio.com/obp contiene non soltanto<br />
questo libro, ma versioni simili per Java e C++ scritti da Allen<br />
Downey. Inoltre potrai trovare una serie di altri documenti che<br />
spaziano dai circuiti elettrici a Python (Python for Fun di Chris<br />
Meyers), passando per il sistema operativo Linux (The Linux<br />
Cookbook by Michael Stultz, con 300 pagine di suggerimenti e<br />
tecniche).  La versione tradotta in italiano e gli<br />
aggiornamenti saranno raggiungibili dal sito<br />
www.zonapython.it.<br />
* Se poi vai su Google e cerchi &#8220;python -snake -monty&#8221; potrai<br />
rimanere stupito della mole di informazioni disponibili.</p>
<p>Per quanto concerne i libri, la bibliografia su Python, in italiano,<br />
si sta via via ampliando. Prova a chiedere in libreria: non ha neanche<br />
tanto senso indicare dei titoli quando il materiale disponibile è<br />
soggetto a variazioni così repentine. Non dimenticare che Python è un<br />
linguaggio giovane ed è soggetto a continue modifiche.</p>
<p>Per quanto concerne i libri in lingua inglese tra gli altri si<br />
distinguono quelli del nostro Alex Martelli. Una ricerca in<br />
www.amazon.com presenta circa 200 titoli disponibili. Tra questi<br />
consigliamo:<br />
* Python in a Nutshell di Alex Martelli, è un ottimo riferimento per<br />
programmatori. Risolve brillantemente le difficoltà che insorgono<br />
quando è necessario ricordare la sintassi del linguaggio e dei<br />
suoi molti moduli, tratta sia le parti più usate delle librerie<br />
standard che le estensioni più conosciute.<br />
* Python Cookbook di Alex Martelli e David Ascher, è un&#8217;ottima<br />
raccolta di &#8220;ricette&#8221; basate su esempi pratici e offre la<br />
soluzione a oltre 200 problemi in ogni campo di applicazione.<br />
* Core Python Programming di Wesley Chun (750 pagine), copre il<br />
linguaggio a partire dai concetti fondamentale per arrivare a<br />
trattare di tecniche avanzate.<br />
* Python Essential Reference (2nd edition) di David M. Beazley e<br />
Guido Van Rossum è molto ben fatto, tratta il linguaggio ed i<br />
moduli della libreria standard.<br />
* Python Pocket Reference di Mark Lutz really, sebbene non così<br />
completo come la Python Essential Reference è un ottimo<br />
riferimento per le funzioni usate più frequentemente.<br />
* Python Programming on Win32 di Mark Hammond e Andy Robinson deve<br />
far parte della biblioteca di chiunque si appresti a programmare<br />
in Python in ambiente Windows.</p>
<p>Informatica in generale</p>
<p>Ecco qualche suggerimento per ulteriori letture, inclusi molti dei<br />
libri favoriti dagli autori. Trattano delle tecniche di programmazione<br />
da preferire e dell&#8217;informatica in generale.<br />
* The Practice of Programming di Kernighan e Pike, oltre alla<br />
progettazione e alla codifica degli algoritmi e delle strutture di<br />
dati, tratta del debug, del test e del miglioramento delle<br />
performance dei programmi. Non ci sono esempi in Python.<br />
* The Elements of Java Style di Al Vermeulen è un altro piccolo<br />
libro che tratta dei punti caratteristici della buona<br />
programmazione usando come riferimento il linguaggio Java.<br />
* Programming Pearls di Jon Bentley è un classico e consiste di una<br />
raccolta di casi reali trattati dall&#8217;autore nella sua rubrica<br />
Communications of the ACM. Ciò che emerge è il concetto che<br />
raramente la prima idea per lo sviluppo di un programma è quella<br />
ottimale. È piuttosto datato (1986) ed è stato seguito da un<br />
secondo volume.<br />
* The New Turing Omnibus di A.K Dewdney fornisce un&#8217;introduzione<br />
indolore a 66 argomenti correlati all&#8217;informatica,<br />
dall&#8217;elaborazione parallela ai virus, passando per gli algoritmi<br />
genetici. Tutti gli argomenti sono trattati in modo divertente e<br />
succinto.<br />
* Turtles, Termites and Traffic Jams di Mitchel Resnick mostra come<br />
il comportamento complesso può nascere dal coordinamento di<br />
semplici attività delegate a molteplici agenti. Molti degli esempi<br />
del libro sono scritti in StarLogo, sono stati sviluppati da<br />
studenti e possono essere riscritti usando Python.<br />
* Gödel, Escher, Bach: un&#8217;eterna ghirlanda brillante di Douglas<br />
Hofstadter. Se ti piace la ricorsione la troverai come<br />
protagonista di questo libro, pubblicato anche in lingua italiana<br />
* Note. L&#8217;autore dimostra la relazione esistente tra la musica di<br />
J.S.Bach, le immagini di Cornelius Escher ed il teorema<br />
dell&#8217;incompletezza di Gödel.</p>
<p>GNU Free Documentation License</p>
<p>Version 1.1, March 2000</p>
<p>Copyright © 2000 Free Software Foundation, Inc.<br />
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA<br />
Everyone is permitted to copy and distribute verbatim copies of this<br />
license document, but changing it is not allowed.</p>
<p>Preamble</p>
<p>The purpose of this License is to make a manual, textbook, or other<br />
written document &#8220;free&#8221; in the sense of freedom: to assure everyone<br />
the effective freedom to copy and redistribute it, with or without<br />
modifying it, either commercially or noncommercially. Secondarily,<br />
this License preserves for the author and publisher a way to get<br />
credit for their work, while not being considered responsible for<br />
modifications made by others.</p>
<p>This License is a kind of &#8220;copyleft,&#8221; which means that derivative<br />
works of the document must themselves be free in the same sense. It<br />
complements the GNU General Public License, which is a copyleft<br />
license designed for free software.</p>
<p>We have designed this License in order to use it for manuals for free<br />
software, because free software needs free documentation: a free<br />
program should come with manuals providing the same freedoms that the<br />
software does. But this License is not limited to software manuals; it<br />
can be used for any textual work, regardless of subject matter or<br />
whether it is published as a printed book. We recommend this License<br />
principally for works whose purpose is instruction or reference.</p>
<p>Applicability and Definitions</p>
<p>This License applies to any manual or other work that contains a<br />
notice placed by the copyright holder saying it can be distributed<br />
under the terms of this License. The &#8220;Document,&#8221; below, refers to any<br />
such manual or work. Any member of the public is a licensee, and is<br />
addressed as &#8220;you.&#8221;</p>
<p>A &#8220;Modified Version&#8221; of the Document means any work containing the<br />
Document or a portion of it, either copied verbatim, or with<br />
modifications and/or translated into another language.</p>
<p>A &#8220;Secondary Section&#8221; is a named appendix or a front-matter section of<br />
the Document that deals exclusively with the relationship of the<br />
publishers or authors of the Document to the Document&#8217;s overall<br />
subject (or to related matters) and contains nothing that could fall<br />
directly within that overall subject. (For example, if the Document is<br />
in part a textbook of mathematics, a Secondary Section may not explain<br />
any mathematics.) The relationship could be a matter of historical<br />
connection with the subject or with related matters, or of legal,<br />
commercial, philosophical, ethical, or political position regarding<br />
them.</p>
<p>The &#8220;Invariant Sections&#8221; are certain Secondary Sections whose titles<br />
are designated, as being those of Invariant Sections, in the notice<br />
that says that the Document is released under this License.</p>
<p>The &#8220;Cover Texts&#8221; are certain short passages of text that are listed,<br />
as Front-Cover Texts or Back-Cover Texts, in the notice that says that<br />
the Document is released under this License.</p>
<p>A &#8220;Transparent&#8221; copy of the Document means a machine-readable copy,<br />
represented in a format whose specification is available to the<br />
general public, whose contents can be viewed and edited directly and<br />
straightforwardly with generic text editors or (for images composed of<br />
pixels) generic paint programs or (for drawings) some widely available<br />
drawing editor, and that is suitable for input to text formatters or<br />
for automatic translation to a variety of formats suitable for input<br />
to text formatters. A copy made in an otherwise Transparent file<br />
format whose markup has been designed to thwart or discourage<br />
subsequent modification by readers is not Transparent. A copy that is<br />
not &#8220;Transparent&#8221; is called &#8220;Opaque.&#8221;</p>
<p>Examples of suitable formats for Transparent copies include plain<br />
ASCII without markup, Texinfo input format, LaTeX input format, SGML<br />
or XML using a publicly available DTD, and standard-conforming simple<br />
HTML designed for human modification. Opaque formats include<br />
PostScript, PDF, proprietary formats that can be read and edited only<br />
by proprietary word processors, SGML or XML for which the DTD and/or<br />
processing tools are not generally available, and the<br />
machine-generated HTML produced by some word processors for output<br />
purposes only.</p>
<p>The &#8220;Title Page&#8221; means, for a printed book, the title page itself,<br />
plus such following pages as are needed to hold, legibly, the material<br />
this License requires to appear in the title page. For works in<br />
formats which do not have any title page as such, &#8220;Title Page&#8221; means<br />
the text near the most prominent appearance of the work&#8217;s title,<br />
preceding the beginning of the body of the text.</p>
<p>Verbatim Copying</p>
<p>You may copy and distribute the Document in any medium, either<br />
commercially or noncommercially, provided that this License, the<br />
copyright notices, and the license notice saying this License applies<br />
to the Document are reproduced in all copies, and that you add no<br />
other conditions whatsoever to those of this License. You may not use<br />
technical measures to obstruct or control the reading or further<br />
copying of the copies you make or distribute. However, you may accept<br />
compensation in exchange for copies. If you distribute a large enough<br />
number of copies you must also follow the conditions in Section 3.</p>
<p>You may also lend copies, under the same conditions stated above, and<br />
you may publicly display copies.</p>
<p>Copying in Quantity</p>
<p>If you publish printed copies of the Document numbering more than 100,<br />
and the Document&#8217;s license notice requires Cover Texts, you must<br />
enclose the copies in covers that carry, clearly and legibly, all<br />
these Cover Texts: Front-Cover Texts on the front cover, and<br />
Back-Cover Texts on the back cover. Both covers must also clearly and<br />
legibly identify you as the publisher of these copies. The front cover<br />
must present the full title with all words of the title equally<br />
prominent and visible. You may add other material on the covers in<br />
addition. Copying with changes limited to the covers, as long as they<br />
preserve the title of the Document and satisfy these conditions, can<br />
be treated as verbatim copying in other respects.</p>
<p>If the required texts for either cover are too voluminous to fit<br />
legibly, you should put the first ones listed (as many as fit<br />
reasonably) on the actual cover, and continue the rest onto adjacent<br />
pages.</p>
<p>If you publish or distribute Opaque copies of the Document numbering<br />
more than 100, you must either include a machine-readable Transparent<br />
copy along with each Opaque copy, or state in or with each Opaque copy<br />
a publicly accessible computer-network location containing a complete<br />
Transparent copy of the Document, free of added material, which the<br />
general network-using public has access to download anonymously at no<br />
charge using public-standard network protocols. If you use the latter<br />
option, you must take reasonably prudent steps, when you begin<br />
distribution of Opaque copies in quantity, to ensure that this<br />
Transparent copy will remain thus accessible at the stated location<br />
until at least one year after the last time you distribute an Opaque<br />
copy (directly or through your agents or retailers) of that edition to<br />
the public.</p>
<p>It is requested, but not required, that you contact the authors of the<br />
Document well before redistributing any large number of copies, to<br />
give them a chance to provide you with an updated version of the<br />
Document.</p>
<p>Modifications</p>
<p>You may copy and distribute a Modified Version of the Document under<br />
the conditions of Sections 2 and 3 above, provided that you release<br />
the Modified Version under precisely this License, with the Modified<br />
Version filling the role of the Document, thus licensing distribution<br />
and modification of the Modified Version to whoever possesses a copy<br />
of it. In addition, you must do these things in the Modified Version:<br />
* Use in the Title Page (and on the covers, if any) a title distinct<br />
from that of the Document, and from those of previous versions<br />
(which should, if there were any, be listed in the History section<br />
of the Document). You may use the same title as a previous version<br />
if the original publisher of that version gives permission.<br />
* List on the Title Page, as authors, one or more persons or<br />
entities responsible for authorship of the modifications in the<br />
Modified Version, together with at least five of the principal<br />
authors of the Document (all of its principal authors, if it has<br />
less than five).<br />
* State on the Title page the name of the publisher of the Modified<br />
Version, as the publisher.<br />
* Preserve all the copyright notices of the Document.<br />
* Add an appropriate copyright notice for your modifications<br />
adjacent to the other copyright notices.<br />
* Include, immediately after the copyright notices, a license notice<br />
giving the public permission to use the Modified Version under the<br />
terms of this License, in the form shown in the Addendum below.<br />
* Preserve in that license notice the full lists of Invariant<br />
Sections and required Cover Texts given in the Document&#8217;s license<br />
notice.<br />
* Include an unaltered copy of this License.<br />
* Preserve the section entitled &#8220;History,&#8221; and its title, and add to<br />
it an item stating at least the title, year, new authors, and<br />
publisher of the Modified Version as given on the Title Page. If<br />
there is no section entitled &#8220;History&#8221; in the Document, create one<br />
stating the title, year, authors, and publisher of the Document as<br />
given on its Title Page, then add an item describing the Modified<br />
Version as stated in the previous sentence.<br />
* Preserve the network location, if any, given in the Document for<br />
public access to a Transparent copy of the Document, and likewise<br />
the network locations given in the Document for previous versions<br />
it was based on. These may be placed in the &#8220;History&#8221; section. You<br />
may omit a network location for a work that was published at least<br />
four years before the Document itself, or if the original<br />
publisher of the version it refers to gives permission.<br />
* In any section entitled &#8220;Acknowledgements&#8221; or &#8220;Dedications,&#8221;<br />
preserve the section&#8217;s title, and preserve in the section all the<br />
substance and tone of each of the contributor acknowledgements<br />
and/or dedications given therein.<br />
* Preserve all the Invariant Sections of the Document, unaltered in<br />
their text and in their titles. Section numbers or the equivalent<br />
are not considered part of the section titles.<br />
* Delete any section entitled &#8220;Endorsements.&#8221; Such a section may not<br />
be included in the Modified Version.<br />
* Do not retitle any existing section as &#8220;Endorsements&#8221; or to<br />
conflict in title with any Invariant Section.</p>
<p>If the Modified Version includes new front-matter sections or<br />
appendices that qualify as Secondary Sections and contain no material<br />
copied from the Document, you may at your option designate some or all<br />
of these sections as invariant. To do this, add their titles to the<br />
list of Invariant Sections in the Modified Version&#8217;s license notice.<br />
These titles must be distinct from any other section titles. You may<br />
add a section entitled &#8220;Endorsements,&#8221; provided it contains nothing<br />
but endorsements of your Modified Version by various parties [DEL:<br />
 <img src='http://s0.wp.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> EL] for example, statements of peer review or that the text has been<br />
approved by an organization as the authoritative definition of a<br />
standard. You may add a passage of up to five words as a Front-Cover<br />
Text, and a passage of up to 25 words as a Back-Cover Text, to the end<br />
of the list of Cover Texts in the Modified Version. Only one passage<br />
of Front-Cover Text and one of Back-Cover Text may be added by (or<br />
through arrangements made by) any one entity. If the Document already<br />
includes a cover text for the same cover, previously added by you or<br />
by arrangement made by the same entity you are acting on behalf of,<br />
you may not add another; but you may replace the old one, on explicit<br />
permission from the previous publisher that added the old one. The<br />
author(s) and publisher(s) of the Document do not by this License give<br />
permission to use their names for publicity for or to assert or imply<br />
endorsement of any Modified Version.</p>
<p>Combining Documents</p>
<p>You may combine the Document with other documents released under this<br />
License, under the terms defined in Section 4 above for modified<br />
versions, provided that you include in the combination all of the<br />
Invariant Sections of all of the original documents, unmodified, and<br />
list them all as Invariant Sections of your combined work in its<br />
license notice. The combined work need only contain one copy of this<br />
License, and multiple identical Invariant Sections may be replaced<br />
with a single copy. If there are multiple Invariant Sections with the<br />
same name but different contents, make the title of each such section<br />
unique by adding at the end of it, in parentheses, the name of the<br />
original author or publisher of that section if known, or else a<br />
unique number. Make the same adjustment to the section titles in the<br />
list of Invariant Sections in the license notice of the combined work.<br />
In the combination, you must combine any sections entitled &#8220;History&#8221;<br />
in the various original documents, forming one section entitled<br />
&#8220;History&#8221;; likewise combine any sections entitled &#8220;Acknowledgements,&#8221;<br />
and any sections entitled &#8220;Dedications.&#8221; You must delete all sections<br />
entitled &#8220;Endorsements.&#8221;</p>
<p>Collections of Documents</p>
<p>You may make a collection consisting of the Document and other<br />
documents released under this License, and replace the individual<br />
copies of this License in the various documents with a single copy<br />
that is included in the collection, provided that you follow the rules<br />
of this License for verbatim copying of each of the documents in all<br />
other respects. You may extract a single document from such a<br />
collection, and distribute it individually under this License,<br />
provided you insert a copy of this License into the extracted<br />
document, and follow this License in all other respects regarding<br />
verbatim copying of that document.</p>
<p>Aggregation with Independent Works</p>
<p>A compilation of the Document or its derivatives with other separate<br />
and independent documents or works, in or on a volume of a storage or<br />
distribution medium, does not as a whole count as a Modified Version<br />
of the Document, provided no compilation copyright is claimed for the<br />
compilation. Such a compilation is called an &#8220;aggregate,&#8221; and this<br />
License does not apply to the other self-contained works thus compiled<br />
with the Document, on account of their being thus compiled, if they<br />
are not themselves derivative works of the Document.</p>
<p>If the Cover Text requirement of Section 3 is applicable to these<br />
copies of the Document, then if the Document is less than one quarter<br />
of the entire aggregate, the Document&#8217;s Cover Texts may be placed on<br />
covers that surround only the Document within the aggregate. Otherwise<br />
they must appear on covers around the whole aggregate.</p>
<p>Translation</p>
<p>Translation is considered a kind of modification, so you may<br />
distribute translations of the Document under the terms of Section 4.<br />
Replacing Invariant Sections with translations requires special<br />
permission from their copyright holders, but you may include<br />
translations of some or all Invariant Sections in addition to the<br />
original versions of these Invariant Sections. You may include a<br />
translation of this License provided that you also include the<br />
original English version of this License. In case of a disagreement<br />
between the translation and the original English version of this<br />
License, the original English version will prevail.</p>
<p>Termination</p>
<p>You may not copy, modify, sublicense, or distribute the Document<br />
except as expressly provided for under this License. Any other attempt<br />
to copy, modify, sublicense, or distribute the Document is void, and<br />
will automatically terminate your rights under this License. However,<br />
parties who have received copies, or rights, from you under this<br />
License will not have their licenses terminated so long as such<br />
parties remain in full compliance.</p>
<p>Future Revisions of This License</p>
<p>The Free Software Foundation may publish new, revised versions of the<br />
GNU Free Documentation License from time to time. Such new versions<br />
will be similar in spirit to the present version, but may differ in<br />
detail to address new problems or concerns. See<br />
http:///www.gnu.org/copyleft/.</p>
<p>Each version of the License is given a distinguishing version number.<br />
If the Document specifies that a particular numbered version of this<br />
License &#8220;or any later version&#8221; applies to it, you have the option of<br />
following the terms and conditions either of that specified version or<br />
of any later version that has been published (not as a draft) by the<br />
Free Software Foundation. If the Document does not specify a version<br />
number of this License, you may choose any version ever published (not<br />
as a draft) by the Free Software Foundation.</p>
<p>Addendum: How to Use This License for Your Documents</p>
<p>To use this License in a document you have written, include a copy of<br />
the License in the document and put the following copyright and<br />
license notices just after the title page:</p>
<p>Copyright © YEAR YOUR NAME. Permission is granted to copy,<br />
distribute and/or modify this document under the terms of the GNU<br />
Free Documentation License, Version 1.1 or any later version<br />
published by the Free Software Foundation; with the Invariant<br />
Sections being LIST THEIR TITLES, with the Front-Cover Texts being<br />
LIST, and with the Back-Cover Texts being LIST. A copy of the<br />
license is included in the section entitled &#8220;GNU Free Documentation<br />
License.&#8221;</p>
<p>If you have no Invariant Sections, write &#8220;with no Invariant Sections&#8221;<br />
instead of saying which ones are invariant. If you have no Front-Cover<br />
Texts, write &#8220;no Front-Cover Texts&#8221; instead of &#8220;Front-Cover Texts<br />
being LIST&#8221;; likewise for Back-Cover Texts.</p>
<p>If your document contains nontrivial examples of program code, we<br />
recommend releasing these examples in parallel under your choice of<br />
free software license, such as the GNU General Public License, to<br />
permit their use in free software.</p>
<p>Indice</p>
<p>accesso&#8230; 8.2<br />
accettare con fiducia&#8230; 5.6<br />
politica&#8230; 19<br />
priorità&#8230; 19<br />
accumulatore&#8230; 15.6, 15.9, 16.7<br />
addizione&#8230; 20.5<br />
addizione di frazioni&#8230; Appendice B<br />
metodo&#8230; 17.8<br />
albero&#8230; 20<br />
attraversamento&#8230; 20.2, 20.4<br />
binario&#8230; 20.8, 20<br />
di espressione&#8230; 20.3, 20.5<br />
vuoto&#8230; 20<br />
algoritmo&#8230; 1.6, 13.7, 13.8<br />
alias&#8230; 8.11, 8.17, 10.3, 12.8<br />
linguaggio di&#8230; 1.1<br />
ambiguità&#8230; 1.4, 12.4<br />
teorema fond&#8230;. 17.6<br />
gioco&#8230; 20.7<br />
annidamento&#8230; 4.13<br />
lista&#8230; 8.14<br />
metodo&#8230; 15.5<br />
argomento&#8230; 3.1, 3.9, 3.13<br />
serie&#8230; 6.3<br />
assegnazione&#8230; 2.2, 2.11, 6.1<br />
assegnazione ripetuta&#8230; 6.10<br />
ripetuta&#8230; 6.1<br />
tupla&#8230; 9.2, 9.9, 15.7<br />
attraversamento&#8230; 7.3, 7.7, 7.11, 8.5, 16.6, 19.5, 20.2, 20.4<br />
di una lista&#8230; 8.2, 8.17, 17.3, 17.4<br />
AttributeError&#8230; Appendice A<br />
attributo&#8230; 12.2, 12.9<br />
di classe&#8230; 15.3, 15.9<br />
linguaggio di&#8230; 1.1<br />
tabella&#8230; 6.4<br />
operatore&#8230; 20.3, 20.8<br />
blocco&#8230; 4.4, 4.13, Appendice A<br />
espressione&#8230; 4.2, 4.13<br />
funzione&#8230; 5.4, 15.8<br />
istruzione&#8230; 11.6, 11<br />
bug&#8230; 1.3, 1.6<br />
da una lista&#8230; 8.9<br />
carattere&#8230; 7.1<br />
di sottolineatura&#8230; 2.3<br />
classe&#8230; 15.2<br />
casuale&#8230; 15.7<br />
numero&#8230; 9.4<br />
chiamata di funzione&#8230; 3.1, 3.13<br />
chiave&#8230; 10.8, 10<br />
ciclo&#8230; 6.2, 6.10<br />
annidato&#8230; 15.5<br />
attraversamento&#8230; 7.3<br />
condizione&#8230; Appendice A<br />
corpo&#8230; 6.2, 6.10<br />
elaborazione trasversale&#8230; 7.3<br />
for&#8230; 7.3, 8.5<br />
infinito&#8230; 6.2, 6.10, Appendice A<br />
nella lista&#8230; 17.5<br />
variabile&#8230; 16.3<br />
while&#8230; 6.2<br />
definizione&#8230; 5.5<br />
classe&#8230; 12.9, 12<br />
attributo&#8230; 15.3, 15.9<br />
Carta&#8230; 15.2<br />
figlia&#8230; 16.1, 16.8<br />
genitore&#8230; 16.1, 16.2, 16.4, 16.8<br />
GiocoOldMaid&#8230; 16.7<br />
Golf&#8230; 19.6<br />
ListaLinkata&#8230; 17.9<br />
ManoOldMaid&#8230; 16.6<br />
Nodo&#8230; 17.2<br />
Pila&#8230; 18.3<br />
Punto&#8230; 14.7<br />
caratteri&#8230; 7.10<br />
cliente&#8230; 18.1, 18.9<br />
clonazione&#8230; 8.12, 8.17, 10.3<br />
coda&#8230; 19.7, 19<br />
Coda&#8230; 19.1<br />
coda con priorità&#8230; 19.7<br />
TDA&#8230; 19.5<br />
coda linkata&#8230; 19.7<br />
coda migliorata&#8230; 19.4<br />
con priorità&#8230; 19<br />
della lista&#8230; 19.1<br />
linkata&#8230; 19.2<br />
migliorata&#8230; 19.4<br />
linkata&#8230; 19.2<br />
morto&#8230; 5.1, 5.9<br />
oggetto&#8230; 1.6<br />
sorgente&#8230; 1.6<br />
temporaneo&#8230; 5.2, 5.9<br />
codifica&#8230; 15.2<br />
codificare&#8230; 15.9<br />
collezione&#8230; 17.3, 18.2<br />
colonna&#8230; 8.15<br />
commento&#8230; 2.10, 2.11<br />
compilatore&#8230; 1.1, 1.6, Appendice A<br />
linguaggio&#8230; 5.5<br />
composizione&#8230; 2.9, 2.11, 3.5, 5.3, 15.1, 15.5<br />
istruzione&#8230; 4.4<br />
tipo di dati&#8230; 7.1, 12.1<br />
compressione&#8230; 10.7<br />
concatenamento&#8230; 2.8, 2.11, 7.3, 7.6<br />
di liste&#8230; 8.6<br />
istruzione&#8230; 4.13<br />
operatore&#8230; 15.4<br />
condizione&#8230; 4.13, 6.2<br />
del ciclo&#8230; Appendice A<br />
di guardia&#8230; 5.9<br />
in serie&#8230; 4.6<br />
confrontabile&#8230; 15.4<br />
carte&#8230; 15.4<br />
frazioni&#8230; Appendice B<br />
stringhe&#8230; 7.5<br />
contatore&#8230; 7.8, 7.11<br />
conteggio&#8230; 9.6, 10.7<br />
contenitore&#8230; 17.11<br />
metodo&#8230; 17.8<br />
contenuto&#8230; 17.1, 17.11, 20<br />
istruzione&#8230; 11.1, 11.6<br />
degli errori&#8230; 5.8<br />
dei tipi&#8230; 5.8<br />
conversione di tipo&#8230; 3.2<br />
copia&#8230; 10.3, 12.8<br />
debole&#8230; 12.9<br />
forte&#8230; 12.9<br />
coppia chiave-valore&#8230; 10.8, 10<br />
modulo&#8230; 12.8<br />
corpo&#8230; 4.4, 4.13<br />
ciclo&#8230; 6.2<br />
di istruzione composta&#8230; 4.4<br />
costruttore&#8230; 12.1, 12.9, 15.2<br />
cursore&#8230; 6.10<br />
dati astratti, vedi TDA&#8230; 18.1<br />
recupero&#8230; 11.4<br />
struttura ricorsiva&#8230; 17.11<br />
debug&#8230; 1.3, 1.6, Appendice A<br />
decremento&#8230; 7.11<br />
circolare&#8230; 5.5<br />
di funzione&#8230; 3.6, 3.13<br />
ricorsiva&#8230; 20.5<br />
delimitatore&#8230; 8.17, 11.3, 18.6, 18.9<br />
denominatore&#8230; Appendice B<br />
programma&#8230; 9.9<br />
di stack&#8230; 3.13, 4.10<br />
di stato&#8230; 2.2, 2.11<br />
directory&#8230; 11.3, 11.6<br />
distribuire le carte&#8230; 16.3<br />
divisione tra interi&#8230; 2.6, 2.11, 3.3<br />
dizionario&#8230; 8.15, 10.8, 11.2, 10, Appendice A<br />
metodi&#8230; 10.2<br />
operazioni sul&#8230; 10.1<br />
documentazione&#8230; 17.10<br />
Doyle, Arthur Conan&#8230; 1.3<br />
eccezione&#8230; 1.3, 1.6, 11.5, 11.6, Appendice A<br />
gestire&#8230; 11.5, 11.6<br />
sollevare&#8230; 11.5, 11.6<br />
elaborazione trasversale&#8230; 7.3, 7.7, 8.5<br />
di una lista&#8230; 8.2<br />
elemento&#8230; 8.17, 8<br />
elemento singolo&#8230; 17.11<br />
singolo&#8230; 17.7<br />
ereditarietà&#8230; 16.1, 16.8<br />
di semantica&#8230; 1.3, 1.6, 9.3, Appendice A<br />
di sintassi&#8230; 1.3, 1.6, Appendice A<br />
in compilazione&#8230; Appendice A<br />
in esecuzione&#8230; 1.3, 1.6, 4.11, 7.2, 7.6, 8.2, 9.1, 10.2, 10.4,<br />
10.5, 11.2, 11, Appendice A<br />
runtime&#8230; 1.3, Appendice A<br />
sintassi&#8230; Appendice A<br />
esecuzione condizionale&#8230; 4.4<br />
errore&#8230; 1.3<br />
errore in&#8230; 4.11<br />
flusso&#8230; Appendice A<br />
eseguibile&#8230; 1.6<br />
espressione&#8230; 2.6, 2.11, 18.5<br />
albero di&#8230; 20.3, 20.5<br />
booleana&#8230; 4.2, 4.13<br />
infissa&#8230; 18.5<br />
lunga&#8230; Appendice A<br />
postfissa&#8230; 18.5<br />
regolare&#8230; 18.6<br />
Euclide&#8230; Appendice B<br />
istruzione&#8230; 11.5<br />
funzione&#8230; 5.5, 5.8<br />
funzione&#8230; 5.7<br />
FIFO&#8230; 19.7, 19<br />
classe&#8230; 16.1<br />
figlio&#8230; 20<br />
file&#8230; 11.6, 11<br />
di testo&#8230; 11.1, 11.6<br />
float&#8230; 2.1<br />
flusso di esecuzione&#8230; 3.8, 3.13, Appendice A<br />
foglia&#8230; 20<br />
linguaggio&#8230; 1.4<br />
operatore&#8230; 11.2, 11.6, 19.6, Appendice A<br />
fornitore&#8230; 18.1, 18.9<br />
forzatura&#8230; 3.13<br />
di tipo&#8230; 3.3, 10.6<br />
frame di funzione&#8230; 3.11, 3.13, 4.10, 10.5<br />
frazione&#8230; Appendice B<br />
addizione&#8230; Appendice B<br />
confronto&#8230; Appendice B<br />
moltiplicazione&#8230; Appendice B<br />
funzione&#8230; 3.6, 3.13, 6.9, 14.1, 13<br />
funzione fattoriale&#8230; 5.8<br />
argomento&#8230; 3.9<br />
booleana&#8230; 5.4, 15.8<br />
chiamata&#8230; 3.1<br />
composizione&#8230; 3.5, 5.3<br />
definizione&#8230; 3.6<br />
fattoriale&#8230; 5.5<br />
Fibonacci&#8230; 5.7, 10.5<br />
gamma&#8230; 5.8<br />
matematica&#8230; 3.4<br />
modificatore&#8230; 13.3<br />
parametro&#8230; 3.9<br />
polimorfica&#8230; 14.10<br />
pura&#8230; 13.2, 13.8<br />
tupla come valore di ritorno&#8230; 9.3<br />
generalizzazione&#8230; 6.5, 6.10, 12.7, 13.6<br />
struttura di dati&#8230; 18.3, 18.4<br />
genitore&#8230; 20<br />
classe&#8230; 16.1, 16.2, 16.4<br />
serie&#8230; 6.3<br />
gestione degli errori&#8230; 20.6<br />
di un&#8217;eccezione&#8230; 11.5, 11.6<br />
Golf&#8230; 19.6<br />
grafico delle chiamate&#8230; 10.5<br />
hello world&#8230; 1.5<br />
Holmes, Sherlock&#8230; 1.3<br />
identità&#8230; 12.4<br />
immutabile&#8230; 9.1<br />
stringa&#8230; 7.6<br />
Coda&#8230; 19.1<br />
in&#8230; 8.4<br />
operatore&#8230; 15.8<br />
incapsulamento&#8230; 6.5, 6.10, 12.7, 18.1, 18.8<br />
sviluppo&#8230; 13.8<br />
incremento&#8230; 7.11<br />
indice&#8230; 7.1, 7.11, 8.17, 10, Appendice A<br />
di ciclo&#8230; 6.10<br />
negativo&#8230; 7.2<br />
IndiceError&#8230; Appendice A<br />
lista&#8230; 17.5<br />
ricorsione&#8230; 4.11, 5.8, Appendice A<br />
ciclo&#8230; 6.2, Appendice A<br />
infissa&#8230; 18.5<br />
metodo&#8230; 14.6<br />
metodo&#8230; 15.5<br />
inordine&#8230; 20.4, 20.8<br />
int&#8230; 2.1<br />
Intel&#8230; 6.3<br />
interfaccia&#8230; 18.2<br />
divisione tra&#8230; 3.3<br />
riferimento&#8230; 12.8, 17.1, 17.11<br />
lungo&#8230; 10.6<br />
interprete&#8230; 1.1, 1.6<br />
invariante&#8230; 17.10, 17.11<br />
invocazione&#8230; 10.8<br />
dei metodi&#8230; 10.2<br />
irrazionale&#8230; Appendice B<br />
istanza&#8230; 12.3, 12.6, 12.9, 14.1<br />
dell&#8217;oggetto&#8230; 12.1, 14.1, 15.3<br />
istanziazione&#8230; 12.1, 12.9<br />
istogramma&#8230; 9.8, 9.9, 10.7<br />
istruzione&#8230; 2.11<br />
istruzione di stampa&#8230; 1.6<br />
assegnazione&#8230; 2.2, 6.1<br />
blocco&#8230; 4.4<br />
break&#8230; 11.6, 11<br />
composta&#8230; 4.4, 4.13<br />
blocco di istruzioni&#8230; 4.4<br />
intestazione&#8230; 4.4<br />
condizionale&#8230; 4.13<br />
continue&#8230; 11.1, 11.6<br />
di stampa&#8230; 1.5<br />
except&#8230; 11.5, 11.6<br />
pass&#8230; 4.4<br />
print&#8230; Appendice A<br />
raise&#8230; 11.6<br />
return&#8230; 4.8, Appendice A<br />
stampa&#8230; 1.5, 1.6<br />
try&#8230; 11.5<br />
while&#8230; 6.2<br />
iterazione&#8230; 6.2, 6.10, 6<br />
funzione&#8230; 8.16<br />
KeyError&#8230; Appendice A<br />
letteralità&#8230; 1.4<br />
completo&#8230; 5.5<br />
di alto livello&#8230; 1.1, 1.6<br />
di basso livello&#8230; 1.1, 1.6<br />
di programmazione&#8230; 1.1<br />
formale&#8230; 1.4, 1.6<br />
naturale&#8230; 1.4, 1.6, 12.4<br />
orientato agli oggetti&#8230; 14.1, 14.10<br />
programmazione&#8230; 1.1<br />
sicuro&#8230; 1.3<br />
link&#8230; 17.11<br />
lista&#8230; 17.1, 17.11<br />
Linux&#8230; 1.3<br />
lista&#8230; 8.17, 17, 8<br />
annidata&#8230; 8.1, 8.14, 8.15, 8.17, 10.4       appartenenza&#8230;<br />
8.4<br />
attraversamento&#8230; 8.2, 17.3<br />
attraversamento ricorsivo&#8230; 17.4<br />
ben formata&#8230; 17.10<br />
cancellazione&#8230; 8.9<br />
ciclo&#8230; 17.5<br />
ciclo for&#8230; 8.5<br />
clonazione&#8230; 8.12<br />
come parametro&#8230; 8.13, 17.3<br />
di oggetti&#8230; 15.5<br />
elaborazione trasversale&#8230; 8.2<br />
elemento&#8230; 8.2<br />
infinita&#8230; 17.5<br />
linkata&#8230; 17.1, 17.11<br />
lunghezza&#8230; 8.3<br />
metodi&#8230; 10.7<br />
metodo&#8230; 15.5<br />
modifica&#8230; 17.7<br />
mutabile&#8230; 8.8<br />
operazioni&#8230; 8.6<br />
porzione&#8230; 8.7<br />
ripetizione&#8230; 8.6<br />
stampa&#8230; 17.3<br />
stampa invertita&#8230; 17.4<br />
classe&#8230; 17.9<br />
concatenamento&#8230; 8.6<br />
livello&#8230; 20.8, 20<br />
variabile&#8230; 3.10, 6.7<br />
logaritmo&#8230; 6.3<br />
operatore&#8230; 4.2, 4.3<br />
loop&#8230; 6.2<br />
lunghezza&#8230; 8.3<br />
maiuscolo&#8230; 7.10<br />
Make Way for Ducklings&#8230; 7.3<br />
mappare&#8230; 15.9<br />
mappatura&#8230; 15.2<br />
maschera&#8230; 18.3, 19.7<br />
massimo comune divisore&#8230; Appendice B<br />
funzione&#8230; 3.4<br />
matrice&#8230; 8.15<br />
sparsa&#8230; 10.4<br />
mazzo&#8230; 15.5<br />
McCloskey, Robert&#8230; 7.3<br />
modello&#8230; Appendice A<br />
mescolare&#8230; 15.7<br />
messaggi d&#8217;errore&#8230; Appendice A<br />
del dizionario&#8230; 10.2<br />
delle liste&#8230; 10.7<br />
metodo&#8230; 10.2, 10.8, 14.1, 14.10, 13<br />
aiutante&#8230; 17.8, 17.11<br />
append&#8230; 15.5<br />
contenitore&#8230; 17.8<br />
di inizializzaz&#8230; 14.6, 14.10<br />
di inizializzazione&#8230; 15.5<br />
invocazione&#8230; 10.2<br />
lista&#8230; 15.5<br />
minuscolo&#8230; 7.10<br />
modello mentale&#8230; Appendice A<br />
modifica di liste&#8230; 17.7<br />
modificatore&#8230; 13.3, 13.8<br />
modulo&#8230; 3.4, 3.13, 7.9<br />
copy&#8230; 12.8<br />
operatore&#8230; 4.1, 16.3<br />
string&#8230; 7.9, 7.10<br />
di frazioni&#8230; Appendice B<br />
scalare&#8230; 14.8, 14.10<br />
mutabile&#8230; 7.6, 7.11, 9.1<br />
lista&#8230; 8.8<br />
oggetto&#8230; 12.7<br />
NameError&#8230; Appendice A<br />
linguaggio&#8230; 1.4, 12.4<br />
negazione&#8230; Appendice B<br />
negazione unaria&#8230; Appendice B<br />
nodo&#8230; 17.1, 17.11, 20.8, 20<br />
classe&#8230; 17.2<br />
di albero&#8230; 20<br />
figlio&#8230; 20.8, 20<br />
foglia&#8230; 20.8, 20<br />
fratello&#8230; 20.8<br />
genitore&#8230; 20.8, 20<br />
radice&#8230; 20.8, 20<br />
ramo&#8230; 20<br />
None&#8230; 5.1, 5.9<br />
infissa&#8230; 18.5, 18.9, 20.3<br />
postfissa&#8230; 18.5, 18.9, 20.3<br />
prefissa&#8230; 20.3, 20.8<br />
punto&#8230; 3.4, 3.13, 10.2, 14.2, 14.6<br />
numeratore&#8230; Appendice B<br />
numero casuale&#8230; 9.4<br />
oggetto&#8230; 8.10, 8.17, 12.9, 12<br />
invariante&#8230; 17.10<br />
istanza&#8230; 12.1, 15.3<br />
lista di&#8230; 15.5<br />
mutabile&#8230; 12.7<br />
stampa&#8230; 12.2<br />
operando&#8230; 2.6, 2.11<br />
operatore&#8230; 2.6, 2.11<br />
binario&#8230; 20.3, 20.8<br />
condizionale&#8230; 15.4<br />
di formato&#8230; 11.2, 11.6, 19.6, Appendice A<br />
in&#8230; 8.4, 15.8<br />
logico&#8230; 4.2, 4.3<br />
matematico&#8230; Appendice B<br />
modulo&#8230; 4.1, 4.13, 16.3<br />
porzione&#8230; 7.1<br />
ridefinizione&#8230; 14.8, 14.10, Appendice B<br />
unario&#8230; Appendice B<br />
[]&#8230; 7.1<br />
su dizionario&#8230; 10.1<br />
su lista&#8230; 8.6<br />
sulle stringhe&#8230; 2.8<br />
ordinamento&#8230; 15.4<br />
completo&#8230; 15.4<br />
parziale&#8230; 15.4<br />
alfabetico&#8230; 7.3<br />
delle operazioni&#8230; 2.7<br />
di valutazione&#8230; Appendice A<br />
overflow&#8230; 10.5<br />
overloading&#8230; 14.8, 14.10<br />
parametro&#8230; 3.9, 3.13, 8.13, 12.3<br />
lista&#8230; 8.13<br />
parola riservata&#8230; 2.3, 2.11<br />
parsing&#8230; 1.4, 1.6, 18.6, 18.9, 20.5<br />
pass&#8230; 4.4<br />
pattern matching&#8230; 9.9<br />
Pentium&#8230; 6.3<br />
percorso&#8230; 11.3<br />
performance&#8230; 19.3<br />
sviluppo&#8230; 13.8<br />
piano di sviluppo&#8230; 6.10<br />
pickle&#8230; 11.6<br />
pickling&#8230; 11.4<br />
pila&#8230; 18.2<br />
classe&#8230; 18.3<br />
poesia&#8230; 1.4<br />
polimorfismo&#8230; 14.9, 14.10<br />
politica di accodamento&#8230; 19.7, 19<br />
Pop&#8230; 18.4<br />
portabilità&#8230; 1.1, 1.6<br />
porzione&#8230; 7.4, 7.11, 8.7<br />
operatore&#8230; 7.1<br />
postfissa&#8230; 18.5<br />
postordine&#8230; 20.4, 20.8<br />
precedenza&#8230; 2.11, Appendice A<br />
regole&#8230; 2.7<br />
precondizione&#8230; 17.5, 17.11<br />
prefisso&#8230; 20.4<br />
preordine&#8230; 20.4, 20.8<br />
istruzione&#8230; Appendice A<br />
priorità&#8230; 19.6<br />
di accodamento&#8230; 19<br />
prodotto&#8230; 20.5<br />
punto&#8230; 14.8, 14.10<br />
progettazione orientata agli oggetti&#8230; 16.1<br />
programma&#8230; 1.6<br />
deterministico&#8230; 9.9<br />
sviluppo&#8230; 6.10<br />
linguaggio&#8230; 1.1<br />
orientata agli oggetti&#8230; 14.1, 16.1<br />
prompt&#8230; 4.12, 4.13<br />
prosa&#8230; 1.4<br />
pseudocasuale&#8230; 9.9<br />
pseudocodice&#8230; Appendice B<br />
classe&#8230; 14.7<br />
funzione&#8230; 13.2<br />
Push&#8230; 18.4<br />
Python Library Reference&#8230; 7.10<br />
radice&#8230; 20<br />
ramificazione&#8230; 4.4, 4.5, 4.13<br />
ramo&#8230; 4.13, 20<br />
randrange&#8230; 15.7<br />
rango&#8230; 15.2<br />
razionale&#8230; Appendice B<br />
recupero dei dati&#8230; 11.4<br />
regole di precedenza&#8230; 2.7, 2.11<br />
rettangolo&#8230; 12.5<br />
istruzione&#8230; 4.8, Appendice A<br />
ricorsione&#8230; 4.9, 4.13, 5.5, 5.6, 20.2, 20.4<br />
ricorsione infinita&#8230; 4.11, 4.13, 5.8, Appendice A<br />
stato di base&#8230; 4.10<br />
su lista&#8230; 17.4<br />
definizione&#8230; 20.5<br />
funzione&#8230; 4.9<br />
struttura di dati&#8230; 17.1, 20<br />
ridefinizione&#8230; Appendice B<br />
di un operatore&#8230; 14.8, 14.10, 15.4, 19.6<br />
ridondanza&#8230; 1.4<br />
riduzione&#8230; Appendice B<br />
riferimento&#8230; 17.1<br />
alias&#8230; 8.11<br />
interno&#8230; 12.8, 17.1, 17.11, 20<br />
riga&#8230; 8.15<br />
rimuovere le carte&#8230; 15.8<br />
lista&#8230; 8.6<br />
assegnazione&#8230; 6.10<br />
ritorno a capo&#8230; 6.10<br />
errore&#8230; 1.3<br />
variabile&#8230; 17.6<br />
scambio&#8230; 15.7<br />
script&#8230; 1.6<br />
semantica&#8230; 1.3, 1.6<br />
errore&#8230; 1.3, Appendice A<br />
seme&#8230; 15.2<br />
sequenza&#8230; 8.17, 8<br />
di escape&#8230; 6.3, 6.10<br />
aritmetica&#8230; 6.3<br />
di condizioni&#8230; 4.6<br />
geometrica&#8230; 6.3<br />
linguaggio&#8230; 1.3<br />
similarità&#8230; 12.4<br />
singolo elemento&#8230; 17.8<br />
sintassi&#8230; 1.3, 1.6, Appendice A<br />
errore&#8230; 1.3<br />
sistema di conoscenze&#8230; 20.7<br />
sollevare un&#8217;eccezione&#8230; 11.5, 11.6<br />
soluzione di problemi&#8230; 1.6<br />
somma&#8230; 20.5<br />
sottoclasse&#8230; 16.1, 16.4, 16.8<br />
spazio bianco&#8230; 7.10, 7.11<br />
funzione&#8230; 8.16<br />
mano di carte&#8230; 16.4<br />
oggetto&#8230; 12.2, 14.2<br />
oggetto Mazzo&#8230; 15.6<br />
stato di base&#8230; 4.10, 4.13<br />
stile di programmazione funzionale&#8230; 13.4, 13.8<br />
modulo&#8230; 7.9, 7.10<br />
stringa&#8230; 2.1<br />
di formato&#8230; 11.2, 11.6<br />
immutabile&#8230; 7.6<br />
lunghezza&#8230; 7.2<br />
porzione&#8230; 7.4<br />
confronto&#8230; 7.5<br />
annidata&#8230; 15.1<br />
generica&#8230; 18.3, 18.4<br />
ricorsiva&#8230; 17.1, 17.11, 20<br />
sub-espressione&#8230; 20.5<br />
suggerimento&#8230; 10.5, 10.8<br />
del programma&#8230; 6.10<br />
generalizzazione&#8230; 6.5<br />
incapsulamento&#8230; 6.5<br />
incrementale&#8230; 5.2, 5.9, 13.8, Appendice A<br />
pianificato&#8230; 13.8<br />
prototipale&#8230; 13.5<br />
tabella&#8230; 6.3<br />
bidimensionale&#8230; 6.4<br />
tabulazione&#8230; 6.10<br />
TDA&#8230; 18.1, 18.8, 18.9<br />
Coda&#8230; 19.1<br />
coda&#8230; 19<br />
coda con priorità&#8230; 19.5, 19<br />
Pila&#8230; 18.2<br />
costante&#8230; 19.3, 19.7<br />
lineare&#8230; 19.3, 19.7<br />
variabile&#8230; 5.1, 5.9, Appendice A<br />
teorema amb. fond&#8230;. 17.6, 17.11<br />
Teorema di Turing &#8230; 5.5<br />
file&#8230; 11.1<br />
tipo&#8230; 2.1, 2.11<br />
astratto&#8230; 18.8, 18.9<br />
composto&#8230; 7.1, 7.11, 12.1<br />
conversione&#8230; 3.2<br />
definito dall&#8217;utente&#8230; 12.1, Appendice B<br />
di dati astratto, vedi TDA&#8230; 18.1<br />
di elaborazione&#8230; 7.7, 7.8<br />
modificatore&#8230; 13.3<br />
pura&#8230; 13.2<br />
dizionario&#8230; 10<br />
float&#8230; 2.1<br />
forzatura&#8230; 3.3, 10.6<br />
immutabile&#8230; 9.1, 9.9<br />
int&#8230; 2.1<br />
intero lungo&#8230; 10.6<br />
mutabile&#8230; 9.9<br />
stringa&#8230; 2.1<br />
tupla&#8230; 9.1<br />
virgola mobile&#8230; 2.1<br />
token&#8230; 1.6, 18.6, 18.9, 20.5<br />
traccia&#8230; 3.11, 3.13, 4.11, 11.5, Appendice A<br />
try&#8230; 11.6<br />
istruzione&#8230; 11.5<br />
tupla&#8230; 9.1, 9.3, 9.9<br />
assegnazione&#8230; 9.2, 9.9, 15.7<br />
Turing, Alan&#8230; 5.5<br />
TypeError&#8230; Appendice A<br />
uguaglianza&#8230; 12.4<br />
debole&#8230; 12.4, 12.9<br />
forte&#8230; 12.4, 12.9<br />
operatore&#8230; Appendice B<br />
valore&#8230; 2.1, 2.11, 8.10<br />
valore di ritorno&#8230; 3.1, 3.13, 5.1, 5.9, 12.6<br />
tupla&#8230; 9.3<br />
tupla&#8230; 9.3<br />
ordine&#8230; Appendice A<br />
variabile&#8230; 2.2, 2.11<br />
di ciclo&#8230; 16.3, 17.3<br />
locale&#8230; 3.10, 3.13, 6.7<br />
ruoli&#8230; 17.6<br />
temporanea&#8230; 5.1, 5.9, Appendice A<br />
virgola mobile&#8230; 2.1, 2.11, 12.1<br />
while&#8230; 6.2<br />
zurloso&#8230; 5.5<br />
operatore&#8230; 7.1</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/5/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/5/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/5/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=5&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/06/05/imparare-a-programmare/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Un libro gratuito di un ex programmatore Microsoft che spiega perchè il software libero è migliore</title>
		<link>http://informaticaetica.wordpress.com/2009/05/28/un-libro-gratuito-di-un-ex-programmatore-microsoft-che-spiega-perche-il-software-libero-e-migliore/</link>
		<comments>http://informaticaetica.wordpress.com/2009/05/28/un-libro-gratuito-di-un-ex-programmatore-microsoft-che-spiega-perche-il-software-libero-e-migliore/#comments</comments>
		<pubDate>Thu, 28 May 2009 14:13:43 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/2009/05/28/un-libro-gratuito-di-un-ex-programmatore-microsoft-che-spiega-perche-il-software-libero-e-migliore/</guid>
		<description><![CDATA[document.write(code);Miracoli dell’informatica: Keith Curtis dopo 11 anni di carriera in Microsoft, sviluppando tra l’altro su Windows e Office, ha deciso di passare dall’altra parte della barricata. Ora, anche grazie al suo libro “After the Software Wars“, spiega i motivi per &#8230; <a href="http://informaticaetica.wordpress.com/2009/05/28/un-libro-gratuito-di-un-ex-programmatore-microsoft-che-spiega-perche-il-software-libero-e-migliore/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=55&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p> document.write(code);<br />Miracoli dell’informatica: <strong>Keith Curtis dopo 11 anni di carriera in <a id="ed_Id_4" target="_blank" href="http://adv08.edintorni.net/affiliati/click/?q=software+microsoft&amp;a=6074&amp;e=4&amp;y=4&amp;j=112B2289A57BC653F33788016F43E48Fhttp%3A%2F%2Fadvertiser%2Eedintorni%2Enet%2Fredirect%2Easp%3FidG%3D4967%26idA%3D91021%26query%3Dsoftware%2Bmicrosoft%26cpk%3Di%26idU%3D218%26location%3Dhttp%253A%252F%252Ffeed%252Eedintorni%252Enet%252Fkelkoo%252Fredir%252Easp%253Ftrack%253D%2525keywordid%2525%2526url%253D%2526id%253D3560123117001it610767b1f3a0c528397781fd4accd16d%2526query%253Dsoftware%252Bmicrosoft%2526country%253Dit%2526category%253D117001%2526merchant%253D3560123%2526operator%253Dand&amp;r=&amp;x=1243519550125&amp;z=tt.lh.DC9EA081E61CECF7071B8E020DE20CB0&amp;i=336">Microsoft</a>, sviluppando tra l’altro su Windows e Office, ha deciso di passare dall’altra parte della barricata.</strong> Ora, anche grazie al suo libro “<a href="http://www.lulu.com/content/4964815" target="_blank">After the Software Wars</a>“, <strong>spiega i motivi per cui il codice opensource prevarrà sul modello proprietario imposto da <a id="ed_Id_5" target="_blank" href="http://adv08.edintorni.net/affiliati/click/?q=software+microsoft&amp;a=6074&amp;e=4&amp;y=4&amp;j=112B2289A57BC653F33788016F43E48Fhttp%3A%2F%2Fadvertiser%2Eedintorni%2Enet%2Fredirect%2Easp%3FidG%3D4967%26idA%3D91021%26query%3Dsoftware%2Bmicrosoft%26cpk%3Di%26idU%3D218%26location%3Dhttp%253A%252F%252Ffeed%252Eedintorni%252Enet%252Fkelkoo%252Fredir%252Easp%253Ftrack%253D%2525keywordid%2525%2526url%253D%2526id%253D3560123117001it610767b1f3a0c528397781fd4accd16d%2526query%253Dsoftware%252Bmicrosoft%2526country%253Dit%2526category%253D117001%2526merchant%253D3560123%2526operator%253Dand&amp;r=&amp;x=1243519550125&amp;z=tt.lh.DC9EA081E61CECF7071B8E020DE20CB0&amp;i=336">Microsoft</a> oltre 20 anni fa.</strong> Curtis è decisamente nuovo all’universo GNU/<a id="ed_Id_6" target="_blank" href="http://adv08.edintorni.net/affiliati/click/?q=linux+software&amp;a=6074&amp;e=4&amp;y=3&amp;j=15C435E7276D840B314E44811B33A149http%3A%2F%2Fadvertiser%2Eedintorni%2Enet%2Fredirect%2Easp%3FidG%3D15436%26idA%3D91021%26query%3Dlinux%2Bsoftware%26cpk%3Dk%26idU%3D218%26location%3Dhttp%253A%252F%252Ffeed%252Eedintorni%252Enet%252Fkelkoo%252Fredir%252Easp%253Ftrack%253D%2525keywordid%2525%2526url%253D%2526id%253D8202523117001itdb0f48945bf716c3ec7b55fea6ebf261%2526query%253Dlinux%252Bsoftware%2526country%253Dit%2526category%253D117001%2526merchant%253D8202523%2526operator%253Dand&amp;r=&amp;x=1243519550125&amp;z=tt.lh.DC9EA081E61CECF7071B8E020DE20CB0&amp;i=336">Linux</a>
<p>infatti lo usa dal 2004, anno in cui ha lasciato Microsoft, ma sembra<br />già aver sposato in pieno la sua causa. Secondo la sua opinione,<br /><strong>proprio a causa della presenza del software proprietario stiamo<br />vivendo gli anni più cupi della storia dell’informatica.</strong></p>
<p>Continua a leggere qui&#8230;<br /><strong></strong></p>
<p><strong></strong><a href="http://www.tuxjournal.net/?p=7997">http://www.tuxjournal.net/?p=7997</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/55/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/55/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/55/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=55&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/05/28/un-libro-gratuito-di-un-ex-programmatore-microsoft-che-spiega-perche-il-software-libero-e-migliore/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Richard Stallman e l&#8217;informatizzazione della scuola italiana con il jumpc</title>
		<link>http://informaticaetica.wordpress.com/2009/05/25/richard-stallman-e-linformatizzazione-della-scuola-italiana-con-il-jumpc/</link>
		<comments>http://informaticaetica.wordpress.com/2009/05/25/richard-stallman-e-linformatizzazione-della-scuola-italiana-con-il-jumpc/#comments</comments>
		<pubDate>Mon, 25 May 2009 08:42:25 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[software libero]]></category>
		<category><![CDATA[Laptop]]></category>
		<category><![CDATA[Netbook]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=53</guid>
		<description><![CDATA[Con il coordinamento della Fondazione Mondo Digitale e il contributo di Intel e Olidata, il 196° Circolo di didattico e l’istituto comprensivo Fratelli Cervi sono le due scuole romane che nell’a.s. 2008-2009 hanno avviato la sperimentazione didattica del computer portatile &#8230; <a href="http://informaticaetica.wordpress.com/2009/05/25/richard-stallman-e-linformatizzazione-della-scuola-italiana-con-il-jumpc/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=53&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Con il coordinamento della Fondazione Mondo Digitale e il contributo di Intel e Olidata, il 196° Circolo di didattico e l’istituto comprensivo Fratelli Cervi sono le due scuole romane che nell’a.s. 2008-2009 hanno avviato la sperimentazione didattica del computer portatile <a class="zem_slink" title="Classmate PC" rel="wikipedia" href="http://en.wikipedia.org/wiki/Classmate_PC">JumPC</a>.<br />
Ad ogni bambino, per seguire la lezione a scuola e studiare a casa, è stato donato un JumPc, uno speciale computer portatile pensato per i bambini dai 6 ai 12 anni. Il <a class="zem_slink" title="Netbook" rel="wikipedia" href="http://en.wikipedia.org/wiki/Netbook">netbook</a> ha la forma di una valigetta e le dimensioni di una piccola cartella. È leggero, pesa meno di 1 kg, ma robusto: resiste agli urti, la tastiera è impermeabile e la connessione ad <a class="zem_slink" title="Internet" rel="wikipedia" href="http://en.wikipedia.org/wiki/Internet">Internet</a> è senza filo.</p>
<p>Vediamo cosa ne pensa Richard Stallman</p>
<div class="zemanta-pixie" style="margin-top:10px;height:15px;"><a class="zemanta-pixie-a" title="Reblog this post [with Zemanta]" href="http://reblog.zemanta.com/zemified/9b0a53b9-9083-4325-be6a-d7e21e7e009b/"><img class="zemanta-pixie-img" style="border:medium none;float:right;" src="http://img.zemanta.com/reblog_e.png?x-id=9b0a53b9-9083-4325-be6a-d7e21e7e009b" alt="Reblog this post [with Zemanta]" /></a><span class="zem-script more-related pretty-attribution"></span></div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/53/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/53/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/53/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=53&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/05/25/richard-stallman-e-linformatizzazione-della-scuola-italiana-con-il-jumpc/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>

		<media:content url="http://img.zemanta.com/reblog_e.png?x-id=9b0a53b9-9083-4325-be6a-d7e21e7e009b" medium="image">
			<media:title type="html">Reblog this post [with Zemanta]</media:title>
		</media:content>
	</item>
		<item>
		<title>Che cos&#8217;è Linux, e perché tutti ne parlano</title>
		<link>http://informaticaetica.wordpress.com/2009/04/24/che-cose-linux-e-perche-tutti-ne-parlano/</link>
		<comments>http://informaticaetica.wordpress.com/2009/04/24/che-cose-linux-e-perche-tutti-ne-parlano/#comments</comments>
		<pubDate>Fri, 24 Apr 2009 08:21:12 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[guide e manuali]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[ubuntu 9.04]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=50</guid>
		<description><![CDATA[Dopo l&#8217;uscita della versione Ubuntu 9.04, riscopriamo cos&#8217;è linux e perchè tutti ne parlano Da Windows a Linux Guida a Linux per utenti Windows insoddisfatti Fra il 1999 e il 2000 (con qualche ritocco fino al 2003), io e Roberto &#8230; <a href="http://informaticaetica.wordpress.com/2009/04/24/che-cose-linux-e-perche-tutti-ne-parlano/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=50&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Dopo l&#8217;uscita della versione Ubuntu 9.04, riscopriamo cos&#8217;è linux e perchè tutti ne parlano</p>
<blockquote><p><a title="http://www.attivissimo.net/other_books/w2l1/index.htm" href="http://www.attivissimo.net/other_books/w2l1/index.htm">Da Windows a Linux</a></p>
<p><a title="http://www.attivissimo.net/other_books/w2l1/index.htm" href="http://www.attivissimo.net/other_books/w2l1/index.htm"><em>Guida a Linux per utenti Windows insoddisfatti</em></a></p>
<h1></h1>
</blockquote>
<blockquote><p>Fra il 1999 e il 2000 (con qualche ritocco fino al 2003), io e Roberto &#8220;Odo&#8221; Odoardi abbiamo scritto un libro che spiega in modo semplice e informale <span style="font-weight:bold;">che cos&#8217;è Linux</span> e <span style="font-weight:bold;">come migrare da Windows (95/98/ME) a Linux</span>. Il libro è stato pubblicato su carta dall&#8217;editore Apogeo. Quello che trovate qui, con il permesso dell&#8217;editore, è il suo testo <span style="font-weight:bold;">integrale</span> con alcune reimpaginazioni e aggiornamenti non presenti nell&#8217;edizione cartacea.</p></blockquote>
<p><a href="http://www.informaticaetica.com/wp-content/uploads/2009/04/02.pdf">Scaricate questo capitolo del libro da Windows a Linux</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/50/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/50/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/50/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=50&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/04/24/che-cose-linux-e-perche-tutti-ne-parlano/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Scaricare un programma per modificare le foto: Gimp, libero e gratuito</title>
		<link>http://informaticaetica.wordpress.com/2009/04/19/scaricare-un-programma-per-modificare-le-foto-gimp-libero-e-gratuito/</link>
		<comments>http://informaticaetica.wordpress.com/2009/04/19/scaricare-un-programma-per-modificare-le-foto-gimp-libero-e-gratuito/#comments</comments>
		<pubDate>Sun, 19 Apr 2009 06:09:14 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[aiuto dalla comunità]]></category>
		<category><![CDATA[fotoritocco]]></category>
		<category><![CDATA[guide e manuali]]></category>
		<category><![CDATA[software libero]]></category>
		<category><![CDATA[videoguide]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=34</guid>
		<description><![CDATA[Gimp, come suggerisce wikipedia&#8230; GIMP (GNU Image Manipulation Program) è un programma libero di fotoritocco che permette di creare e modificare immagini bitmap. Un utilizzo tipico include la creazione di grafici e loghi, ridimensionamento e cropping di foto, alterazione o &#8230; <a href="http://informaticaetica.wordpress.com/2009/04/19/scaricare-un-programma-per-modificare-le-foto-gimp-libero-e-gratuito/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=34&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Gimp, come suggerisce wikipedia&#8230;</p>
<p><strong>GIMP</strong> (<strong><a title="GNU" href="http://it.wikipedia.org/wiki/GNU">GNU</a> Image Manipulation Program</strong>) è un <a title="Software libero" href="http://it.wikipedia.org/wiki/Software_libero">programma libero</a> di <a title="Ritocco fotografico" href="http://it.wikipedia.org/wiki/Ritocco_fotografico">fotoritocco</a> che permette di creare e modificare <a title="Grafica raster" href="http://it.wikipedia.org/wiki/Grafica_raster">immagini bitmap</a>. Un utilizzo tipico include la creazione di grafici e <a class="mw-redirect" title="Logo (grafica)" href="http://it.wikipedia.org/wiki/Logo_%28grafica%29">loghi</a>, ridimensionamento e <a class="new" title="Crop (pagina inesistente)" href="http://it.wikipedia.org/w/index.php?title=Crop&amp;action=edit&amp;redlink=1">cropping</a> di foto, alterazione o ritocco dei colori, unione o sovrapposizione di molte immagini in una sola, eliminazione di particolari non desiderati e conversione tra <a title="Formato di file" href="http://it.wikipedia.org/wiki/Formato_di_file">formati</a>.<sup class="reference"><a href="http://it.wikipedia.org/wiki/Gimp#cite_note-0">[1]</a></sup></p>
<p>Questi sono i link utili per scaricare ed utilizzare il programma e partecipare alle comunità che seguono il progetto</p>
<ul>
<li><a href="http://gimp.linux.it/www/">GIMP.it</a>
<ul>
<li><a href="http://gimp.linux.it/www/doc-home.html">manuale in italiano più aggiornato</a></li>
</ul>
</li>
<li><a href="http://www.istitutomajorana.it/index.php?option=com_content&amp;task=view&amp;id=421&amp;Itemid=33/">GIMP 2.6.6 facile con 25 videoguide</a></li>
<li><a href="http://gimpitalia.it/">Gimp Italia :: Home</a></li>
<li> <a href="http://www.gimp-italia.org/">www.Gimp-Italia.org &#8211; Home</a></li>
<li><a href="http://www.volalibero.it/gimp.html">GIMP | Volalibero.it</a></li>
<li><a href="http://docs.gimp.org/it/">Manuale online di Gimp</a></li>
</ul>
<p><a title="consulenza aziendale avanzata" href="http://www.micromercati.com">Marco Costanzo</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/34/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/34/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/34/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/34/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/34/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/34/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/34/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/34/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=34&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/04/19/scaricare-un-programma-per-modificare-le-foto-gimp-libero-e-gratuito/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Libro da regalare agli amici: Cavalieri, Ubuntu per tutti</title>
		<link>http://informaticaetica.wordpress.com/2009/04/06/libro-da-regalare-agli-amici-cavalieri-ubuntu-per-tutti/</link>
		<comments>http://informaticaetica.wordpress.com/2009/04/06/libro-da-regalare-agli-amici-cavalieri-ubuntu-per-tutti/#comments</comments>
		<pubDate>Tue, 07 Apr 2009 02:18:46 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[guide e manuali]]></category>
		<category><![CDATA[manuale ubuntu]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=32</guid>
		<description><![CDATA[Frutto del lavoro comune intrapreso da Arci, Comune di Modena e Stampa Alternativa, questo volume spiega tutti su installazione e utilizzo di UBUNTU &#8211; il sistema operativo basato su Linux che rappresenta una delle più importanti e affermate novità informatiche &#8230; <a href="http://informaticaetica.wordpress.com/2009/04/06/libro-da-regalare-agli-amici-cavalieri-ubuntu-per-tutti/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=32&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Frutto del lavoro comune intrapreso da Arci, Comune di Modena e Stampa Alternativa, questo volume spiega tutti su installazione e utilizzo di UBUNTU &#8211; il sistema operativo basato su Linux che rappresenta una delle più importanti e affermate novità informatiche degli ultimi anni. Il sistema è oggi pienamente disponibile per un uso multipiattaforma &#8211; Intel x86 IBM-PC compatibili, AMD64 e PowerPC Apple iBook e Powerbook, G4 e G5 &#8211; e aderendo pienamente ai principi del software Free e Open Source, fornisce una vastissima gamma di software liberamente fruibile e dal codice aperto. Inoltre l’utente Ubuntu può sempre contare su una vasta comunità online che fornisce supporto attraverso forum, guide e siti, oltre a poter mantenere ad esempio sia Ubuntu che Microsoft Windows sul medesimo PC, per decidere ad ogni avvio, e senza alcuna difficoltà, con quale sistema lavorare. Un manuale completo, agile e adatto sia ai nuovi utenti che a quelli più scafati.</p>
<p>via <a href="http://www.stampalternativa.it/liberacultura/?p=187">Libera Cultura, Libera Conoscenza » Cavalieri, Ubuntu per tutti</a>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/32/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/32/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/32/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=32&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/04/06/libro-da-regalare-agli-amici-cavalieri-ubuntu-per-tutti/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Software per la scuola So.Di.Linux 6&#215;3</title>
		<link>http://informaticaetica.wordpress.com/2009/03/19/software-per-la-scuola-sodilinux-6x3/</link>
		<comments>http://informaticaetica.wordpress.com/2009/03/19/software-per-la-scuola-sodilinux-6x3/#comments</comments>
		<pubDate>Thu, 19 Mar 2009 11:38:24 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[aiuto dalla comunità]]></category>
		<category><![CDATA[software didattico]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=30</guid>
		<description><![CDATA[La distribuzione Linux So.Di.Linux contiene software didattico di alto valore&#8230; Per ciascun titolo, viene indicato il tipo di documento disponibile manuale, tutoriale, guida, esempi, presentazione e note d&#8217;uso, la lingua in cui è scritto e il link per aprire/scaricare il &#8230; <a href="http://informaticaetica.wordpress.com/2009/03/19/software-per-la-scuola-sodilinux-6x3/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=30&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>La distribuzione Linux So.Di.Linux contiene software didattico di alto valore&#8230;</p>
<p>Per ciascun titolo, viene indicato il tipo di documento disponibile manuale, tutoriale, guida, esempi, presentazione e note d&#8217;uso, la lingua in cui è scritto e il link per aprire/scaricare il file corrispondente.</p>
<p>leggi su  <a href="http://sodilinux.itd.cnr.it/sdl6x3/index.php?stile=cl&amp;sez=5">So.Di.Linux 6&#215;3</a>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/30/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/30/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/30/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=30&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/03/19/software-per-la-scuola-sodilinux-6x3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Soluzione del bug nella gestione della rete in Ubuntu 8.10</title>
		<link>http://informaticaetica.wordpress.com/2009/03/18/soluzione-del-bug-nella-gestione-della-rete-in-ubuntu-810/</link>
		<comments>http://informaticaetica.wordpress.com/2009/03/18/soluzione-del-bug-nella-gestione-della-rete-in-ubuntu-810/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 14:58:52 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[aiuto dalla comunità]]></category>
		<category><![CDATA[gestione della rete]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=28</guid>
		<description><![CDATA[La meravigliosa distribuzione Ubuntu 8.10 contiene un piccolo errore che rende difficile la configurazione dei pc nelle reti con indirizzo statico. Il network manager predefenito non &#8220;ricorda&#8221; gli indirizzi assegnati. La comunità Ubuntu viene in aiuto e nel forum è &#8230; <a href="http://informaticaetica.wordpress.com/2009/03/18/soluzione-del-bug-nella-gestione-della-rete-in-ubuntu-810/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=28&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>La meravigliosa distribuzione Ubuntu 8.10 contiene un piccolo errore che rende difficile la configurazione dei pc nelle reti con indirizzo statico.</p>
<p>Il network manager predefenito non &#8220;ricorda&#8221; gli indirizzi assegnati.</p>
<p>La comunità Ubuntu viene in aiuto e nel forum è già pronto un hacking per risolvere il problema&#8230;</p>
<p>La soluzione la trovate qui</p>
<p><a href="http://forum.ubuntu-it.org/index.php/topic,229387.msg1584066.html">http://forum.ubuntu-it.org/index.php/topic,229387.msg1584066.html</a></p>
<p>Buon divertimento con il vostro sistema libero!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/28/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/28/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/28/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/28/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/28/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/28/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/28/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/28/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=28&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/03/18/soluzione-del-bug-nella-gestione-della-rete-in-ubuntu-810/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Pensare da informatico: un libro da leggere</title>
		<link>http://informaticaetica.wordpress.com/2009/03/18/pensare-da-informatico-un-libro-da-leggere/</link>
		<comments>http://informaticaetica.wordpress.com/2009/03/18/pensare-da-informatico-un-libro-da-leggere/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 09:32:36 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[guide e manuali]]></category>
		<category><![CDATA[guide]]></category>
		<category><![CDATA[informatica]]></category>
		<category><![CDATA[informatica etica]]></category>
		<category><![CDATA[libri]]></category>
		<category><![CDATA[manuali]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=23</guid>
		<description><![CDATA[Chi utilizza i computer spesso non conosce nulla di programmazione. Chi legge un libro di programmazione spesso non capisce a fondo cosa l&#8217;autore cerca di comunicare perchè essere esperti non vuol dire saper divulgare la propria esperienza. Questo è un &#8230; <a href="http://informaticaetica.wordpress.com/2009/03/18/pensare-da-informatico-un-libro-da-leggere/">Leggi l'articolo completo <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=23&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Chi utilizza i computer spesso non conosce nulla di programmazione.</p>
<p>Chi legge un libro di programmazione spesso non capisce a fondo cosa l&#8217;autore cerca di comunicare perchè essere esperti non vuol dire saper divulgare la propria esperienza.</p>
<p>Questo è un libro per principianti scritto da un educatore che vi farà amare la programmazione ed il software libero.</p>
<p>Buona lettura.</p>
<p>Per qualsiasi suggerimento lasciate un commento.</p>
<p>Scarica il libro:</p>
<p><a href="http://www.informaticaetica.com/wp-content/uploads/2009/03/pensare-da-informatico.pdf">pensare-da-informatico</a></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/23/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/23/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/23/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=23&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/03/18/pensare-da-informatico-un-libro-da-leggere/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
		<item>
		<title>Mailing List ubuntu Italia</title>
		<link>http://informaticaetica.wordpress.com/2009/03/04/mailing-list-ubuntu-italia/</link>
		<comments>http://informaticaetica.wordpress.com/2009/03/04/mailing-list-ubuntu-italia/#comments</comments>
		<pubDate>Wed, 04 Mar 2009 14:59:42 +0000</pubDate>
		<dc:creator>marco costanzo</dc:creator>
				<category><![CDATA[aiuto dalla comunità]]></category>
		<category><![CDATA[mailing list]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://www.informaticaetica.com/?p=21</guid>
		<description><![CDATA[Se avete installato il sistema operativo Ubuntu e volete un supporto gratuito, il primo passo da compiere è iscriversi alla mailing list degli utenti italiani di Ubuntu. Potete farlo seguendo questo link Mailing List Ubuntu.<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=21&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Se avete installato il sistema operativo Ubuntu e volete un supporto gratuito, il primo passo da compiere è iscriversi alla mailing list degli utenti italiani di Ubuntu.</p>
<p>Potete farlo seguendo questo link <a href="https://lists.ubuntu.com/#Localization+Lists">Mailing List Ubuntu</a>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/informaticaetica.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/informaticaetica.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/informaticaetica.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/informaticaetica.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/informaticaetica.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/informaticaetica.wordpress.com/21/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/informaticaetica.wordpress.com/21/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/informaticaetica.wordpress.com/21/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=informaticaetica.wordpress.com&amp;blog=364488&amp;post=21&amp;subd=informaticaetica&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://informaticaetica.wordpress.com/2009/03/04/mailing-list-ubuntu-italia/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/836b652ead8230ea25c213d4a455daf4?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">costanzo</media:title>
		</media:content>
	</item>
	</channel>
</rss>
