<?xml version="1.0" encoding="UTF-8"?><!-- generator="wordpress.com" -->
<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/"
	>

<channel>
	<title>programmare &amp;laquo; WordPress.com Tag Feed</title>
	<link>http://en.wordpress.com/tag/programmare/</link>
	<description>Feed of posts on WordPress.com tagged "programmare"</description>
	<pubDate>Mon, 07 Dec 2009 18:06:21 +0000</pubDate>

	<generator>http://en.wordpress.com/tags/</generator>
	<language>en</language>

<item>
<title><![CDATA[Notepad ++]]></title>
<link>http://pejone.wordpress.com/2009/11/12/notepad/</link>
<pubDate>Thu, 12 Nov 2009 16:54:02 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/11/12/notepad/</guid>
<description><![CDATA[Notepad ++ è un editor che supporta decine di linguaggi di programmazione (html, javascript, java, c]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Notepad ++ è un editor che supporta decine di linguaggi di programmazione (html, javascript, java, c, c++, python, php, css ecc). Multi documento e configurabile, vi permette di utilizzare stili e indentazione propri di ogni linguaggio. Consente ricerche multiple anche attraverso le espressioni regolari.<br />
Uno strumento completo per ogni sviluppatore!</p>
<p style="text-align:center;"><img class="aligncenter" title="Notepad++" src="http://img11.imageshack.us/img11/260/notepadr.png" alt="" width="450" height="370" /></p>
<p>Scarica dal sito originale: <a href="http://notepad-plus.sourceforge.net/it/site.htm" target="_blank">http://notepad-plus.sourceforge.net/it/site.htm</a></p>
<p>Disponibile anche in versione portabile al link: <a href="http://notepad-portable.softonic.it/" target="_blank">http://notepad-portable.softonic.it/</a></p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><a href="http://pejone.wordpress.com/2009/11/10/eclipse/" target="_self">Eclipse</a> <span style="color:#333333;">(ambiente di sviluppo integrato per la programmazione)</span> </li>
<li><a href="http://pejone.wordpress.com/2009/11/09/eje/" target="_self">Eje</a> <span style="color:#333333;">(completo editor per progammare in Java)</span></li>
</ul>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Eclipse]]></title>
<link>http://pejone.wordpress.com/2009/11/10/eclipse/</link>
<pubDate>Tue, 10 Nov 2009 09:55:50 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/11/10/eclipse/</guid>
<description><![CDATA[Eclipse è un completo ambiente di sviluppo integrato (IDE) che supporta diversi linguaggi di program]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Eclipse è un completo ambiente di sviluppo integrato (IDE) che supporta diversi linguaggi di programmazione.<br />
E&#8217; possibile gestire facilmente Java e C++ nonchè, attraverso opportuni plug-in, PHP e XML.<br />
Include innumerevoli tools per lo sviluppo, quali compilatore, completamento automatico del codice, suggerimenti, CVS ecc.<br />
Eclipse consente di realizzare interfacce grafiche (anche per dispositivi portatili) in un ambiente multipiattaforma e Open Source.</p>
<p style="text-align:center;"><img class="aligncenter" title="Eclipse" src="http://img132.imageshack.us/img132/6048/eclipsei.png" alt="" width="450" height="324" /></p>
<p>Scarica dal sito originale: <a href="http://www.eclipse.org/downloads/" target="_blank">http://www.eclipse.org/downloads/</a></p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"></p>
<li><a href="http://pejone.wordpress.com/2009/11/09/eje/" target="_self">Eje</a> (completo editor per progammare in Java)</li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/09/java-development-kit/" target="_self">Jdk</a> (kit essenziale di sviluppo Java)</span></span></span></span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/09/java-manuale-completo/" target="_self">Java</a> (manuale completo al linguaggio di programmazione)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/12/notepad/" target="_self">Notepad ++</a> (il notepad con tante marce in più!)</span></span></span></span></span></span></li>
<p></span></span></span></span></span></span></ul>
<p><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"> </span></span></span></span></span></span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Eje]]></title>
<link>http://pejone.wordpress.com/2009/11/09/eje/</link>
<pubDate>Mon, 09 Nov 2009 12:29:39 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/11/09/eje/</guid>
<description><![CDATA[Si tratta di un semplice quanto potente editor Java. Ideale per chi vuole imparare questo linguaggio]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Si tratta di un semplice quanto potente editor Java. Ideale per chi vuole imparare questo linguaggio ma non abbisogna di un complesso ambiente di sviluppo. Tutto in italiano.</p>
<p style="text-align:center;"><img class="aligncenter" title="Eje" src="http://img524.imageshack.us/img524/5765/eje.png" alt="" width="450" height="326" /></p>
<p>Scarica dal sito originale: <a href="http://www.claudiodesio.com/eje_it.htm" target="_blank">http://www.claudiodesio.com/eje_it.htm</a></p>
<p>Dello stesso autore è disponibile un esauriente manuale sul linguaggio Java scaricabile gratuitamente qui.</p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/10/eclipse/" target="_self">Eclipse</a> (ambiente di sviluppo integrato per la programmazione)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/09/java-manuale-completo/" target="_self">Java</a> (manuale completo al linguaggio di programmazione)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/09/java-development-kit/" target="_self">Jdk</a> (kit essenziale di sviluppo Java)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/12/notepad/" target="_self">Notepad ++</a> (il notepad con tante marce in più!)</span></span></span></span></span></span></li>
</ul>
<p><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"> </span></span></span></span></span></span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Java manuale completo]]></title>
<link>http://pejone.wordpress.com/2009/11/09/java-manuale-completo/</link>
<pubDate>Mon, 09 Nov 2009 12:29:04 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/11/09/java-manuale-completo/</guid>
<description><![CDATA[Un esauriente guida in italiano scritta dall&#8217;esperto sviluppatore Claudio De Sio Cesari sul li]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Un esauriente guida in italiano scritta dall&#8217;esperto sviluppatore Claudio De Sio Cesari sul linguaggio orientato ad oggetti e multipiattaforma Java. Giunto alla versione 6, questo manuale vi introdurrà passo passo a tutti i segreti della programmazione in Java.<br />
Oltre 700 pagine in formato elettronico stampabile.</p>
<p style="text-align:center;"> <img class="aligncenter" title="GuidaJava" src="http://img9.imageshack.us/img9/8669/manualejava.png" alt="" width="197" height="260" /></p>
<p>Quì disponibile in un comodo formato pdf totalmente gratuito per gentile concessione dell’autore.</p>
<p>Scarica il manuale: <a href="http://www.claudiodesio.com/download/oo_&#38;&#38;_java_5.zip" target="_blank">http://www.claudiodesio.com/download/oo_&#38;&#38;_java_5.zip</a> (il file “pesa” oltre 5 Mb dunque non preoccuparti se il download ti sembra lento a partire)</p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /><br />
<span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/10/eclipse/" target="_self">Eclipse</a> (ambiente di sviluppo integrato per la programmazione)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/09/eje/" target="_self">Eje</a> (completo editor per progammare in Java)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/11/09/java-development-kit/" target="_self">Jdk</a> (kit essenziale di sviluppo Java)</span></span></span></span></span></span></li>
</ul>
<p><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"> </span></span></span></span></span></span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Java Development Kit]]></title>
<link>http://pejone.wordpress.com/2009/11/09/java-development-kit/</link>
<pubDate>Mon, 09 Nov 2009 12:27:31 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/11/09/java-development-kit/</guid>
<description><![CDATA[Java Development Kit è una raccolta di strumenti per sviluppatori Java che comprende il Java Runtime]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Java Development Kit è una raccolta di strumenti per sviluppatori Java che comprende il Java Runtime Environment quale ambiente di esecuzione per applicazioni scritte in questo linguaggio.<br />
Questo software vi mette a disposizione tutto ciò che vi serve per sviluppare applicazioni e applet nel linguaggio multipiattaforma  Java.</p>
<p style="text-align:center;"><img class="aligncenter" title="Jsdk" src="http://img21.imageshack.us/img21/9274/javalogo.jpg" alt="" width="335" height="336" /></p>
<p>Scarica dal sito originale: <a href="http://java.sun.com/javase/downloads/index.jsp#need" target="_blank">http://java.sun.com/javase/downloads/index.jsp#need</a></p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"></p>
<li><a href="http://pejone.wordpress.com/2009/11/10/eclipse/" target="_self">Eclipse</a> (ambiente di sviluppo integrato per la programmazione)</li>
<li><a href="http://pejone.wordpress.com/2009/11/09/eje/" target="_self">Eje</a> (completo editor per progammare in Java)</li>
<li><a href="http://pejone.wordpress.com/2009/11/09/java-manuale-completo/" target="_self">Java</a> (manuale completo al linguaggio di programmazione)</li>
<p>&#160;</p>
<p></span></span></span></span></span></span></ul>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[CalTweet: programmare ed organizzare eventi via Twitter o Facebook]]></title>
<link>http://jacopofarina.wordpress.com/2009/10/30/caltweet-programmare-ed-organizzare-eventi-via-twitter-o-facebook/</link>
<pubDate>Thu, 29 Oct 2009 23:03:37 +0000</pubDate>
<dc:creator>jacopofarina</dc:creator>
<guid>http://jacopofarina.wordpress.com/2009/10/30/caltweet-programmare-ed-organizzare-eventi-via-twitter-o-facebook/</guid>
<description><![CDATA[CalTweet é un servizio che consente di programmare ed organizzare eventi via Twitter e Facebook. Bas]]></description>
<content:encoded><![CDATA[CalTweet é un servizio che consente di programmare ed organizzare eventi via Twitter e Facebook. Bas]]></content:encoded>
</item>
<item>
<title><![CDATA[Guida a Firebug]]></title>
<link>http://pejone.wordpress.com/2009/10/15/guida-a-firebug/</link>
<pubDate>Thu, 15 Oct 2009 12:08:50 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/10/15/guida-a-firebug/</guid>
<description><![CDATA[Eccovi una rapida ma esaustiva guida a Firebug, l&#8217;add-on di Firefox che vi mette a disposizion]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Eccovi una rapida ma esaustiva guida a Firebug, l&#8217;add-on di Firefox che vi mette a disposizione preziosi strumenti per lo svilluppo e il debug delle pagine web.</p>
<p style="text-align:center;"><img class="aligncenter" title="Guidafirebug" src="http://img208.imageshack.us/img208/3279/firebug.jpg" alt="" width="162" height="162" /></p>
<p>Accedi alla guida: <a href="http://www.blographik.it/2009/05/27/guida-firebug/" target="_blank">http://www.blographik.it/2009/05/27/guida-firebug/</a></p>
<p>Firebug è disponibile qui: <a href="http://pejone.wordpress.com/2009/10/15/firebug/" target="_self">http://pejone.wordpress.com/2009/10/15/firebug/</a><br />
<span style="color:#ffffff;">.</span></p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/10/15/firebug/" target="_self">Firebug</a> (permette modifica e debug delle pagine web)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/09/02/firefox/" target="_self">Firefox</a> (il browser web espandibile)</span></span></span></span></span></span></li>
<li><a href="http://pejone.wordpress.com/2009/09/25/httrack/" target="_self">Httrack</a> <span style="color:#333333;">(permette di scaricare interi siti web)</span></li>
</ul>
<p><span style="color:#333333;"> </span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Firebug]]></title>
<link>http://pejone.wordpress.com/2009/10/15/firebug/</link>
<pubDate>Thu, 15 Oct 2009 12:08:03 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/10/15/firebug/</guid>
<description><![CDATA[Questa estensione di Firefox è davvero un potentissimo strumento per gli svilluppatori web. Firebug ]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Questa estensione di Firefox è davvero un potentissimo strumento per gli svilluppatori web. Firebug permette infatti il l&#8217;analisi, il debug e la modifica di ogni elemento di una pagina web.<br />
Compresi fogli di stile css, codice html, javascript e struttura DOM.<br />
Firebug permette di misurare le performance di caricamento di un sito web in ogni suo elemento attraverso la funzione net.<br />
Davvero indispensabile al web-designer!</p>
<p style="text-align:center;"><img class="aligncenter" title="Firebug" src="http://img115.imageshack.us/img115/7499/firebugsplash.png" alt="" width="420" height="254" /></p>
<p>Scarica dal sito originale: <a href="http://getfirebug.com/" target="_blank">http://getfirebug.com/</a></p>
<p>Per utilizzare al meglio Firebug è disponibile una guida <a href="http://pejone.wordpress.com/2009/10/15/guida-a-firebug/" target="_self">cliccando qui</a>.<span style="color:#ffffff;">.</span></p>
<p>Se ancora non disponete di Firefox potete scaricarlo qui: <a href="http://pejone.wordpress.com/2009/09/02/firefox/" target="_blank">http://pejone.wordpress.com/2009/09/02/firefox/</a><br />
<img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /><br />
<span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/10/15/guida-a-firebug/" target="_self">Guida a Firebug</a> (tutorial per utilizzare l’add-on del web designer)</span></span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/09/02/firefox/" target="_self">Firefox</a> (il browser web espandibile)</span></span></span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/09/25/httrack/" target="_self">Httrack</a> (permette di scaricare interi siti web)</span></span></span></span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/pejone.wordpress.com/2009/08/11/siti-utili-per-il-web/" target="_self">Siti utili per il web</a> (siti informativi indispensabili per il web design)</span></span></span></span></span></span></span></span></span></span></li>
</ul>
<p><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"> </span></span></span></span></span></span></span></span></span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Oggi nasce risca.py e risca.math]]></title>
<link>http://riscadeb.wordpress.com/2009/10/13/oggi-nasce-risca-py/</link>
<pubDate>Tue, 13 Oct 2009 12:55:45 +0000</pubDate>
<dc:creator>risca.altervista.org</dc:creator>
<guid>http://riscadeb.wordpress.com/2009/10/13/oggi-nasce-risca-py/</guid>
<description><![CDATA[Ultima novità, oltre al mondo di risca.deb &#8211; incentrato sul sistema operativo universale DEBIA]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Ultima novità, oltre al mondo di risca.deb &#8211; incentrato sul sistema operativo universale DEBIAN &#8211; nasce oggi <a href="http://riscapy.wordpress.com">risca.py</a>, un sito dedicato a python ed alla programmazione e <a href="http://riscamath.wordpress.com">risca.math</a>, un sito interamente dedicato alle scienze matematiche.<br />
Buona navigazione a tutti i lettori!</p>
<p>risca</p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Taweet: programmare tweets per il futuro]]></title>
<link>http://jacopofarina.wordpress.com/2009/10/04/taweet-programmare-tweets-per-il-futuro/</link>
<pubDate>Sat, 03 Oct 2009 23:01:41 +0000</pubDate>
<dc:creator>jacopofarina</dc:creator>
<guid>http://jacopofarina.wordpress.com/2009/10/04/taweet-programmare-tweets-per-il-futuro/</guid>
<description><![CDATA[Taweet é un servizio che consente di programmare i propri tweets nel futuro. E&#8217; possibile acce]]></description>
<content:encoded><![CDATA[Taweet é un servizio che consente di programmare i propri tweets nel futuro. E&#8217; possibile acce]]></content:encoded>
</item>
<item>
<title><![CDATA[Httrack]]></title>
<link>http://pejone.wordpress.com/2009/09/25/httrack/</link>
<pubDate>Fri, 25 Sep 2009 14:16:12 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/09/25/httrack/</guid>
<description><![CDATA[Se volete scaricare un intero sito web e poi studiarvelo comodamente off-line, Httrack è il programm]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Se volete scaricare un intero sito web e poi studiarvelo comodamente off-line, Httrack è il programma che fa per voi.<br />
Questo software vi permette infatti di scaricare in locale un intero sito web!</p>
<p style="text-align:center;"><img class="aligncenter" title="Httrack" src="http://img523.imageshack.us/img523/8200/httrack.png" alt="" width="420" height="322" /></p>
<p>Scarica dal sito originale: <a href="http://www.httrack.com/" target="_blank">http://www.httrack.com/</a><br />
<img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/07/16/file-zilla/" target="_self">File Zilla</a> <span style="color:#333333;">(pratico client Ftp)</span></span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/06/23/free-hosting-e-spaces/" target="_self">Free Hosting e Space</a> (spazi per il tuo sito/blog e non solo)</span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/07/28/servizi-e-widget-web/" target="_self">Servizi e widget web </a>(servizi e “accessori” per il tuo sito/blog)</span></span></li>
</ul>
<p><span style="color:#333333;"><span style="color:#333333;"> </span></span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Siti utili per lo svilluppo web]]></title>
<link>http://pejone.wordpress.com/2009/08/11/siti-utili-per-il-web/</link>
<pubDate>Tue, 11 Aug 2009 13:31:52 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/08/11/siti-utili-per-il-web/</guid>
<description><![CDATA[Ecco una pratica lista di tools e di siti che vi possono tornare utili se avete bisogno di informazi]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Ecco una pratica lista di tools e di siti che vi possono tornare utili se avete bisogno di informazioni durante la realizzazione e la pubblicazione di siti web. </p>
<p style="text-align:center;"><img class="aligncenter" title="Siti utili per il web" src="http://img33.imageshack.us/img33/1200/webdesignmain.jpg" alt="" width="336" height="307" /></p>
<p>Servizi on line:</p>
<ul>
<li><a href="http://validator.w3.org/checklink" target="_blank">Link Checker</a> (strumento che verifica la validità dei links presenti sul tuo sito)</li>
<li><a href="http://jigsaw.w3.org/css-validator/" target="_blank">Validatore W3C</a> (uno strumento che ti permette di formattare le tue pagine web secondo i criteri del W3C)</li>
</ul>
<p>Siti e risorse di informazioni utili per il webmaster:</p>
<ul>
<li><a href="http://www.w3c.it/" target="_blank">W3C</a> (il sito italiano del World Wide Web Consortium ovvero della struttura che definisce gli standard per il web)</li>
<li><a href="http://www.nic.it/" target="_blank">Nic.it</a> (network information center italiano, tutto sui domini .it)</li>
<li><a href="http://www.icann.org/" target="_blank">Icann.org</a> (analogo sito che si occupa di tutti i domini esistenti)</li>
<li><a href="http://www.dropped.it/" target="_blank">Dropped.it</a> (questo sito propone liste di domini scaduti o in scadenza)</li>
<li><a href="http://pejone.wordpress.com/2009/08/11/lista-domini-web/" target="_self">Domini Web</a> (lista <em>speriamo</em> completa di tutti i domini web)</li>
<li><a href="http://pejone.wordpress.com/2009/08/11/i-domini-piu-costosi/" target="_self">I domini più costosi</a> (lista dei domini più cari del web)</li>
</ul>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/06/23/free-hosting-e-spaces/" target="_self">Free Hosting e Space</a> (spazi per il tuo sito/blog e non solo)</span></span></span></span></span></span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/07/28/servizi-e-widget-web/" target="_self">Servizi e widget web </a>(servizi e “accessori” per il tuo sito/blog)</span></span></span></span></span></span></li>
</ul>
<p><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"> </span></span></span></span></span></span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Colori per il web]]></title>
<link>http://pejone.wordpress.com/2009/08/09/colori-per-il-web/</link>
<pubDate>Sun, 09 Aug 2009 10:10:44 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/08/09/colori-per-il-web/</guid>
<description><![CDATA[Chiunque si sia mai approcciato alla costruzione di un sito web sa che uno dei problemi a cui va inc]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Chiunque si sia mai approcciato alla costruzione di un sito web sa che uno dei problemi a cui va incontro in fase di ideazione, è la scelta dei colori.<br />
Dallo sfondo ai menu, dalle sezioni al piè di pagina, non sempre è facile trovare colori coerenti con gli scopi di comunicabilità del sito.</p>
<p style="text-align:center;"><img class="aligncenter" title="Web colors" src="http://img188.imageshack.us/img188/7282/colori.png" alt="" width="250" height="284" /></p>
<p>Di seguito alcuni links che possono tornare molto utili in tale frangente:</p>
<p><a href="http://www.gplorusso.it/colori-sito.htm" target="_blank">Scelta dei colori</a> (linee guida per la scelta dei colori)</p>
<p><a href="http://www.shinynews.it/usability/0504-colori2.shtml" target="_blank">Scelta delle combinazioni</a> (uso e combinazioni di colori)</p>
<p><a href="http://www.colorcombos.com/index.html" target="_blank">Colorcombs.com</a> (sito che permette di trovare una vasta libreria di combinazioni e di testarne di personalizzate)</p>
<p><a href="http://www.dannydesign.it/tabella_colori.htm" target="_blank">Tabella dei colori</a> (una pratica tabella con i codici HEX ed RGB dei colori)</p>
<p><a href="http://vedo.altervista.org/personalizza-sito-web/colori-nel-web/index.php" target="_blank">Tavolozza interrativa</a> (permette di ricavare i codici HEX e RGB direttamente dal colore)</p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><a href="http://pejone.wordpress.com/2009/06/23/free-hosting-e-spaces/" target="_self">Free Hosting e Space</a> <span style="color:#333333;">(spazi per il tuo sito/blog e non solo)</span></li>
<li><span style="color:#333333;"><span style="color:#333333;"><span style="color:#333333;"><span style="color:#3366ff;"><span style="color:#000000;"><span style="color:#333333;"><a href="http://pejone.wordpress.com/2009/07/28/servizi-e-widget-web/" target="_self">Servizi e widget web </a>(servizi e “accessori” per il tuo sito/blog)</span></span></span></span></span></span></li>
<li><a href="http://pejone.wordpress.com/wp-admin/pejone.wordpress.com/2009/08/11/siti-utili-per-il-web/" target="_self">Siti utili per il web</a> <span style="color:#333333;">(siti informativi indispensabili per il web design)</span></li>
</ul>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Einciclopedia dell'hacking]]></title>
<link>http://pejone.wordpress.com/2009/08/04/einciclopedia-dellhacking/</link>
<pubDate>Tue, 04 Aug 2009 12:10:39 +0000</pubDate>
<dc:creator>pejone</dc:creator>
<guid>http://pejone.wordpress.com/2009/08/04/einciclopedia-dellhacking/</guid>
<description><![CDATA[L’einciclopedia dell’hacking di Lord Shinva è un documento ormai obsoleto nei metodi proposti ma int]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>L’einciclopedia dell’hacking di Lord Shinva è un documento ormai obsoleto nei metodi proposti ma interessantissimo dal punto di vista dell’approccio. Resta uno scritto fondamentale nel panorama hacker italiano qui disponibile in comodo formato pdf.</p>
<p style="text-align:center;"><img class="aligncenter" title="LordShinva" src="http://img121.imageshack.us/img121/9210/lord.png" alt="" width="331" height="158" /></p>
<p>Scarica il file in pdf: <a href="http://pejone.files.wordpress.com/2009/07/lord-shinva.pdf" target="_blank">http://pejone.files.wordpress.com/2009/07/lord-shinva.pdf</a></p>
<p><img class="alignright" title="Pejone Research" src="http://pejone.wordpress.com/files/2009/09/pejoneresearch.png" alt="" width="77" height="14" /></p>
<p><span style="color:#ffffff;">.</span></p>
<p><span style="color:#ff0000;"><strong><!--more-->Articoli correlati:<br />
</strong></span><span style="color:#0000ff;">_____________________________________________________</span><span style="color:#0000ff;"> </span></p>
<ul>
<li><a href="http://pejone.wordpress.com/2009/01/22/guide-hacker/" target="_self">Guide generali all’hacking</a> <span style="color:#333333;">(per imparare teoria e pratica)</span></li>
</ul>
<p><span style="color:#333333;"> </span></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Linus Torvalds Facts]]></title>
<link>http://gianmichele.wordpress.com/2009/06/17/linus-torvalds-facts/</link>
<pubDate>Wed, 17 Jun 2009 07:25:36 +0000</pubDate>
<dc:creator>gianmichele</dc:creator>
<guid>http://gianmichele.wordpress.com/2009/06/17/linus-torvalds-facts/</guid>
<description><![CDATA[Sulla falsa riga dei fatti su Chuck Norris, ecco una selezione dedicata a Linus Torvalds: Linus ha i]]></description>
<content:encoded><![CDATA[Sulla falsa riga dei fatti su Chuck Norris, ecco una selezione dedicata a Linus Torvalds: Linus ha i]]></content:encoded>
</item>
<item>
<title><![CDATA[Imparare a programmare]]></title>
<link>http://informaticaetica.wordpress.com/2009/06/05/imparare-a-programmare/</link>
<pubDate>Fri, 05 Jun 2009 08:15:36 +0000</pubDate>
<dc:creator>marco costanzo</dc:creator>
<guid>http://informaticaetica.wordpress.com/2009/06/05/imparare-a-programmare/</guid>
<description><![CDATA[Da http://www.python.it/doc/libri.html La versione Python del libro di Allen Downey : How to Think L]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><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 />
&#8216;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.<!--more--></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 &#8216;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 &#60;iostream.h&#62;</p>
<p>void main()<br />
{<br />
cout &#60;&#60; &#8220;Hello, World!&#8221; &#60;&#60; 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 />
&#62;&#62;&#62; 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 &#62;&#62;&#62;: 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; print 4<br />
4</p>
<p>Se non sei sicuro del tipo di un valore, l&#8217;interprete te lo può dire:</p>
<p>&#62;&#62;&#62; type(&#8220;Hello, World!&#8221;)<br />
&#60;type &#8217;string&#8217;&#62;<br />
&#62;&#62;&#62; type(17)<br />
&#60;type &#8216;int&#8217;&#62;</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>&#62;&#62;&#62; type(3.2)<br />
&#60;type &#8216;float&#8217;&#62;</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>&#62;&#62;&#62; type(&#8220;17&#8243;)<br />
&#60;type &#8217;string&#8217;&#62;<br />
&#62;&#62;&#62; type(&#8220;3.2&#8243;)<br />
&#60;type &#8217;string&#8217;&#62;</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>&#62;&#62;&#62; 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>&#62;&#62;&#62; messaggio = &#8220;Come va?&#8221;<br />
&#62;&#62;&#62; n = 17<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; print messaggio<br />
Come va?<br />
&#62;&#62;&#62; print n<br />
17<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; type(message)<br />
&#60;type &#8217;string&#8217;&#62;<br />
&#62;&#62;&#62; type(n)<br />
&#60;type &#8216;int&#8217;&#62;<br />
&#62;&#62;&#62; type(pi)<br />
&#60;type &#8216;float&#8217;&#62;</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>&#62;&#62;&#62; 76strumenti = &#8220;grande banda&#8221;<br />
SyntaxError: invalid syntax<br />
&#62;&#62;&#62; milione$ = 1000000<br />
SyntaxError: invalid syntax<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 17<br />
17<br />
&#62;&#62;&#62; x<br />
2</p>
<p>La differenza tra &#8220;valutare un&#8217;espressione&#8221; e stamparne il valore è<br />
sottile ma importante:</p>
<p>&#62;&#62;&#62; messaggio = &#8220;Come va?&#8221;<br />
&#62;&#62;&#62; messaggio<br />
&#8220;Come va?&#8221;<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; minuti = 59<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62;  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>&#62;&#62;&#62; type(&#8220;32&#8243;)<br />
&#60;type &#8217;string&#8217;&#62;</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>&#62;&#62;&#62; betty = type(&#8220;32&#8243;)<br />
&#62;&#62;&#62; print betty<br />
&#60;type &#8217;string&#8217;&#62;</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>&#62;&#62;&#62; id(3)<br />
134882108<br />
&#62;&#62;&#62; betty = 3<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; int(&#8220;32&#8243;)<br />
32<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; int(3.99999)<br />
3<br />
&#62;&#62;&#62; int(-2.3)<br />
-2</p>
<p>La funzione float converte interi e stringhe in numeri in virgola<br />
mobile:</p>
<p>&#62;&#62;&#62; float(32)<br />
32.0<br />
&#62;&#62;&#62; float(&#8220;3.14159&#8243;)<br />
3.14159</p>
<p>Infine str converte al tipo stringa:</p>
<p>&#62;&#62;&#62; str(32)<br />
&#8216;32&#8242;<br />
&#62;&#62;&#62; str(3.14149)<br />
&#8216;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>&#62;&#62;&#62; minuti = 59<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; minuti = 59<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; decibel = math.log10 (17.0)<br />
&#62;&#62;&#62; angolo = 1.5<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; gradi = 45<br />
&#62;&#62;&#62; angolo = gradi * 2 * math.pi / 360.0<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Stampa2Volte(&#8216;Pippo&#8217;)<br />
Pippo Pippo<br />
&#62;&#62;&#62; Stampa2Volte(5)<br />
5 5<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Stampa2Volte(&#8220;Pippo&#8221;*4)<br />
PippoPippoPippoPippo PippoPippoPippoPippo<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Messaggio = &#8216;Come va?&#8217;<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Strofa1 = &#8220;Nel mezzo &#8220;<br />
&#62;&#62;&#62; Strofa2 = &#8220;del cammin&#8221;<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Quoziente = 7 / 3<br />
&#62;&#62;&#62; print Quoziente<br />
2<br />
&#62;&#62;&#62; Resto = 7 % 3<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 5 == 5<br />
1<br />
&#62;&#62;&#62; 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 &#62; y        # x è maggiore di y?<br />
x &#60; y        # x è minore di y?<br />
x &#62;= y       # x è maggiore o uguale a y?<br />
x &#60;= 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 =&#60; e =&#62;, ma solo gli equivalenti &#60;= e &#62;=.</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&#62;0)<br />
and (x&#60;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&#62;y è<br />
vera (x è maggiore di y), not(x&#62;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>&#62;&#62;&#62;  x = 5<br />
&#62;&#62;&#62;  x and 1<br />
1<br />
&#62;&#62;&#62;  y = 0<br />
&#62;&#62;&#62;  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>&#62;&#62;&#62;  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 &#62; 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 &#62; 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>&#62;&#62;&#62; StampaParita(17)<br />
&#62;&#62;&#62; 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 &#60; y:<br />
print x, &#8220;e&#8217; minore di&#8221;, y<br />
elif x &#62; 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 &#60; 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 &#60; x:<br />
if x &#60; 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&#62;0 e x&#60;10) sono verificate contemporaneamente. Possiamo quindi usare<br />
l&#8217;operatore booleano and per combinarle:</p>
<p>if 0&#60;x and x&#60;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 &#60; x &#60; 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 &#60;= 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>&#62;&#62;&#62; 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 &#62; 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;&#60;stdin&#62;&#8221;, line 2, in Ricorsione<br />
&#8230;<br />
File &#8220;&#60;stdin&#62;&#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>&#62;&#62;&#62; Inserimento = raw_input ()<br />
Testo inserito<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Nome = raw_input (&#8220;Qual e&#8217; il tuo nome? &#8220;)<br />
Qual e&#8217; il tuo nome? Arturo<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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: ==, !=, &#62;, &#60;,<br />
&#62;= e &#60;=.</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 &#60; 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 &#60; 0:<br />
return -x<br />
elif x &#62; 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>&#62;&#62;&#62; print ValoreAssoluto(0)<br />
None</p>
<p>Esercizio: scrivi una funzione Confronto che ritorna 1 se x&#62;y, 0 se<br />
x==y e -1 se x&#60;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>&#62;&#62;&#62; 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>&#62;&#62;&#62;   Divisibile(6, 4)<br />
0<br />
&#62;&#62;&#62;   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>&#62;&#62;&#62; 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 &#60; 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>&#62;&#62;&#62; Fattoriale(&#8220;AAA&#8221;)<br />
Il fattoriale è definito solo per i valori interi.<br />
-1<br />
&#62;&#62;&#62; 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 &#60;- 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 &#62; 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 &#60; 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 &#60; 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 &#60;= 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 &#60;= 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 &#60;= 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 TabellaMoltiplicazione6&#215;6():<br />
i = 1<br />
while i &#60;= 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 TabellaMoltiplicazione6&#215;6.<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 TabellaMoltiplicazione6&#215;6 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 TabellaMoltiplicazione6&#215;6 va da 1 a 6. Nel diagramma<br />
ha valore 3 e al prossimo ciclo varrà 4. Ad ogni ciclo<br />
TabellaMoltiplicazione6&#215;6 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 TabellaMoltiplicazione6&#215;6.</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 TabellaMoltiplicazione6&#215;6<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 &#60;= 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 &#60;= Grandezza:<br />
print n*i, &#8216;\t&#8217;,<br />
i = i + 1<br />
print<br />
def TabellaMoltiplicazioneGenerica(Grandezza):<br />
i = 1<br />
while i &#60;= 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>&#62;&#62;&#62; Frutto = &#8220;banana&#8221;<br />
&#62;&#62;&#62; Lettera = Frutto[1]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Lettera = Frutto[0]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Frutto = &#8220;banana&#8221;<br />
&#62;&#62;&#62; 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 &#60; 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 &#60; 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>&#62;&#62;&#62; s = &#8220;Pietro, Paolo e Maria&#8221;<br />
&#62;&#62;&#62; print s[0:6]<br />
Pietro<br />
&#62;&#62;&#62; print s[8:13]<br />
Paolo<br />
&#62;&#62;&#62; 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://s.wordpress.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>&#62;&#62;&#62; Frutto = &#8220;banana&#8221;<br />
&#62;&#62;&#62; Frutto[:3]<br />
&#8216;ban&#8217;<br />
&#62;&#62;&#62; 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 &#60; &#8220;BANANA&#8221;:<br />
print &#8220;la tua parola&#8221; + Parola + &#8220;viene prima di BANANA.&#8221;<br />
elif Parola &#62; &#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; &#60; &#8220;BAnana&#8221; &#60; &#8220;Banana&#8221; &#60; &#8220;bANANA&#8221; &#60; &#8220;banana&#8221;<br />
&#8220;ZEBRA&#8221; &#60; &#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 &#60; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Frutto = &#8220;banana&#8221;<br />
&#62;&#62;&#62; Posizione = string.find(Frutto, &#8220;a&#8221;)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; print string.lowercase<br />
&#62;&#62;&#62; print string.uppercase<br />
&#62;&#62;&#62; 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; &#60;= Carattere &#60;= &#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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Vocabolario = ["amico", "casa", "telefono"]<br />
&#62;&#62;&#62; Numeri = [17, 123]<br />
&#62;&#62;&#62; ListaVuota = []<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; print Numeri[0]<br />
17<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Numeri[3-2]<br />
5<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Numeri[-1]<br />
5<br />
&#62;&#62;&#62; Numeri[-2]<br />
17<br />
&#62;&#62;&#62; 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 &#60; 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 &#60; 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>&#62;&#62;&#62; Squadre = ['Juventus', 'Inter', 'Milan', 'Roma']<br />
&#62;&#62;&#62; &#8216;Inter&#8217; in Squadre<br />
1<br />
&#62;&#62;&#62; &#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>&#62;&#62;&#62; &#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 &#60; 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>&#62;&#62;&#62; a = [1, 2, 3]<br />
&#62;&#62;&#62; b = [4, 5, 6]<br />
&#62;&#62;&#62; c = a + b<br />
&#62;&#62;&#62; print c<br />
[1, 2, 3, 4, 5, 6]</p>
<p>L&#8217;operatore * ripete una lista un dato numero di volte:</p>
<p>&#62;&#62;&#62; [0] * 4<br />
[0, 0, 0, 0]<br />
&#62;&#62;&#62; [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>&#62;&#62;&#62; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&#62;&#62;&#62; Lista[1:3]<br />
['b', 'c']<br />
&#62;&#62;&#62; Lista[:4]<br />
['a', 'b', 'c', 'd']<br />
&#62;&#62;&#62; Lista[3:]<br />
['d', 'e', 'f']<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Frutta = ["banana", "mela", "susina"]<br />
&#62;&#62;&#62; Frutta[0] = &#8220;pera&#8221;<br />
&#62;&#62;&#62; Frutta[-1] = &#8220;arancia&#8221;<br />
&#62;&#62;&#62; print Frutta<br />
['pera', 'mela', 'arancia']</p>
<p>Con l&#8217;operatore porzione possiamo modificare più elementi alla volta:</p>
<p>&#62;&#62;&#62; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&#62;&#62;&#62; Lista[1:3] = ['x', 'y']<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&#62;&#62;&#62; Lista[1:3] = []<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Lista = ['a', 'd', 'f']<br />
&#62;&#62;&#62; Lista[1:1] = ['b', 'c']<br />
&#62;&#62;&#62; print Lista<br />
['a', 'b', 'c', 'd', 'f']<br />
&#62;&#62;&#62; Lista[4:4] = ['e']<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; a = ['uno', 'due', 'tre']<br />
&#62;&#62;&#62; del a[1]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Lista = ['a', 'b', 'c', 'd', 'e', 'f']<br />
&#62;&#62;&#62; del Lista[1:5]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; id(a)<br />
135044008<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; a = [1, 2, 3]<br />
&#62;&#62;&#62; b = [1, 2, 3]<br />
&#62;&#62;&#62; id(a)<br />
135045528<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; a = [1, 2, 3]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; b[0] = 5<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; a = [1, 2, 3]<br />
&#62;&#62;&#62; b = a[:]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; b[0] = 5<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Numeri = [1, 2, 3]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Numeri = [1, 2, 3]<br />
&#62;&#62;&#62; CancellaTesta(Numeri)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Numeri = [1, 2, 3]<br />
&#62;&#62;&#62; Resto = Coda(Numeri)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Elemento = Lista[3]<br />
&#62;&#62;&#62; Elemento[0]<br />
10</p>
<p>O possiamo combinare i due passi in un&#8217;unica istruzione:</p>
<p>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Matrice[1]<br />
[4, 5, 6]</p>
<p>O estrarre una singola cella usando il doppio indice:</p>
<p>&#62;&#62;&#62; 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>&#62;&#62;&#62; import string<br />
&#62;&#62;&#62; Verso = &#8220;Nel mezzo del cammin&#8230;&#8221;<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Lista = ['Nel', 'mezzo', 'del', 'cammin...']<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; t1 = (&#8216;a&#8217;,)<br />
&#62;&#62;&#62; type(t1)<br />
&#60;type &#8216;tuple&#8217;&#62;</p>
<p>Senza la virgola, infatti, Python tratterebbe (&#8216;a&#8217;) come una stringa<br />
tra parentesi:</p>
<p>&#62;&#62;&#62; t2 = (&#8216;a&#8217;)<br />
&#62;&#62;&#62; type(t2)<br />
&#60;type &#8217;string&#8217;&#62;</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>&#62;&#62;&#62; tupla = (&#8216;a&#8217;, &#8216;b&#8217;, &#8216;c&#8217;, &#8216;d&#8217;, &#8216;e&#8217;)<br />
&#62;&#62;&#62; tupla[0]<br />
&#8216;a&#8217;</p>
<p>e l&#8217;operatore porzione seleziona una serie di elementi consecutivi:</p>
<p>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; tupla = (&#8216;A&#8217;,) + tupla[1:]<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; temp = a<br />
&#62;&#62;&#62; a = b<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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 &#60; Numero &#60; 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 &#60; Numero &#60; 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>&#62;&#62;&#62; Eng2Ita = {}<br />
&#62;&#62;&#62; Eng2Ita['one'] = &#8216;uno&#8217;<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Magazzino = {&#8216;mele&#8217;: 430, &#8216;banane&#8217;: 312, &#8216;arance&#8217;: 525,<br />
&#8216;pere&#8217;: 217}<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; del Magazzino['pere']<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Magazzino['pere'] = 0<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Eng2Ita.has_key(&#8216;one&#8217;)<br />
1<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Opposti = {&#8216;alto&#8217;: &#8216;basso&#8217;, &#8216;giusto&#8217;: &#8217;sbagliato&#8217;,<br />
&#8216;vero&#8217;: &#8216;falso&#8217;}<br />
&#62;&#62;&#62; Alias = Opposti<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Alias['giusto'] = &#8216;errato&#8217;<br />
&#62;&#62;&#62; Opposti['giusto']<br />
&#8216;errato&#8217;</p>
<p>Opposti resta immutato se modifichiamo Copia:</p>
<p>&#62;&#62;&#62; Copia['giusto'] = &#8216;errato&#8217;<br />
&#62;&#62;&#62; Opposti['giusto']<br />
&#8217;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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Matrice[(0,3)]<br />
1<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Matrice[1,3]<br />
KeyError: (1, 3)</p>
<p>Il metodo get risolve il problema:</p>
<p>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; type(1L)<br />
&#60;type &#8216;long int&#8217;&#62;</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>&#62;&#62;&#62; long(1)<br />
1L<br />
&#62;&#62;&#62; long(3.9)<br />
3L<br />
&#62;&#62;&#62; long(&#8216;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>&#62;&#62;&#62; Precedenti = {0:1L, 1:1L}<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; ConteggioLettere = {}<br />
&#62;&#62;&#62; for Lettera in &#8220;Mississippi&#8221;:<br />
&#8230;   ConteggioLettere [Lettera] = ConteggioLettere.get \<br />
(Lettera, 0) + 1<br />
&#8230;<br />
&#62;&#62;&#62; ConteggioLettere<br />
{&#8216;M&#8217;: 1, &#8217;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>&#62;&#62;&#62; ConteggioLettere = ConteggioLettere.items()<br />
&#62;&#62;&#62; ConteggioLettere.sort()<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; f = open(&#8220;test.dat&#8221;,&#8221;w&#8221;)<br />
&#62;&#62;&#62; print f<br />
&#60;open file &#8216;test.dat&#8217;, mode &#8216;w&#8217; at fe820&#62;</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>&#62;&#62;&#62; f.write(&#8220;Adesso&#8221;)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Testo = f.read()<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; f = open(&#8220;test.dat&#8221;,&#8221;r&#8221;)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; print f.read(1000006)<br />
ochiudi il file<br />
&#62;&#62;&#62; print f.read()<br />
&#62;&#62;&#62;</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>&#62;&#62;&#62; f = open(&#8220;test.dat&#8221;,&#8221;w&#8221;)<br />
&#62;&#62;&#62; f.write(&#8220;linea uno\nlinea due\nlinea tre\n&#8221;)<br />
&#62;&#62;&#62; f.close()</p>
<p>Il metodo readline legge tutti i caratteri fino al prossimo ritorno a<br />
capo:</p>
<p>&#62;&#62;&#62; f = open(&#8220;test.dat&#8221;,&#8221;r&#8221;)<br />
&#62;&#62;&#62; print f.readline()<br />
linea uno<br />
&#62;&#62;&#62;</p>
<p>readlines ritorna tutte le righe rimanenti come lista di stringhe:</p>
<p>&#62;&#62;&#62; 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>&#62;&#62;&#62; print f.readline()<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; x = 52<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; NumAuto = 52<br />
&#62;&#62;&#62; &#8220;%d&#8221; % NumAuto<br />
&#8216;52&#8242;</p>
<p>Il risultato è la stringa &#8216;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>&#62;&#62;&#62; NumAuto = 52<br />
&#62;&#62;&#62; &#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>&#62;&#62;&#62; &#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>&#62;&#62;&#62; &#8220;%d %d %d&#8221; % (1,2)<br />
TypeError: not enough arguments for format string<br />
&#62;&#62;&#62; &#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>&#62;&#62;&#62; &#8220;%6d&#8221; % 62<br />
&#8216;    62&#8242;<br />
&#62;&#62;&#62; &#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>&#62;&#62;&#62; &#8220;%-6d&#8221; % 62<br />
&#8216;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>&#62;&#62;&#62; &#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>&#62;&#62;&#62; Tariffe = {&#8216;Maria&#8217;: 6.23, &#8216;Giovanni&#8217;: 5.45, &#8216;Alberto&#8217;: 4.25}<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62;   f = open(&#8220;/usr/share/dict/words&#8221;,&#8221;r&#8221;)<br />
&#62;&#62;&#62;   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>&#62;&#62;&#62; f.write (str(12.3))<br />
&#62;&#62;&#62; f.write (str(4.567))<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62;   f.readline()<br />
&#8216;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>&#62;&#62;&#62; import pickle<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; pickle.dump(12.3, f)<br />
&#62;&#62;&#62; pickle.dump(4.567, f)<br />
&#62;&#62;&#62; pickle.dump([1,2,3], f)<br />
&#62;&#62;&#62; f.close()</p>
<p>Puoi riaprire il file e caricare le strutture di dati memorizzati con<br />
il metodo load:</p>
<p>&#62;&#62;&#62; f = open(&#8220;test.pck&#8221;,&#8221;r&#8221;)<br />
&#62;&#62;&#62; x = pickle.load(f)<br />
&#62;&#62;&#62; x<br />
12.3<br />
&#62;&#62;&#62; type(x)<br />
&#60;type &#8216;float&#8217;&#62;<br />
&#62;&#62;&#62; x2 = pickle.load(f)<br />
&#62;&#62;&#62; x2<br />
4.567<br />
&#62;&#62;&#62; type(x2)<br />
&#60;type &#8216;float&#8217;&#62;<br />
&#62;&#62;&#62; y = pickle.load(f)<br />
&#62;&#62;&#62; y<br />
[1, 2, 3]<br />
&#62;&#62;&#62; type(y)<br />
&#60;type &#8216;list&#8217;&#62;</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 />
&#62;&#62;&#62; print 55/0<br />
ZeroDivisionError: integer division or modulo<br />
* la richiesta di un elemento di una lista con un indice errato:<br />
&#62;&#62;&#62; a = []<br />
&#62;&#62;&#62; print a[5]<br />
IndexError: list index out of range<br />
* la richiesta di una chiave non esistente in un dizionario:<br />
&#62;&#62;&#62; b = {}<br />
&#62;&#62;&#62; 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 &#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; P1.x = 3.0<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; print P1.y<br />
4.0<br />
&#62;&#62;&#62; x = P1.x<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; print P1<br />
&#60;__main__.Punto instance at 80f8e70&#62;</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>&#62;&#62;&#62; P1 = Punto()<br />
&#62;&#62;&#62; P1.x = 3<br />
&#62;&#62;&#62; P1.y = 4<br />
&#62;&#62;&#62; P2 = Punto()<br />
&#62;&#62;&#62; P2.x = 3<br />
&#62;&#62;&#62; P2.y = 4<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; P2 = P1<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; P1 = Punto()<br />
&#62;&#62;&#62; P1.x = 3<br />
&#62;&#62;&#62; P1.y = 4<br />
&#62;&#62;&#62; P2 = Punto()<br />
&#62;&#62;&#62; P2.x = 3<br />
&#62;&#62;&#62; P2.y = 4<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Centro = TrovaCentro(Rett)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; R1 = Rettangolo()<br />
&#62;&#62;&#62; R1.Larghezza = 100.0<br />
&#62;&#62;&#62; R1.Altezza = 200.0<br />
&#62;&#62;&#62; R1.AltoSinistra = Punto()<br />
&#62;&#62;&#62; R1.AltoSinistra.x = 0.0;<br />
&#62;&#62;&#62; R1.AltoSinistra.y = 0.0;<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; import copy<br />
&#62;&#62;&#62; P1 = Punto()<br />
&#62;&#62;&#62; P1.x = 3<br />
&#62;&#62;&#62; P1.y = 4<br />
&#62;&#62;&#62; P2 = copy.copy(P1)<br />
&#62;&#62;&#62; P1 == P2<br />
0<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; OraCorrente = Tempo()<br />
&#62;&#62;&#62; OraCorrente.Ore = 9<br />
&#62;&#62;&#62; OraCorrente.Minuti = 14<br />
&#62;&#62;&#62; OraCorrente.Secondi=  30<br />
&#62;&#62;&#62; TempoCottura = Tempo()<br />
&#62;&#62;&#62; TempoCottura.Ore =  3<br />
&#62;&#62;&#62; TempoCottura.Minuti = 35<br />
&#62;&#62;&#62; TempoCottura.Secondi = 0<br />
&#62;&#62;&#62; PastoPronto = SommaTempi(OraCorrente, TempoCottura)<br />
&#62;&#62;&#62; 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 &#62;= 60:<br />
Somma.Secondi = Somma.Secondi &#8211; 60<br />
Somma.Minuti = Somma.Minuti + 1<br />
if Somma.Minuti &#62;= 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 &#62;= 60:<br />
Tempo.Secondi = Tempo.Secondi &#8211; 60<br />
Tempo.Minuti = Tempo.Minuti + 1<br />
if Tempo.Minuti &#62;= 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 &#62;= 60:<br />
Tempo.Secondi = Tempo.Secondi &#8211; 60<br />
Tempo.Minuti = Tempo.Minuti + 1<br />
while Tempo.Minuti &#62;= 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>&#62;&#62;&#62; OraAttuale = Tempo()<br />
&#62;&#62;&#62; OraAttuale.Ore = 9<br />
&#62;&#62;&#62; OraAttuale.Minuti = 14<br />
&#62;&#62;&#62; OraAttuale.Secondi = 30<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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 &#62;= 60:<br />
self.Secondi = self.Secondi &#8211; 60<br />
self.Minuti = self.Minuti + 1<br />
while self.Minuti &#62;= 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 &#62; Tempo2.Ore:<br />
return 1<br />
if self.Ore &#60; Tempo2.Ore:<br />
return 0<br />
if self.Minuti &#62; Tempo2.Minuti:<br />
return 1<br />
if self.Minuti &#60; Tempo2.Minuti:<br />
return 0<br />
if self.Secondi &#62; 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 &#60; 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 &#60; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; OraAttuale = Tempo(9, 14, 30)<br />
&#62;&#62;&#62; OraAttuale.StampaTempo()<br />
&#62;&#62;&#62; 9:14:30</p>
<p>Dato che i parametri sono opzionali possiamo anche ometterli:</p>
<p>&#62;&#62;&#62; OraAttuale = Tempo()<br />
&#62;&#62;&#62; OraAttuale.StampaTempo()<br />
&#62;&#62;&#62; 0:0:0</p>
<p>Possiamo anche fornire solo il primo parametro:</p>
<p>&#62;&#62;&#62; OraAttuale = Tempo(9)<br />
&#62;&#62;&#62; OraAttuale.StampaTempo()<br />
&#62;&#62;&#62; 9:0:0</p>
<p>o i primi due parametri:</p>
<p>&#62;&#62;&#62; OraAttuale = Tempo(9, 14)<br />
&#62;&#62;&#62; OraAttuale.StampaTempo()<br />
&#62;&#62;&#62; 9:14:0</p>
<p>Infine possiamo anche passare un sottoinsieme dei parametri<br />
nominandoli esplicitamente:</p>
<p>&#62;&#62;&#62; OraAttuale = Tempo(Secondi = 30, Ore = 9)<br />
&#62;&#62;&#62; OraAttuale.StampaTempo()<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; P = Punto(3, 4)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; P = Punto(3, 4)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62;   P1 = Punto(3, 4)<br />
&#62;&#62;&#62;   P2 = Punto(5, 7)<br />
&#62;&#62;&#62;   P3 = P1 + P2<br />
&#62;&#62;&#62;   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>&#62;&#62;&#62; P1 = Punto(3, 4)<br />
&#62;&#62;&#62; P2 = Punto(5, 7)<br />
&#62;&#62;&#62; print P1 * P2<br />
43<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; MoltSomma(3, 2, 1)<br />
7</p>
<p>o con oggetti di tipo Punto:</p>
<p>&#62;&#62;&#62; P1 = Punto(3, 4)<br />
&#62;&#62;&#62; P2 = Punto(5, 7)<br />
&#62;&#62;&#62; print MoltSomma(2, P1, P2)<br />
(11, 15)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62;   Lista = [1, 2, 3, 4]<br />
&#62;&#62;&#62;   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>&#62;&#62;&#62;   P = Punto(3, 4)<br />
&#62;&#62;&#62;   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 (+, -, *, &#62;, &#60;, 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 -&#62; 3<br />
Cuori  -&#62; 2<br />
Quadri -&#62; 1<br />
Fiori  -&#62; 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   -&#62; 1<br />
Jack   -&#62; 11<br />
Regina -&#62; 12<br />
Re     -&#62; 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>&#62;&#62;&#62; Carta1 = Carta(1, 11)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Carta2 = Carta(1, 3)<br />
&#62;&#62;&#62; print Carta2<br />
3 di Quadri<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Carta1.ListaSemi[1] = &#8220;Bastoni&#8221;<br />
&#62;&#62;&#62; print Carta1<br />
Jack di Bastoni</p>
<p>&#8230;tutti i Quadri diventerebbero dei Bastoni:</p>
<p>&#62;&#62;&#62; 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 (&#60;, &#62;,<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 &#62; Altro.Seme: return 1<br />
if self.Seme &#60; Altro.Seme: return -1<br />
# se i semi sono uguali controlla il rango<br />
if self.Rango &#62; Altro.Rango: return 1<br />
if self.Rango &#60; 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>&#62;&#62;&#62; Mazzo1 = Mazzo()<br />
&#62;&#62;&#62; 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 &#60;= x &#60; 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>&#62;&#62;&#62; Mazzo1 = Mazzo()<br />
&#62;&#62;&#62; Mazzo1.Mescola()<br />
&#62;&#62;&#62; Mano1 = Mano(&#8220;pippo&#8221;)<br />
&#62;&#62;&#62; Mazzo1.Distribuisci([Mano1], 5)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Partita = GiocoDiCarte()<br />
&#62;&#62;&#62; Mano1 = ManoOldMaid(&#8220;Franco&#8221;)<br />
&#62;&#62;&#62; Partita.Mazzo.Mescola([Mano1], 13)<br />
&#62;&#62;&#62; 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 />
&#62;&#62;&#62; 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 />
&#62;&#62;&#62; 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 &#60; 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>&#62;&#62;&#62; import Carte<br />
&#62;&#62;&#62; Gioco = Carte.GiocoOldMaid()<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Nodo1 = Nodo(&#8220;test&#8221;)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Nodo1 = Nodo(1)<br />
&#62;&#62;&#62; Nodo2 = Nodo(2)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; Nodo1.ProssimoNodo = Nodo2<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; StampaLista(Nodo1)<br />
1 2 3<br />
&#62;&#62;&#62; Rimosso = RimuoviSecondo(Nodo1)<br />
&#62;&#62;&#62; StampaLista(Rimosso)<br />
2<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; P = Pila()<br />
&#62;&#62;&#62; P.Push(54)<br />
&#62;&#62;&#62; P.Push(45)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; import string<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; import re<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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] &#62; 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>&#62;&#62;&#62; q = CodaConPriorita()<br />
&#62;&#62;&#62; q.Inserimento(11)<br />
&#62;&#62;&#62; q.Inserimento(12)<br />
&#62;&#62;&#62; q.Inserimento(14)<br />
&#62;&#62;&#62; q.Inserimento(13)<br />
&#62;&#62;&#62; 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 &#62; 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 &#60; Altro.Punteggio: return  1<br />
if self.Punteggio &#62; Altro.Punteggio: return -1<br />
return 0</p>
<p>Ora siamo pronti a testare la coda con priorità sulla classe Golf:</p>
<p>&#62;&#62;&#62; tiger = Golf(&#8220;Tiger Woods&#8221;,    61)<br />
&#62;&#62;&#62; phil  = Golf(&#8220;Phil Mickelson&#8221;, 72)<br />
&#62;&#62;&#62; hal   = Golf(&#8220;Hal Sutton&#8221;,     69)<br />
&#62;&#62;&#62;<br />
&#62;&#62;&#62; pq = CodaConPriorità()<br />
&#62;&#62;&#62; pq.Inserimento(tiger)<br />
&#62;&#62;&#62; pq.Inserimento(phil)<br />
&#62;&#62;&#62; pq.Inserimento(hal)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Albero = Albero(&#8216;+&#8217;, Albero(1), Albero(&#8216;*&#8217;, Albero(2), \<br />
Albero(3)))<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; Lista = [9, 11, 'end']<br />
&#62;&#62;&#62; x = ControllaNumero(Lista)<br />
&#62;&#62;&#62; StampaAlberoPost(x)<br />
9<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; ListaToken = [9, '*', 11, 'end']<br />
&#62;&#62;&#62; Albero = EsprProdotto(ListaToken)<br />
&#62;&#62;&#62; StampaAlberoPost(Albero)<br />
9 11 *</p>
<p>&#62;&#62;&#62; ListaToken = [9, '+', 11, 'end']<br />
&#62;&#62;&#62; Albero = EsprProdotto(ListaToken)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; ListaToken = [2, '*', 3, '*', 5 , '*', 7, 'end']<br />
&#62;&#62;&#62; Albero = EsprProdotto(ListaToken)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; ListaToken = [9, '*', 11, '+', 5, '*', 7, 'end']<br />
&#62;&#62;&#62; Albero = EsprSomma(ListaToken)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; ListaToken = [9, '*', '(', 11, '+', 5, ')', '*', 7, 'end']<br />
&#62;&#62;&#62; Albero = EsprSomma(ListaToken)<br />
&#62;&#62;&#62; 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] == &#8217;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 &#62; 0 and y &#60; 0 :<br />
# fai qualcosa con x<br />
# fai qualcosa con y<br />
print  "x: ", x<br />
print  "y: ", y<br />
print  "condizione: ", (x &#62; 0 and y &#60; 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>&#62;&#62;&#62; from Frazione import Frazione<br />
&#62;&#62;&#62; f = Frazione(5,6)<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; print Frazione(5,6) * 4<br />
20/6<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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>&#62;&#62;&#62; print Frazione(5,6) + Frazione(5,6)<br />
60/36<br />
&#62;&#62;&#62; print Frazione(5,6) + 3<br />
23/6<br />
&#62;&#62;&#62; 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>&#62;&#62;&#62; 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 &#62; c/d allora ad &#62; 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 &#62; Altro.Seme: return 1<br />
if self.Seme &#60; Altro.Seme: return -1<br />
# se i semi sono uguali controlla il rango<br />
if self.Rango &#62; Altro.Rango: return 1<br />
if self.Rango &#60; 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 &#60; 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] == &#8217;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://s.wordpress.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</p>
<p>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>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[189 Messaggi d'errore divertenti su UNIX]]></title>
<link>http://discoverylinux.wordpress.com/2009/06/03/189-messaggi-derrore-divertenti-su-unix/</link>
<pubDate>Wed, 03 Jun 2009 18:45:17 +0000</pubDate>
<dc:creator>sparazza</dc:creator>
<guid>http://discoverylinux.wordpress.com/2009/06/03/189-messaggi-derrore-divertenti-su-unix/</guid>
<description><![CDATA[Perchè a volte, i programmatori, sanno esprimere creativamente il fatto che qualcosa è andato irrime]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p style="text-align:center;"><img class="size-full wp-image-88 aligncenter" title="Tutto è perduto!" src="http://discoverylinux.wordpress.com/files/2009/06/error_linux.png" alt="Tutto è perduto!" width="190" height="124" /></p>
<p>Perchè a volte, i programmatori, sanno esprimere creativamente il fatto che qualcosa è andato irrimediabilmente a puttane&#8230;</p>
<p><!--more--></p>
<ol>
<li>“Values of B will give rise to dom.”</li>
<li>FATAL system error #nnnn CAUSE: We should never get here!</li>
<li>OHHHH…. I give up Core dumped</li>
<li>COMPILER UNABLE TO ABORT</li>
<li>AN ATTEMPT WAS MADE TO WRITE BEYOND THE MAXIMUM ASSIGNED SPACE FOR A MASS STORAGE FILE. AN ATTEMPT WAS MADE TO EXPAND A MASS STORAGE FILE BEYOND THE MAXIMUM ASSIGNED SPACE. A READ FUNCTION FOR A MASS STORAGE FILE SPECIFIED AN ADDRESS (WORD 5 OF THE I/O PACKET) THAT IS BEYOND THE MAXIMUM ASSIGNED SPACE. A READ OR WRITE FUNCTION FOR A WORD-ADDRESSABLE MASS STORAG FILE SPECIFIED A MASS STORAGE ADDRESS (WORD 5 OF THE I/O PACKET) AND A TOTAL DATA COUNT. WHEN THE MASS STORAGE ADDRESS IS ADDED TO THE TOTAL DATA COUNT, THE RESULTING ENDING MASS STORAGE ADDRESS IS GREATER THAN 2*/35-1. A READ OR WRITE FUNCTION FOR A SECTOR-FORMATTED MASS STORAGE FILE SPECIFIED A MASS STORAGE ADDRESS (WORD 5 OF THE I/O PACKET) THAT IS GREATER THAN 2*/30-1. ADI ONLY: REFERENCE ATTEMPTED BEYOND THE ASSIGNED FILE WHEN THE FILE IS CONFIGURED AS A FH-432 OR FH-1782 DRUM.</li>
<li>ERROR: A really big FUCK UP has been detected !!</li>
<li>Momentaraly writing while seeking..<br />
Constantly writing while seeking..<br />
Momentaraly writing while reading..</li>
<li>initstate: not enough state (%d bytes) with which to do jack; ignored.</li>
<li>“Keyboard not present, press any key”</li>
<li>“You lied to me when you told me this was a program”</li>
<li>“PROGRAMMER GOOFED . . . YOU SHOULD NEVER SEE THIS MESSAGE”</li>
<li>YOU CAN’T DO THAT!</li>
<li>Man the Lifeboats! Women and children first!</li>
<li>$ make :== $ sys$system:teco32 make<br />
$ make love<br />
Not war?</li>
<li>That makes 100 errors; please try again.</li>
<li>You can now delete more, or insert, or whatever.</li>
<li>Sorry, I don’t know how to help in this situation.</li>
<li>Maybe you should try asking a human?</li>
<li>Sorry, I already gave what help I could…</li>
<li>An error might have occurred before I noticed any problems.</li>
<li>If all else fails, read the instructions.</li>
<li>This can’t happen.</li>
<li>I’m broken. Please show this to someone who can fix can fix</li>
<li>I can’t go on meeting you like this.</li>
<li>One of your faux pas seems to have wounded me deeply.. in fact, I’m barely conscious. Please fix it and try again.</li>
<li>Interruption</li>
<li>You rang?</li>
<li>IMPOSSIBLE.</li>
<li>NONEXISTENT.</li>
<li>ETC.</li>
<li>BAD.</li>
<li>A funny symbol that I can’t read has just been input.  Continue, and I’ll forget that it ever happened.</li>
<li>I suspect you’ve forgotten a `}’, causing me to apply this control sequence to too much text. How can we recover? My plan is to forget the whole thing and hope for the best.</li>
<li>I dddon’t go any higher than filll.</li>
<li>Dimensions can be in units of em, ex, in, pt, pc, cm, mm, dd, cc, bp, or sp; but yours is a new one!</li>
<li>Something Rotten in Denmark, Interp Stack Not ALigned</li>
<li>&#60;Assorted DEC ID fruitcake&#62; ILLEGAL ERROR</li>
<li>bad magic number</li>
<li>“very funny”</li>
<li>“Unexpected ‘;’, expecting ‘;’”</li>
<li>You can’t do that in horizontal mode.</li>
<li>“COMPILER THWARTED”</li>
<li>“Keyboard error or no keyboard present. Press F1 to continue.”</li>
<li>“Argument is bletchful.”</li>
<li>“Guru Meditation”</li>
<li>“lint’s little mind is blown.”</li>
<li>“Hot Damn! You need more ram!”</li>
<li>String literal too long (I let you have 512 characters, that’s 3 more than ANSI said I should)</li>
<li>And the lord said, ‘lo, there shall only be case or default labels inside a switch statement’a typedef name was a complete surprise to me at this point in your program</li>
<li>You can’t modify a constant, float upstream, win an argument with the IRS, or satisfy this compiler</li>
<li>This struct already has a perfectly good definitiontype in (cast) must be scalar; ANSI 3.3.4; page 39, lines 10-11 (I know you<br />
don’t care, I’m just trying to annoy you)</li>
<li>Can’t cast a void type to type void (because the ANSI spec. says so, that’s why)</li>
<li>Huh ?</li>
<li>can’t go mucking with a ‘void *’</li>
<li>we already did this function</li>
<li>This label is the target of a goto from outside of the block containing this label AND this block has an automatic variable with an initializer AND your window wasn’t wide enough to read this whole error message</li>
<li>Call me paranoid but finding ‘/*’ inside this comment makes me suspicious</li>
<li>Symbol table full &#8211; fatal heap error; please go buy a RAM upgrade from your local Apple dealer</li>
<li>“It seem you are trying to check the output from a word-processor. Not only does this not make sense, but you would probably damage the file</li>
<li>if you tried so I am not going to let you do this!”</li>
<li>It looks like the active file is messed up. Contact your news administrator and leave the “bogus” groups alone, and they may come back to normal. Maybe.</li>
<li>Attention K-Mart shoppers: Blue Light special in out SYSTEM UTILITIES department. for the next 10 days we will be taking requests for the utilities that you think should be here. Thank you again for shopping K-Mart.</li>
<li>“Things are not looking good!”</li>
<li>“I didn’t think this set of error conditions could ever happen”</li>
<li>“Now deleting all files. Goodbye”</li>
<li>“file has bad magic.”</li>
<li>“Hi Linda! We wondered how long it would take, for you to mess up this bad.”</li>
<li>“The running master will not die…”</li>
<li>“Shut ‘er down, Clancy, she’s a-pumpin’ mud!”</li>
<li>An error has occured on the error logging device.</li>
<li>“Out of order”</li>
<li>“Hey are you talking to me? Try again!”</li>
<li>“Invalid command. Feel ashamed for yourself and try again.”</li>
<li>“Of all the commands available you picked the wrong one!”</li>
<li>“Shut her down, Scotty, she’s sucking mud again!”</li>
<li>ERROR 1164 HOW IN THE HELL DID YOU GET HERE</li>
<li>WARNING: FILE GENERATED THE FILE WHICH WAS SPECIFIED AS THE ‘COPY TO’ OR DESTINATION FILE WAS NOT THERE AND WAS THEREFORE GENERATED BY JOBCONTROL. IF YOU DID NOT MEAN TO COPY TO A NEW FILE ELIMINATE THE FILE.</li>
<li>NO ACCESS FOR $TOAD SERVICE A USER PROGRAM MADE A CALL TO A $TOAD SERVICE AND THE USER DOES NOT HAVE THE PROPER ACCESS TO BIT TO USE THAT SERVICE. ACCESS RESTRICTIONS ARE PLACED ON THE $TOADS SERVICES IN GENERAL, AND $CPRIOR, $PABORT, AND $SUSP FOR INDIVIDUAL RESTRICTIONS. JOBCNTRL ER 2167 : NO ACCESS TO VULCANIZE PROGRAM</li>
<li>AN ATTEMPT HAS BEEN MADE TO VULCANIZE A REAL-TIME, MONITOR, OR NRH<br />
TYPE PROGRAM, OR A PROGRAM WITH HIGH ACCESS, ACCOUNTING FILE ACCESS,<br />
OR SUB-SYSTEM ACCESS. THE VULCANIZE REQUEST IS IGNORED BECAUSE THE<br />
USER DOES NOT HAVE ACCESS TO GENERATE SUCH A PROGRAM.</li>
<li>IT’S NOT NICE TO FOOL POP!</li>
<li>YOU JUST TRIED TO FAKE-OUT MOTHER NATURE, AND SHE CAUGHT YOU! SUPER-VULCAN NOW HAS YOUR NAME ON HIS ENEMY LIST, AND YOU CAN BE CERTAIN THAT FUTURE ATTEMPTS TO RESOURCE LFN 0,3,OR 6 WILL RESULT IN YOUR BEING ABORTED, SPINDLED, MANGLED, FOLDED, PUNCHED, DELETED, AND DEALLOCATED.</li>
<li>PROGRAM FILE DESTROYED. THE PROGRAM HAS BEEN ABORTED DUE TO INCONSISTENCIES IN THE INFORMATION GENERATED BY THE VULCANIZER. THE DISC COPY OF THE PROGRAM MAY HAVE BEEN<br />
DESTROYED OR THE PROGRAM MAY NOT HAVE BEEN RE-VULCANIZED AFTER A MAJOR SYSTEM RELEASE. IN ANY CASE RE-VULCANIZE THE PROGRAM (RLIBS ALSO).</li>
<li>minor alarm</li>
<li>major alarm</li>
<li>critical alarm</li>
<li>alarm system failure alarm</li>
<li>This application has violated system integrity and must be terminated.</li>
<li>“You are a charlatan.”</li>
<li>“Go away. You don’t exist.”</li>
<li>Tsk tsk? Have I been a bad computer?</li>
<li>line 2706 compiler error: schain botch</li>
<li>Are you lonely?</li>
<li>Anyone have a better memory of this than I do?</li>
<li>?NO ERROR</li>
<li>I give up…. dumping core now!</li>
<li>For heavens sake, doesn’t anyone just talk anymore?</li>
<li>Not tonight, I’ve got a headache.</li>
<li>I beg your pardon?</li>
<li>Your place or mine?</li>
<li>FORTRAN FATAL INTERNAL ERROR FATAL COMPILER DAMAGE REPORT FOLLOWS</li>
<li>?Invalid Character At Terminal — Please Go Away</li>
<li>?Unibus timeout — send in a new quarterback</li>
<li>?Ouch, That HURTS!</li>
<li>You must be joking.</li>
<li>Error: Error ocurred when attempting to print error message.</li>
<li>Error #1: Power supply not found”</li>
<li>ERROR 0: POWER NOT ON</li>
<li>Break Rob’s knuckles</li>
<li>“You can tune a filesystem, but you can’t tuna fish”</li>
<li>$ man fish<br />
Don’t say “fish”, Bishop. It doesn’t mean anything.</li>
<li>$ man overboard<br />
BUGS: No life raft</li>
<li>“Oops! Error while handling error!”</li>
<li>Can’t find wicked faraway objects.</li>
<li>Can’t fit 27″ tape through 25″ door.</li>
<li>“Invalid Error”.</li>
<li>“code has no effect”</li>
<li>“No message, no subject; hope that’s ok.”</li>
<li>I the most critical examiner of all have determined that there is an error on line 42.</li>
<li>Parity Error But Segment Doesn’t Found</li>
<li>MORE CORE AVAILABLE, BUT NOT FOR YOU</li>
<li>Shannon and Bill say this can’t happen”);</li>
<li>PUNT</li>
<li>‘Weird magic happens here’.</li>
<li>“Thou hast new mail.”</li>
<li>Too much “sourcing” going on.</li>
<li>Okie dokie</li>
<li>Mail’s idea of conditions is screwed up</li>
<li>~h: no can do!?</li>
<li>Too many regrets</li>
<li>detract asked to insert commas</li>
<li>metoo</li>
<li>Somethings amiss — no @ or % in arpafix</li>
<li>Made up bad net name</li>
<li>ubluit</li>
<li>Who are you!?</li>
<li>; why =</li>
<li>“The impossible has happened!”</li>
<li>Beam me up Scotty, there’s no life out here.</li>
<li>“NO ERRORS DETECTED”.</li>
<li>NO COMPILER DETECTED ERRORS.</li>
<li>Holy Panes Batman, the window’s missing!</li>
<li>“Holy PH, Batman, the buffer’s missing!”</li>
<li>“Holy Vectors Batman, I can’t get more lines!”</li>
<li>“System Error &#8211; Sureness out of Bounds”</li>
<li>Mysterious Error -nnn</li>
<li>Internal Error: Illegal hedge TV number. (huh?? what?!)</li>
<li>Internal Error: BlinkThere or HiliteThere messed up.</li>
<li>Bad External File System: Boy, is your system messed up.</li>
<li>Hodie natus est radici frate<br />
“Today unto the root is born a brother’”</li>
<li>Looks like mere mortals are trying to enter the Twilight Zone</li>
<li>FATAL: Major security hack. Notify Administrator.</li>
<li>Identity problems, eh ?</li>
<li>Bad Craziness</li>
<li>’tis is no game for mere mortals</li>
<li>Go away and get a life</li>
<li>Death before dishonour ?</li>
<li>Dave, don’t do that…</li>
<li>Good afternoon, gentelman, I’m a HAL 9000 Computer</li>
<li>Only few mortals may try to enter the Twiligth Zone</li>
<li>Only real wizzards know the spells to open the gate of paradize</li>
<li>Trying to unlock the door twice eh ?</li>
<li>Use the force, Luke !</li>
<li>Change balls, please</li>
<li>Bad Craziness</li>
<li>“NONE of your errors have been found”</li>
<li>“Well, you ran into something and the game is over.”</li>
<li>“Unused error message #xxx”</li>
<li>“FALL DOWN GO BOOM”</li>
<li>Data potato doo-wop doo-wop</li>
<li>Okie dokie, core dumped.bash</li>
<li>Puke.</li>
<li>spurious multibus interrupt</li>
<li>EDOTDOT!!!!</li>
<li>Your guess is probably much better than mine.</li>
<li>You wascal wabbit! Wandering wizards won’t win!</li>
<li>savemail: HELP!!!!</li>
<li>Who are you ?</li>
<li>MAIL DELETED BECAUSE OF LACK OF DISK SPACE</li>
<li>Can’t suspend a login shell (yet).</li>
<li>EH?</li>
<li>You are a charlatan, bordeaux.kpno.noao.edu</li>
<li>“Your expression has defeated me”</li>
<li>“Your formula has defeated me”</li>
<li>WARNING: 54 &#8211; PROGRAM NOT RECURSIVE</li>
<li>Help is not available for you.</li>
<li>Masscomp C compiler:”Insane structure member list”</li>
<li>User Error: An unknown error has occurred in an unidentified program while executing an unimplemented function at an undefined address. Correct error and resubmit.</li>
<li>Liar, Liar! Pants on Fire!</li>
<li>Error: Success</li>
</ol>
<p>Fonte: <a href="http://www.fsckin.com/2007/09/24/189-humorous-unix-errors/" target="_blank">http://www.fsckin.com/2007/09/24/189-humorous-unix-errors/</a></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Crea il Tuo Software]]></title>
<link>http://nadialavora.wordpress.com/2009/04/27/crea-il-tuo-software/</link>
<pubDate>Mon, 27 Apr 2009 15:34:00 +0000</pubDate>
<dc:creator>infoprodottiepercheno</dc:creator>
<guid>http://nadialavora.wordpress.com/2009/04/27/crea-il-tuo-software/</guid>
<description><![CDATA[Studia il linguaggio C, crea da solo le tue interfacce e impara i trucchi per diventare un abile pro]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Studia il linguaggio C, crea da solo le tue interfacce e impara i trucchi per diventare un abile programmatore di software grazie agli esempi e alla praticità di un grande esperto del settore dell&#8217;ITC.</p>
<p>Questo Ebook <a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=296&#38;pp=78062">Crea il Tuo Software</a> ti insegna a Programmare e a Realizzare Software con i più Grandi Linguaggi di Programmazione. <br />&#160; <br />Un Ebook di 199 Pagine+ Report di Vincenzo Iavazzo </p>
<div class="separator" style="clear:both;text-align:center;"><a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=296&#38;pp=78062" style="margin-left:1em;margin-right:1em;"><img alt="" border="0" src="http://nadialavora.wordpress.com/files/2009/04/bcreailtuosoftware.jpg?w=202" /></a></div>
<div style="border-bottom:medium none;border-left:medium none;border-right:medium none;border-top:medium none;">Ricordati: <a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=296&#38;pp=78062">Crea il Tuo Software</a></div>
<div style="border-bottom:medium none;border-left:medium none;border-right:medium none;border-top:medium none;">Interessante per Informatica ed Internet</div>
<div></div>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Microsoft rilascia nuovi DevKit, e che DevKit!]]></title>
<link>http://inserirelamemorycard.wordpress.com/2009/03/26/microsoft-rilascia-nuovi-devkit-e-che-devkit/</link>
<pubDate>Thu, 26 Mar 2009 03:14:39 +0000</pubDate>
<dc:creator>Selector</dc:creator>
<guid>http://inserirelamemorycard.wordpress.com/2009/03/26/microsoft-rilascia-nuovi-devkit-e-che-devkit/</guid>
<description><![CDATA[In occasione della GDC09, che si sta svolgendo in questi giorni a San Francisco, Microsoft ha presen]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p style="text-align:center;"><a><img class="aligncenter" style="cursor:auto;" src="http://images.console-tribe.com/xbox360/news/09.03.25/thumbs/x3a.jpg" alt="http://images.console-tribe.com/xbox360/news/09.03.25/thumbs/x3a.jpg" /></a></p>
<p style="text-align:justify;">In occasione della <em>GDC09</em>, che si sta svolgendo in questi giorni a San Francisco, <em>Microsoft </em>ha presentato nuovi DevKit per <a id="JADVKey_2_0_1" class="JADV_Spot" href="http://contextual.juiceadv.com/banner_showext5.asp?tipo=2&#38;url=112&#38;key=536&#38;redirect=966e448baf9bfd1e3df9b0aeea3fd0d4" target="_blank">Xbox</a> 360. Per chi non lo sapesse questa versione della console, chiamata anche XDK (Xbox Development Kit), è quella destinata agli sviluppatori per programmare i <a id="JADVKey_1_0_0" class="JADV_Spot" href="http://contextual.juiceadv.com/banner_showext5.asp?tipo=2&#38;url=112&#38;key=477&#38;redirect=5ea09e3b0f02bac8b5dc12353b75e18b" target="_blank">giochi</a>. La console infatti differisce sull&#8217;hardware rispetto alla versione retail: ha per esempio ha una memoria doppia. In ogni caso, quello che ha suscitato scalpore, non è tanto la natura dei DevKit &#8211; che vengono aggiornati periodicamente da Microsoft stessa &#8211; ma la bellezza di questa SKU.</p>
<p style="text-align:justify;"><a href="http://xbox360.console-tribe.com/news/5-3-230203/microsoft-rilascia-nuovi-devkit-e-che-devkit.html" target="_blank"><strong>Leggi tutto »</strong></a></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Lezione ||: Come funziona in senso generale un pc (software)]]></title>
<link>http://dario2994.wordpress.com/2009/03/24/lezione-come-funziona-in-senso-generale-un-pc-software/</link>
<pubDate>Tue, 24 Mar 2009 18:26:52 +0000</pubDate>
<dc:creator>dario2994</dc:creator>
<guid>http://dario2994.wordpress.com/2009/03/24/lezione-come-funziona-in-senso-generale-un-pc-software/</guid>
<description><![CDATA[Come promesso nella lezione I che parlava di hardware ora analizzo anche il lato software. (Lezione ]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Come promesso nella lezione I che parlava di hardware ora analizzo anche il lato software. (<a title="http://dario2994.wordpress.com/2009/03/10/lezione-i-come-funziona-in-senso-generale-un-pc-hardware/" href="http://dario2994.wordpress.com/2009/03/10/lezione-i-come-funziona-in-senso-generale-un-pc-hardware/">Lezione &#124;</a>)</p>
<p>Allora tutto quello che voi fate con il pc, lo fate grazie ad un aggeggio che si chiama Processore. Questo coso è, detto in modo semplice, un coso che sa sommare 1 e 0 e lo fa mooooolto velocemente così da rendersi utile. Questo perchè tutto quello che voi vedete si riconduce ad una serie di 1 e 0.</p>
<p>Il computer non ragiona a parole o a numeri, ragiona in binario. Questo vuol dire che tutto è salvato in Binario, cioè le nostre lettere vengono trasformate in una serie di 1,0 e poi salvate.</p>
<p>Per essere chiari ogni lettera digitata e poi salvato occupa la bellezza di 8 &#8220;1/0&#8243; questo perchè 8 &#8220;1/0&#8243; danno la possibilità di scrivere 64 cose diverse, questo è il minimo perchè 32 non sarebbero bastati per tutti i simboli.</p>
<p>Tornando a noi tutti i programmi che eseguite si basano su calcoli &#8220;ovvi&#8221; che poi diventano strabilianti. Anche il vostro videogioco preferito si basa su 1,0&#8230; secondo voi come fa il pc a decidere se andare a destra o a sinistra, a zoomare etc&#8230; tutte formule matematiche <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  Che sono riconducibili a calcoli semplici.</p>
<p>In generale l&#8217;esecuzione di qualcosa funge così: il qualcosa chiede al sistema operativo di dire questo al processore, il sistema &#8220;acconsente&#8221; e il software comunica al processore il calcolo che gli serve, il processore esegue e restituisce il risultato.</p>
<p>Tutti i vostri software sono stati scritti da qualcuno, un cosidetto programmatore, che ha scritto un codice in cui dice al processore cosa fare. Il codice scritto è diciamo &#8220;ad alto livello&#8221; che vuol dire che sfrutta linguaggi in cui basta dire ad esempio &#8220;New array&#8221; per creare una matrice. Questo comando è relativamente molto complesso, ma il linguaggio automatizza tutto per rendere veloce. Poi una volta scritto il codice, questo va compilato, cioè trasformato in qualcosa che il processore possa leggere. La compilazione è un processo molto lento che trasforma ad esempio il comando New Array in tutta una serie di operazioni semplici passabili al processore. Anche il linguaggio js, che si crede non venga compilato, è compilato ed è per questo che le funzioni precotte sono molto più veloci di quelle fatte dall&#8217;utente <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  (Lo so perchè dopo aver fatto un bel algoritmo di ordinamento, una meraviglia, ho visto che era 20 volte più lento di quello di base xD). I linguaggi di programmazione sono veramente tanti, e si differenziano in generale in 2 gruppi: compilati e non. Tra i compilati ci sono:C++,C,Pascal,Ruby; mentre tra i non:java,python.</p>
<p>Spesso i software si basano su librerie precotte. Una libreria è un insieme di funzioni fatte da qualcun altro utili per velocizzare la scrittura del codice. Ad esempio in un linguaggio non c&#8217;è l&#8217;algoritmo per le radici, se io lo costruisco una volta e poi lo piazzo in una libreria poi lo posso riusare in futuro senza riscriverlo. Ovviamente per le librerie serie si parla di cose IMMENSE che rendono il lavoro moooolto più facile.</p>
<p>I sistemi operativi poi danno la possibilità di eseguire più &#8220;processi&#8221; per volta (multitasking) e cioè di fare più richieste contemporaneamente al processore, e ogni software fa la sua richiesta e il sistema le smista una dopo l&#8217;altra al processore.Se un pc possiede più di un processsore questo serve a poter eseguire più di un comando alla volta.</p>
<p>Infine consiglio a tutti di tentare di imparare a programmare perchè puo dare grandi soddisfazioni <img src='http://s.wordpress.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Come volevasi dimostrare.</p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Wolfram Research Mathematica 7]]></title>
<link>http://melatorrent.wordpress.com/2009/03/13/wolfram-research-mathematica-7/</link>
<pubDate>Fri, 13 Mar 2009 15:13:12 +0000</pubDate>
<dc:creator>MelaTorrent</dc:creator>
<guid>http://melatorrent.wordpress.com/2009/03/13/wolfram-research-mathematica-7/</guid>
<description><![CDATA[Mathematica 7 Come mi ha chiesto Carlo nell&#8217;apposita pagina &#8211; e lo ringrazio per l]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><div id="attachment_368" class="wp-caption aligncenter" style="width: 700px"><img class="size-full wp-image-368" title="Mathematica 7" src="http://melatorrent.wordpress.com/files/2009/03/mathematica.jpg" alt="Mathematica 7" width="690" height="170" /><p class="wp-caption-text">Mathematica 7</p></div>
<p>Come mi ha chiesto Carlo nell&#8217;apposita pagina &#8211; e lo ringrazio per l&#8217;ottima richiesta &#8211; parliamo di un programma molto usato a livello studentesco anche universitario.</p>
<p><strong>Mathematica</strong> è un programma molto usato in ambienti scolastici e universitari. Personalmente, anche facendo ingegneria informatica, non mi è mai capitato di doverlo studiare o comunque usare per qualche scopo ma è un &#8220;oggetto&#8221; molto potente per la risoluzione di problemi matematici alquanto complessi.</p>
<p>Si tratta di un ambiente di sviluppo e di calcolo, che dopo si tramuta comunque in un linguaggio di programmazione, rivolto alla riscrittura e risoluzione di problemi prettamente matematici. Uno strumento molto potente che viene usato, come già detto, molto in ambiente anche universitario per automatizzare, snellire e aiutare il lavoro di ricerca, programmazione e implementazione di molti problemi.</p>
<p>Il programma è in inglese (non credo che esista proprio una versione italiana) e dovrebbe essere facilmente aggiornabile alla versione 7.0.1 appena uscita.</p>
<p><strong>Link al torrent: <a href="http://www.demonoid.com/files/details/1724194/15658980/">http://www.demonoid.com/files/details/1724194/15658980/</a></strong></p>
<p>Per chi non potesse accedere a Demonoid (tracker chiuso con restrizioni) link un torrent pubblico che non posso assicurare che funzioni ma che, sinceramente, a naso non mi pare abbia problemi.</p>
<p><strong>Link al torrent: <a href="http://thepiratebay.org/torrent/4599301/Wolfram_Reserach_Mathematica_7_(Mac_OS_X)">http://thepiratebay.org/torrent/4599301/Wolfram_Reserach_Mathematica_7_(Mac_OS_X)</a></strong></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[La figura del programmatore]]></title>
<link>http://nadialavora.wordpress.com/2009/03/11/la-figura-del-programmatore/</link>
<pubDate>Wed, 11 Mar 2009 08:11:00 +0000</pubDate>
<dc:creator>infoprodottiepercheno</dc:creator>
<guid>http://nadialavora.wordpress.com/2009/03/11/la-figura-del-programmatore/</guid>
<description><![CDATA[Ciao a tutti, mi presento con piacere con questo articolo alla splendida compagine di lettori e scri]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Ciao a tutti, mi presento con piacere con questo articolo alla splendida compagine di lettori e scrittori del blog di Bruno Editore.<br />La crisi economica è profonda e diffusa, inutile nasconderlo.<br />Avere un atteggiamento mentale positivo e avere l’energia sufficiente per mettersi in moto è fondamentale ma, purtroppo, non sufficiente.<br />Al giorno d’oggi bisogna sia VOLER fare che SAPER fare. Ecco quindi la mia ricetta per superare la crisi: imparare una o più professioni, darsi da fare, sbracciarsi con tutte le proprie forze per trovare una lavoro remunerativo.</p>
<p>In quest’ottica, ho scritto il mio E-Book, il linguaggio Java, per trasmettere parte delle mie conoscenze informatiche a coloro che volessero avviarsi ad apprendere questa professione.<br />La professione del programmatore/analista, oggi, è nettamente in controtendenza rispetto a molte altre professioni: non solo sembra che la richiesta di esperti del settore informatico non è in crisi, ma addirittura soprattutto in città come Milano e altre del nord Italia la richiesta di programmatori e analisti è in netto aumento.</p>
<p>E questo l’ho provato sulla mia pelle: dopo la laurea, ho trovato lavoro nel settore in un tempo praticamente nullo.<br />La mia mail era continuamente inondata di messaggi del sito infojobs.it che mi avvisavano di nuove possibilità di lavoro nel settore nelle città che avevo scelto, ovvero Milano Torino Roma e Napoli.<br />Potreste obiettare che questo succedeva perché io mi ero laureato, ma voi magari avete solo un diploma: beh vi assicuro che in questo settore la laurea non serve a molto.<br />I colloqui sono orientati a capire le capacità del candidato e la sua esperienza nel settore: poco importa avere una laurea, se poi non si sa scrivere nemmeno un programma decente.</p>
<p>Tornando al mio e-book, ho voluto dedicarlo al linguaggio Java.<br />Perché? Intanto perché lo ritengo più immediato e piacevole rispetto ad altri. Poi perché è uno dei più ricercati nell’ambito lavorativo, e anche ben remunerato: ricordo benissimo un annuncio su infojobs che mi lasciò impressionato dopo la laurea, in cui si offriva nella zona di Milano un lavoro come programmatore J2EE ( quindi programmatore Java, versione Enterprise) per una cifra SUPERIORE AI 3000 Euro.<br />E si offriva inoltre possibilità di carriera! Quindi capite bene che la professionalità del programmatore può essere molto ben retribuita.<br />Ed il lavoro del programmatore è un lavoro sempre pieno di nuovi stimoli e di problemi da affrontare, davvero stimolante.</p>
<p>Per chi invece non fosse interessato alla carriera perché ne ha già una, il mio libro offre strumenti efficaci per imparare a lavorare su pagine Web complesse o su siti E-Commerce tramite JavaScript e HTML. Ed è presente tra gli esempi del libro un intero sito fatto con javascript in cui viene simulato un E-commerce, con tanto di carrello elettronico e trasmissione dell’ordine via mail.<br />Quindi, ricapitolando, il mio libro viene incontro sia a coloro i quali volessero apprendere una remunerativa carriera informatica, sia a coloro che svolgono già altre carriere ma desiderano realizzare un bel sito E-Commerce per la propria attività online.<br />Nei prossimi articoli sul blog terrò alcune lezioni d’informatica che riprendono alcuni passi del mio libro.<br />Spero che vi saranno utili.<br />In ogni caso, consiglio a tutti coloro i quali volessero avviarsi ad una professione informatica, o coloro che volessero acquisire gli strumenti per costruire un buon sito e-commerce con le proprie mani, di leggere il mio e-book pieno di nuovi stimoli e di problemi da affrontare, davvero stimolante.</p>
<p>Come dico nel mio curriculum, per molti anni ho dato lezioni di informatica: alcuni dei miei più accaniti alunni, con mio grande piacere, oggi lavorano a Milano come programmatori… e mi ringraziano per le nozioni che ho loro insegnato.<br />Per chi invece non fosse interessato alla carriera perché ne ha già una, il mio libro offre strumenti efficaci per imparare a lavorare su pagine Web complesse o su siti E-Commerce tramite JavaScript e HTML.<br />Ed è presente tra gli esempi del libro un intero sito fatto con javascript in cui viene simulato un E-commerce, con tanto di carrello elettronico e trasmissione dell’ordine via mail.<br />Quindi, ricapitolando, il mio libro viene incontro sia a coloro i quali volessero apprendere una remunerativa carriera informatica, sia a coloro che svolgono già altre carriere ma desiderano realizzare un bel sito E-Commerce per la propria attività online.<br />Nei prossimi articoli sul blog terrò alcune lezioni d’informatica che riprendono alcuni passi del mio libro. Spero che vi saranno utili. In ogni caso, consiglio a tutti coloro i quali volessero avviarsi ad una professione informatica, o coloro che volessero acquisire gli strumenti per costruire un buon sito e-commerce con le proprie mani, di leggere il mio e-book.</p>
<p>Grazie, un saluto…<br />A Cura di Ignazio Barbagallo,Autore di <a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=270&#38;pp=78062">“Il Linguaggio Java”</a></p>
</div>]]></content:encoded>
</item>
<item>
<title><![CDATA[Blog Business]]></title>
<link>http://nadialavora.wordpress.com/2009/02/23/blog-business/</link>
<pubDate>Mon, 23 Feb 2009 08:54:00 +0000</pubDate>
<dc:creator>infoprodottiepercheno</dc:creator>
<guid>http://nadialavora.wordpress.com/2009/02/23/blog-business/</guid>
<description><![CDATA[Questo Ebook Blog Business Ti insegna Come Creare un’Attività di Successo nell&#8217;Era del Web 2.0]]></description>
<content:encoded><![CDATA[<div class='snap_preview'><p>Questo Ebook <a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=284&#38;pp=78062">Blog Business</a> Ti insegna Come Creare un’Attività di Successo nell&#8217;Era del Web 2.0po.<br />Metti in pratica tutti gli step proposti per creare e gestire il tuo blog, scopri i piccoli segreti che faranno grande la tua attività e inizia a farti conoscere nel mondo del web 2.0<br />Ricordati: <a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=284&#38;pp=78062">Blog Business</a> . Un Ebook di 292 Pagine + 3 Report di Emanuele Papalia.<br />Per gli Interessati a Internet e Informatica.<a href="http://www.autostima.net/shopping/prodotto.php?id_prodotto=284&#38;pp=78062"><img alt="" src="http://nadialavora.wordpress.com/files/2009/02/bblogbusiness.jpg?w=202" border="0" /></a></p>
</div>]]></content:encoded>
</item>

</channel>
</rss>
