La Geometria della tartaruga (superiori)

lezione
lezione
La Geometria della tartaruga (superiori)
Tipo di risorsa Tipo: lezione
Materia di appartenenza Materie:
Avanzamento Avanzamento: lezione completa al 100%

Nascita della tartaruga

modifica
 
FIGURA 1: Seymour Papert il creatore di Logo.

Perché la geometria della tartaruga.

Negli anni 80 del secolo scorso, al MIT Seymour Papert ha modificato un linguaggio dedicato alla soluzione di problemi di intelligenza artificiale per comandare un robottino che aveva una penna e permettere ai bambini di dare le istruzioni per realizzare dei disegni.

Ha realizzato così il linguaggio Logo.

La tartaruga è un cursore grafico che può lasciare un segno quando si muove. La geometria della tartaruga è caratterizzata da avere un riferimento intrinseco cioè la geometria è riferita alla posizione e alla direzione del cursore stesso e non ad un riferimento esterno.

I comandi base della geometria della tartaruga sono semplici:

  • Forward (avanti).
  • Back (indietro).
  • Right (destra).
  • Left (sinistra).
  • Penup (penna su).
  • Pendown (penna giù).

A partire da questi comandi si possono affrontare problemi con un ampio ventaglio di difficoltà, da quelli elementari a problemi che richiedono conoscenze matematiche molto elevate. Nei seguenti paragrafi vedremo una introduzione alla geometria della tartaruga.

Installiamo un interprete

modifica

Cosa installare per lavorare con la grafica della tartaruga.

Come visto sopra per comandare la tartaruga dobbiamo scrivere delle istruzioni e dobbiamo farlo utilizzando un linguaggio. Nel seguito useremo Python + pygraph.

Il linguaggio che utilizzeremo all'interno di questo corso sarà Python. La normale installazione di Python ci mette a disposizione la libreria turtle che permette di realizzare la grafica della tartaruga. Chi usa come sistema operativo Windows può installare Python a partire dal sito:

www.python.org/downloads

E installare la versione più recente della serie 3.x.x.

Chi utilizza altri sistemi operativi può installarlo partendo dal proprio gestore di pacchetti installando Python3 e anche IDLE.

Python + pygraph

modifica

Python rende facile scrivere delle librerie che permettono di estendere le funzionalità del linguaggio. La libreria che utilizzeremo nel resto di questo manuale permette di utilizzare la tartaruga, ma fornisce anche alcuni altri comodi strumenti che useremo più avanti. Si può scaricare l'intero pacchetto da:

bitbucket.org/zambu/pygraph/downloads

A questo punto bisogna fare a mano alcune operazioni che dipendono dal proprio sistema operativo:

Windows

  • Scompattare il file scaricato.
  • Entrare nella cartella pygraph.
  • Selezionare il file pygraph.pth e la cartella pygraph lì presenti.
  • Copiarli nella cartella

C:

Python3x

Lib

site-package

A seconda della versione installata “Python3x” potrebbe essere: “Python34”, “Python35”,

....

MacOSX

  • Scompattare il file scaricato.
  • Entrare nella cartella pygraph.
  • Selezionare il file pygraph.pth e la cartella pygraph lì presenti.
  • Copiarli nella cartella HD/libreria/python/3.x/site-package .

Se in “HD/libreria/python/” non è presente la cartella “3.4/site-packages”, bisogna crearla.

GNU/Linux

  • Scompattare il file scaricato.
  • Entrare nella directory pygraph.
  • Aprire un terminale in questa directory.
  • Copiare la cartella pygraph e il file pygraph.pth nella cartella /usr/lib/python3/dist-packages/ .

Dato che in Linux, per modificare le directory di sistema bisogna essere amministratori, il comando da dare assomiglierà a questo: sudo cp -R python* /usr/lib/python3/dist-packages/ .

A questo punto se tutto è andato bene dovremmo essere in grado di avviare Python-IDLE e dare il comando:

import pyturtle as pt

Se non succede nulla vuol dire che tutto è andato a buon fine, se invece appare una scritta rossa, bisogna leggere almeno l'ultima riga e cercare di capire cosa non è andato bene. Magari ci si può far aiutare da qualcuno esperto nell'installazione di programmi.

Se tutto è andato per il verso giusto possiamo procedere.

Altri interpreti

modifica

Ci sono moltissime implementazioni della geometria della tartaruga in molti linguaggi diversi. Di seguito riporto alcune delle più interessanti.

modifica

LibreOffice permette anche di produrre disegni con lo strumento Draw. Basta aggiungere a Libreoffice l'estensione LibreLogo per realizzare, all'interno di una pagina di testo, disegni realizzati dalla tartaruga. Una volta installata l'estensione LibreLogo, bisogna andare su menu-visualizza-barre degli strumenti e aggiungere Logo. A questo punto, seguendo l'help () si può imparare come dare i comandi alla tartaruga per produrre i disegni che abbiamo in mente.

Snap è un linguaggio a blocchi derivato dal linguaggio Squeak e è molto interessante per un uso nella didattica. Permette di realizzare funzioni con parametri e è in grado di interfacciarsi con vari dispositivi esterni come:

  • Lego NXT package by Connor Hudson.
  • Nintendo Wiimote package by Connor Hudson.
  • Finch and Hummingbird robots package by Tom Lauwers.
  • Parallax S2 robot package by Connor Hudson.
  • LEAP Motion by Connor Hudson.
  • Speech synthesis by Connor Hudson.
  • Arduino package by Alan Yorinks.

Siti di riferimento per Snap:

snap.berkeley.edu/snapsource/snap.html .

Primi comandi

modifica

Facciamo lavorare un po' Python. Il modo più semplice per scrivere un programma in Python è quello di usare l'interfaccia Idle.

Per cui dal menu-programmi-Python, si avvii Idle.

Idle ci permette di dare dei comandi e di vederne il risultato alla pressione del tasto .

Ad esempio possiamo dare il comando:

>>> fa qualcosa !

ma questo produce solo una scritta rossa la cui ultima riga dice:

SyntaxError : inva l id syntax

È chiaro che Python non sa eseguire qualunque comando. Una istruzione che dovrebbe capire è:

>>> print(5)

5

questa volta è andato...

Al posto di 5 possiamo scrivere un'espressione complessa quanto vogliamo. Se è corretta verrà eseguita e verrà stampato il risultato. Dobbiamo tenere presente che nei linguaggi di programmazione le parentesi delle espressioni sono solo tonde. Quindi dovremo tradurre eventuali parentesi quadre e graffe in parentesi tonde.

Provate...

Il simbolo per l'elevamento a potenza è una coppia di asterischi: “**”, proviamo:

>>> print(2**100)

1267650600228229401496703205376

L'aritmetica dei numeri interi di Python prevede numeri limitati solo dalle capacità del computer. Pyton è in grado di calcolare anche  .

>>> print(2**1000)

107150860718626732094842504906000181056140481170553360744375038837

035105112493612249319837881569585812759467291755314682518714528569

231404359845775746985748039345677748242309854210746050623711418779

541821530464749835819412673987675591655439460770629145711964776865

42167660429831652624386837205668069376

Python è in grado di fare i calcoli interi con una precisione illimitata, ma il tempo impiegato è dato dalla velocità della macchina su cui gira. Se chiediamo un calcolo molto lungo, potrebbe impiegare minuti, giorni o anni per realizzarlo. Sulla mia macchina (un po' vecchiotta)   impiega una decina di secondi per essere eseguito. Se abbiamo chiesto a Python un calcolo troppo complesso l'unico modo per uscirne è chiudere la finestra di IDLE e ripartire da capo.

Oltre ai numeri interi Python opera anche con altri oggetti primitivi:

  • Interi.
  • Numeri con la virgola (il separatore dei decimali è il punto).
  • Stringhe (sequenze di caratteri delimitati da virgolette doppie o semplici).
  • Insiemi.
  • Tuple.
  • Liste.
  • ....

Giusto per curiosità possiamo anche vedere che Python è in grado di fare operazioni piuttosto strane:

>>> print('casa' + 'matta')

casamatta

>>>print('ciao' * 3)

ciaociaociao

>>> print('ciao ' * 3)

ciao ciao ciao

Avanti, indietro, destra, sinistra

modifica

Come convincere un triangolino detto Tartaruga a disegnare linee sullo schermo.

La geometria della tartaruga è caratterizzata dall'avere un riferimento intrinseco invece che estrinseco: descrive le figure dal punto di vista di chi le sta disegnando muovendosi su una superficie piuttosto che dal punto di vista di chi le guarda dall'alto.

Per questioni storiche il cursore grafico è detto tartaruga dato che le prime realizzazioni di questa geometria utilizzavano un piccolo robot a forma di semisfera che muovendosi su un grande foglio lasciava una traccia con una penna.

I principali comandi della geometria della tartaruga sono le istruzioni per farla muovere o ruotare. La sintassi di questi comandi è:

<tartaruga>.forward(<passi>)

<tartaruga>.back(<passi>)

<tartaruga>.left(<gradi>)

<tartaruga>.right(<gradi>) Ma come fare per creare una tartaruga al nostro servizio? Prima di tutto dobbiamo caricare dalla libreria pyturtle.py gli oggetti che ci servono per il disegno:

  • Un foglio su cui possono disegnare.
  • Le tartarughe e le tartarughe stesse:

>>> from pyturtle import TurtlePlane , Turtle

Da osservare che Python distingue tra maiuscole e minuscole, per cui è diverso scrivere “TurtlePlane” o Turtleplane”.

A questo punto, caricati gli oggetti di cui abbiamo bisogno, dobbiamo istanziarli (crearli):

>>> foglio = TurtlePlane ()

>>> tina = Turtle ()

Altra osservazione: la coppia di parentesi che segue gli identificatori TurtlePlane e Turtle indicano a Python che deve creare un oggetto di questa classe e collegarlo all'identificatore che precede l'uguale. Le due istruzioni significano:

Crea un oggetto ‘TurtlePlane‘ e associalo al nome ‘foglio‘.

Crea un oggetto ‘Turtle‘ e associalo al nome ‘tina‘.

D'ora in poi l'identificatore tina è associato ad un particolare oggetto della classe Turtle.

Ora non ci resta che sperimentare il funzionamento dei metodi della classe Turtle, magari risolvendo il famoso problemino della teoria dei grafi: disegna una casetta con una croce dentro senza sollevare la penna:

>>> tina.forward(100)

16

>>> tina.left(90)

>>> tina.forward(100)

17

>>> tina.left(90)

>>> tina.forward(100)

18

>>> tina.right(135)

>>> tina.forward(50 * 1.4142)

19

>>> ...

 
FIGURA 2: Casetta da tracciare senza alzare la penna.

Osservazione (assolutamente insignificante): Ogni volta che viene eseguito un comando che traccia una linea sullo schermo, nella shell appare un numero progressivo, non preoccupiamoci di questo, magari ci potrà servire più avanti. (Chi fosse proprio curioso può studiarsi l'esempio orologio.py).

Cercando di risolvere questo problema, le persone normali fanno diversi errori. Questa libreria non permette di cancellare l'effetto dell'ultimo comando, quando si commette un errore conviene usare il comando<tartaruga>.reset(), cancellare tutto e ridisegnare tutto daccapo. Per non perdere un mucchio di tempo in scrittura, è possibile sfruttare una caratteristica di IDLE:

per riscrivere un comando basta posizionare il cursore sulla riga da ripetere e premere , la riga verrà riscritta. Ora la si può modificare o mantenere invariata, premendo di nuovo<Invio> viene eseguita.

Altri due metodi importanti della classe Turtle sono:

<tartaruga >.up()

<tartaruga >.down()

L'effetto di questi due comandi è, rispettivamente, quello di sollevare la penna e riabbassare la penna. Dopo il comando up() la tartaruga si muove senza lasciare traccia, dopo il comando down() riprende a disegnare.

Ad esempio per disegnare un quadrato centrato nell'attuale posizione di tina posso dare i seguenti comandi:

>>> tina.reset()

>>> tina.up()

>>> tina.back(100)

>>> tina.right(90)

>>> tina.down()

>>> tina.forward(100)

43

>>> tina.left(90)

...

Se non vi riesce al primo colpo, non preoccupatevi, io ho fatto 5 tentativi prima di ottenere esattamente il disegno che avevo progettato.

Riassumendo

Il modulo pyturtle mette a disposizione le classi TurtlePlane e Turtle. Per caricare queste classi si deve dare il comando:

from pyturtle import TurtlePlane , Turtle

Per creare un piano e una tartaruga si deve dare il comando:

piano = TurtlePlane()

tina = Turtle()

dove dobbiamo stare attenti alla maiuscola e alla coppia di parentesi. Al posto di tina e di piano possiamo usare un qualunque altro nome.

I principali comandi della geometria della tartaruga sono:

<tartaruga >.forward(<passi >), <tartaruga >.back(<passi >),

<tartaruga >.left(<gradi >), <tartaruga >.right(<gradi >),

<tartaruga >. reset(), <tartaruga >.up(), <tartaruga >.down().

Iterazione: ciclo for

modifica

Come ripetere istruzioni e modificare dimensione e colore del tratto.

Cammino dell'ubriaco

modifica

Se vogliamo mandare a spasso tina lungo un percorso casuale, invece che farle realizzare un disegno ben preciso, possiamo caricare la funzione randrange dal modulo random e dare una serie di comandi:

>>> from pyturtle import TurtlePlane , Turtle

>>> from random import randrange

>>> foglio = TurtlePlane()

>>> tina = Turtle()

>>> tina.forward(randrange(50)); tina.left(120)

25

>>> tina.forward(randrange(50)); tina.left(120)

26

...

randrange(<numero>) restituisce un numero intero “casuale” compreso tra 0 e <numero>. In questo modo tina avanzerà di un numero di passi diverso ogni volta che viene dato questo comando.

È possibile scrivere più istruzioni Python sulla stessa riga, basta separarle con un punto e virgola “;”. In generale non è una buona cosa scrivere più istruzioni sulla stessa riga, di regola non va fatto, ma come ogni regola, anche questa ha delle eccezioni. In questo caso invece di riscrivere tutta la riga ogni volta che vogliamo rieseguirla, basta che con la freccia della tastiera o con il mouse riportiamo il cursore su quella riga e premiamo il tasto . In questo modo la riga di istruzioni verrà riscritta su una linea vuota, potremo modificarla, se vogliamo, ed eseguirla premendo ancora il tasto . Quattro tasti invece di quaranta!

Anche se il meccanismo di IDLE che ci permette di ricopiare una riga è comodo, se volessi eseguire quelle due istruzioni per 500 volte o 1000 volte, il lavoro e il consumo della tastiera diventerebbero inaccettabili. Gli informatici, che sono tra gli esseri umani (?) più pigri che esistano, hanno inventato un meccanismo che permette di ripetere un blocco di istruzioni quante volte vogliamo specificando semplicemente il numero. Questo meccanismo, in informatica, prende il nome di “ciclo”. Esistono diversi tipi di ciclo, in questo capitolo utilizzeremo il ciclo for.

Se vogliamo che il nostro ubriaco esegua 300 tratti del suo cammino strampalato, possiamo scrivere:

>>> tina.reset()} >>> for conta tor e in range(300):

tina.forward(random.randrange(50));

tina.left(120)

La sintassi del comando for è:

for <variabile> in <sequenza>:

<istruzioni>

Nel caso precedente la sequenza è generata dalla funzione range(<numero>), ma si possono creare sequenze del tutto fantasiose come nell'esempio seguente:

>>> for elemento in ['Et' , 'telefono' , 'casa' , 123]:

print(elemento)

Et

telefono

casa

123

Anche in questo caso la variabile assume ad ogni ciclo un differente valore della sequenza.

I primi tempi è facile dimenticarsi i due punti “:” che segnano l'inizio del blocco di istruzioni da ripetere, ma un po' alla volta si impara ad essere precisi. Provate a ricopiare uno dei due cicli e a cancellare i ”:” per vedere cosa accade. Le istruzioni che si vuole siano ripetute devono essere indentate cioè devono essere scritte rientrando di 4 caratteri rispetto all'istruzione for.

Caratteristiche della tartaruga

modifica

Può darsi che il colore nero della penna della tartaruga ci sembri piuttosto noioso, possiamo modificarlo usando l'attributo della tartaruga color:
<tartaruga>.color = <colore>Ad esempio se vogliamo che venga disegnata una raggiera di linee rosse:

>>> foglio.reset()

>>> tina.color = 'red'

>>> for cont in range (72):

tina.forward(100)

tina.back(100)

tina.left(5)

Le istruzioni da ripetere devono costituire un blocco cioè essere tutte rientrate rispetto all'istruzione for. Questo rientro si chiama indentazione. In Python un blocco di istruzioni è definito dall'avere tutte la stessa indentazione. Quindi il comando precedente dice a Python di eseguire per 72 volte le istruzioni seguenti che si trovano indentate.

Il colore è una stringa che può contenere il nome (in inglese) di un colore oppure una stringa nel formato: "#rrggbb" dove rr, gg, bb sono 3 numeri in esadecimale (numeri compresi tra 00 e ff) che indicano le componenti dei 3 colori primari in emissione, cioè: rosso, verde e blu. Combinando questi 3 numeri possiamo ottenere un colore tra 256*256*256 possibili colori (16.777.216 possibili colori). Ad esempio per ottenere il giallo dobbiamo mescolare il rosso al verde: "#ffff00" per ottenere il magenta si mescola il rosso al blu: "#ff00ff" per ottenere il ciano si mescola il verde al blu: "#ff00ff".

Anche le dimensioni del tratto della penna e della tartaruga stessa possono essere cambiate modificando l'attributo 'width' della tartaruga:

<tar taruga >.width = <numero> Ad esempio se voglio ottenere dei raggi più grossi: >>> foglio.reset()

>>> tina.width = 5

>>> tina.color = 'pink'

>>> for cont in range (72):

tina.forward(100)

tina.back(100)

tina.left(5)

Riassumendo

  • In IDLE possiamo richiamare una istruzione scritta precedentemente portando il cursore su quella riga e premendo .
  • Il ciclo for permette di ripetere quante volte vogliamo un blocco di istruzioni.
  • La libreria random ci permette di utilizzare numeri pseudo-casuali.
  • Possiamo modificare il colore e lo spessore della tartaruga e della sua penna.

Programmi e funzioni

modifica

Come insegnare a Python a eseguire comandi nuovi.

Finora abbiamo usato comandi definiti all'interno di Python o di una sua libreria. Ad esempio per disegnare un quadrato, dopo aver caricato la libreria pyturtle e creato una tartaruga di nome tina dovevamo scrivere:

tina.forward(50)

tina.left(90)

tina.forward(50)

tina.left(90)

tina.forward(50)

tina.left(90)

tina.forward(50)

tina.left(90)

oppure:

for cont in range (4):

tina.forward(50)

tina.left(90)

Questo modo di lavorare va bene finché i progetti sono piccoli, ma se dovessi scrivere un programma in cui i quadrati sono tanti, si rivelerebbe piuttosto scomodo. Non solo. I comandi dati nella shell vanno persi quando esco da IDLE, se devo abbandonare a metà un lavoro e spegnere il computer, la volta seguente dovrò ripartire da capo. Ciò non va affatto bene! I programmatori hanno inventato dei modi:

  • Per salvare i propri programmi sia finiti sia in fase di costruzione.
  • Per dare un nome a un blocco di codice in modo da poterlo eseguire scrivendo una parola invece che 3, 8, o 800 linee di codice.

Il primo programma

modifica

Innanzitutto, una volta avviato IDLE, diamo il comando: menu-File-NewWindow. Si aprirà una nuova finestra di editor vuota. Prima ancora di incominciare a riempirla, la salviamo, nella nostra cartella di lavoro con il nome: 01quadrato.py

O un altro nome che sia sensato per noi.

Qualche osservazione

  • Per salvare un documento con un altro nome: menu-File-SaveAs....
  • Il file va salvato nella nostra cartella di lavoro, magari in una sotto-cartella dove teniamo i lavori di informatica.
  • Perché un nome così strano:
    • “01” Perché questo aiuterà il sistema operativo a tenere in ordine i nostri programmi.
    • “Quadrato” perché in questo primo programma insegneremo a tartaruga a disegnare un quadrato.
    • “.Py” per segnalare al sistema operativo che questo file contiene un programma scritto nel linguaggio Python.

Controlliamo che nella barra del titolo della finestra di editor appaia il nome del file associato. Possiamo iniziare a scrivere il nostro programma. Per prima cosa scriviamo qualcosa che Python non considererà affatto: dei commenti. Ogni programma deve iniziare con dei commenti che devono contenere almeno le seguenti informazioni: titolo, autore, data.

Python considera un commento tutto ciò che segue il carattere cancelletto “#”. Il nostro programma quindi inizierà con alcune righe che assomiglieranno a queste:

# <data>

# <nome>

# Poligoni regolari con la grafica della tartaruga: Quadrato

L'editor di IDLE si accorge che sono commenti e colora queste righe di rosso. Se ciò non avviene controllate che il nome del file abbia esattamente l'estensione “.py”. A questo punto possiamo finalmente possiamo incominciare a scrivere dei comandi da far eseguire a Python.

Incominciamo con i due comandi per caricare la libreria e per creare una tartaruga:

from pyturtle import Turtleplane , Turtle

foglio = TurtlePlane()

tina = Turtle()

Quando premiamo il tasto <Invio> non viene eseguito niente, viene solo inserito un carattere di fine linea e il cursore di inserimento va a capo. Per eseguire il programma si deve premere il tasto <F5> oppure dare il comando menu-Run-Run Module. IDLE prima di eseguire qualunque cosa vorrà salvare il contenuto dell'editor. Poi, se non abbiamo introdotto errori, verrà caricata la libreria e creata una tartaruga. Provandolo mi sono accorto di un errore... lo avete individuato? Dalla shell di IDLE possiamo dare i comandi che vogliamo alla nuova tartaruga. Ma non lo faremo, perché noi vogliamo mettere tutte le istruzioni in un programma.

Aggiungiamo le istruzioni per disegnare un quadrato:

tina.forward(50)

tina.left(90)

tina.forward(50)

tina.left(90)

...

No, c'era un altro metodo, usando l'iterazione:

for cont in range (4):

tina.forward(50)

tina.left(90)

Eseguiamo il programma... bene, funziona, appare il nostro quadrato.

Funzioni

modifica

Ora vogliamo dare un nome ad un blocco di codice. Vogliamo scrivere le istruzioni per disegnare un quadrato e farle eseguire scrivendo la parola quadrato(). Notate anche qui la coppia di parentesi tonde che indicano a Python che deve eseguire qualcosa. Il blocco di istruzioni preceduto dal nome si chiama “funzione”. La funzione quadrato si può scrivere così:

def quadrato():

for cont in range (4):

tina.forward(50)

tina.left(90)

la sintassi è:

def<nome della funzione>():

<istruzioni>

Notate anche qui:

  • Le parentesi tonde.
  • Il carattere due punti “:” che separa la dichiarazione della funzione dalla sua definizione.
  • La definizione della funzione è costituita da un blocco di istruzioni che è indentato rispetto alla dichiarazione.
 
FIGURA 3: Disposizione delle finestre sul desktop.

Per eseguire il programma basta premere il tasto <F5> o eseguire il comando menu-Run-Run Module. Viene caricata la libreria, creata la tartaruga e dis... NO, non viene disegnato un quadrato.

Con i comandi precedenti abbiamo solo insegnato a Python a disegnare un quadrato, ma non gli abbiamo detto di disegnarlo. Spostiamoci nella Shell di IDLE e diamo il comando: quadrato(). Ora il quadrato appare nella finestra grafica.

Per fare in modo che il nostro programma disegni il quadrato, aggiungiamo questo comando come ultima istruzione e rieseguiamo il tutto(<F5>).

Funziona! Il nostro primo programma è terminato.

Struttura di un programma

modifica

Non basta che il programma funzioni, deve anche essere scritto bene. Il nostro può essere migliorato. In generale i programmi sono strutturati in questo modo:

<intestazione>

<lettura delle librerie>

<definizioni>

<programma principale> Altra cosa importante: ogni funzione deve avere una stringa di documentazione, detta “docstring”. È una stringa, solitamente delimitata da tre doppi apici che dà una descrizione di cosa fa la funzione.

Operiamo quindi il nostro primo refactoring cioè modifichiamo il programma, che già funziona, per adeguarlo ad uno stile di programmazione più produttivo: # <data> # <nome> # Poligoni regolari con la grafica della tartaruga: Quadrato # lettura delle librerie from pyturtle import TurtlePlane , Turtle

# Definizioni di funzioni def quadrato():

"""Disegna un quadrato di lato 50."""

for cont in range (4):

tina.forward(50)

tina.left(90)

# Programma principale

foglio = TurtlePlane()

tina = Turtle()

quadrato()

Controlliamo che funzioni, correggiamo eventuali errori, ... Bene, è il nostro primo programma e possiamo esserne soddisfatti. Questo programma ci fornirà la base per scrivere tutti gli altri.

Osservazione

Possiamo richiamare una funzione anche dall'interno di un'altra funzione. Per esempio potremmo definire bandierina come un'asta seguita da un quadrato:

def bandierina():

"""Disegna una bandierina quadrata."""

tina.forward(40)

quadrato()

tina.back(40)

Aggiungi questa funzione al programma (dove?) e fa disegnare una bandierina.

Poi usando l'iterazione (dove?) fa disegnare una rosa di bandierine.

È cosa molto buona avere tutti gli elementi in vista quando si programma: quindi disponi le finestre in modo da avere uno spazio per la finestra grafica in alto a sinistra. Abbassa la Shell di IDLE in modo da farla stare sotto alla finestra grafica e restringi l'editor di 01quadrato.py in modo da farlo stare a destra. In questo modo hai sempre in vista tutto quello che serve per capire la situazione.

Riassumendo

  • Un programma è un documento di testo dove sono scritte le istruzioni che l'interprete Python deve eseguire.
  • Una funzione associa ad un nome il blocco di istruzioni da eseguire. La sintassi per definire una funzione è:

def <nome>():

<istruzioni>

  • Ogni funzione è bene che abbia una propria stringa di documentazione, “docstring”.
  • Per eseguire una funzione basta scrivere il nome della funzione seguito da una coppia di parentesi tonde.
  • Una volta definita una funzione, può essere eseguita dalla shell o dall'interno di un'altra funzione.
  • Un programma ben strutturato è composto dai seguenti elementi:

<intestazione>

<lettura delle librerie>

<definizioni>

<programma principale>

Parametri

modifica

Come disegnare mille quadrati diversi con un'unica funzione.

Abbiamo visto che se devo disegnare tanti quadrati, invece che ripetere tante volte i ciclo for è molto più sensato definire una funzione da richiamare tutte le volte ce mi serve usando semplicemente il suo nome.

Ora se ho definito la funzione quadrato, posso disegnare quanti quadrati voglio, in tutte le posizioni e in tutte le direzioni, ma saranno tutti quadrati congruenti. E se mi servissero quadrati grandi e quadrati piccoli? Vediamo come gli informatici hanno risolto questo problema.

Quadratini, quadratoni... quadrati!

modifica

Creiamo una nuova finestra di editor e salviamola con il nome xxquadrati.py (dove con xx intendo il numero di programmi a cui siete arrivati). Poi, nella sezione delle funzioni definiamo la funzione quadrato come abbiamo già fatto nel primo programma. Poi scriviamo il programma principale, quello che crea un foglio, crea una tartaruga e disegna un quadrato.

Ricordiamoci anche di scrivere i commenti. Dovrebbe essere qualcosa di simile a questo:

# <data> # <nome> # Poligoni regolari con la grafica della tartaruga: Quadrato

# lettura delle librerie from pyturtle import TurtlePlane , Turtle # Definizioni di funzioni

def quadrato():

"""Disegna un quadrato di lato 50."""

for cont in range (4):

tina.forward(50)

tina.left(90)

# Programma principale

foglio = TurtlePlane()

tina = Turtle()

quadrato()

Eseguiamo il programma (tasto <F5>) e correggiamo tutti gli errori finché non otteniamo il nostro quadrato. Ricordiamoci di disporre le finestre in modo da poter vedere contemporaneamente il codice che abbiamo scritto, il foglio su cui disegna la tartaruga e la finestra della shell su cui appaiono eventuali messaggi.

Nei prossimi programmi avremo bisogno di quadrati grandi e di quadrati piccoli. Non è un problema scrivere una funzione quadratino e una funzione quadratone, ad esempio:

def quadratino(): """Disegna un quadrato di lato 10."""

for cont in range (4):

tina.forward(10)

tina.left(90)

def quadratone():

"""Disegna un quadrato di lato 100."""

for cont in range (4):

tina.forward(100)

tina.left(90)

Osservazione: tra due funzioni lasciate sempre una linea vuota, migliora la leggibilità del codice.

Modifichiamo anche il programma principale in modo che disegni tutti tre i quadrati.

Proviamolo e correggiamo tutti gli errori.

Le due funzioni fanno il loro dovere, ma non siamo soddisfatti, ci resta un dubbio. Se avessi bisogno anche di un quadratino di lato 7 o di lato 9.2 o di lato ... dovrei riempire il mio programma di funzioni quasi del tutto uguali.

Mettiamo in evidenza in che cosa sono diverse le funzioni precedenti. L'unica cosa che cambia da una all'altra è un numero. Possiamo sostituire quel numero con un parametro, cioè una variabile che una volta valga 10 una volta 100 e quando mi serve un quadrato di lato 25.6 valga esattamente 25.6! Il parametro deve essere dichiarato all'interno delle parentesi che seguono il nome della funzione e deve avere un nome che sia significativo per noi, nel nostro caso un buon nome per il parametro può essere “lato”. Modifichiamo la funzione quadrato in questo modo:

def quadrato (lato):

"""Disegna un quadrato dato il lato."""

for cont in range (4):

tina.forward(lato)

tina.left(90)

La sintassi completa di una funzione è dunque:

def <nome>([<parametri>]):

<istruzioni>

Ora se tentiamo di eseguire la funzione quadrato con il comando:

>>> quadrato()}

Traceback (most recent call last):

File <pyshell>, line 1, in <module>

quadrato()

TypeError: quadrato () takes exactly 1 argument (0 given)

... invece della figura otteniamo un messaggio di errore. L'ultima riga ci dice quale difficoltà ha incontrato Python nel tentare di eseguire il nostro comando: la funzione quadrato() richiede esattamente un argomento, noi gliene abbiamo dati zero. Dobbiamo dire a quadrato quanto grande lo vogliamo:

>>> quadrato(25.6)

Questa volta appare il quadrato e con questa unica funzione possiamo disegnare quadrati di tutte le dimensioni. Il numero posto tra parentesi, che si chiama “argomento”, viene associato alla parola “lato”, che si chiama “parametro” e quando viene dato alla tartaruga il comando di andare avanti le viene detto di andare avanti esattamente di quella quantità associata al parametro.

Modifichiamo il programma principale in modo che disegni i tre quadrati chiamando solo la funzione quadrato con diversi argomenti.

Riassumendo

  • È possibile definire delle funzioni con alcuni valori variabili, il nome delle variabili vanno inseriti tra la coppia di parentesi che segue il nome della funzione. La sintassi per definire una funzione è:

def <nome>([<parametri>]):

<istruzioni>

  • I parametri sono zero o più nomi eventualmente separati da virgole.
  • Al momento dell'esecuzione si deve mettere tra le parentesi che seguono il nome della funzione tanti argomenti quanti sono i parametri:

<nome>([<argomenti>])

  • Gli argomenti sono zero o più oggetti eventualmente separati da virgole.

Altri parametri

modifica

Come costruire funzioni sempre più flessibili

Perché fermarsi alla dimensione dei poligoni? Una volta usati i parametri per disegnare quadrati grandi e piccoli, possiamo generalizzare il meccanismo. In un nuovo file di nome poli.py, scriviamo le due procedure per disegnare quadrati e triangoli di lato variabile...:

# <data> # <nome> # Poligoni regolari con la grafica della tartaruga: parametri

# lettura delle librerie

from pyturtle import TurtlePlane, Turtle

# Definizioni di funzioni

def triangolo(lato):

"""Disegna un triangolo dato il lato."""

for cont in range (3):

tina.forward(lato)

tina.left(120)

def quadrato(lato):

"""Disegna un quadrato dato il lato."""

for cont in range (4):

tina.forward(lato)

tina.left(90)

# Programma principale

foglio = TurtlePlane()

tina = Turtle()

triangolo(20)

quadrato(20) Evidenziamo le differenze tra una funzione e l'altra ce ne sono due: il numero di lati, il quadrato ne ha 4 e il triangolo 3, e l'ampiezza dell'angolo esterno, 90 per il quadrato, 120 per il triangolo. Proviamo a cercare una relazione che leghi il numero di lati all'ampiezza dell'angolo esterno.

Aggiungiamo, nel programma precedente le funzioni che disegnino altri poligoni e completiamo la tabella relativa a numero di lati e angolo esterno.

Quando i lati aumentano gli angoli diminuiscono... il prodotto tra numero di lati e angolo è sempre lo stesso... L'ultima riga della tabella avrà il prodotto uguale a 360 e l'angolo esterno uguale a 360 / n.

Nelle funzioni precedenti al posto di 90, possiamo scrivere 360/4 e al posto di 120, 360/3:

# Definizioni di funzioni

def triangolo(lato):

"""Disegna un triangolo dato il lato."""

for cont in range (3):

tina.forward(lato)

TABELLA 1: Relazione lati - angolo

numerolati angoloesterno prodotto
2 ?
3
4
5
6
7
8
9
10
n

tina.left(360/3)

def quadrato(lato):

"""Disegna un triangolo dato il lato."""

for cont in range (4):

tina.forward(lato)

tina.left(360/4)

Osservazione la divisione in Python 2.x se opera su due numeri interi dà come risultato un numero intero:

>>> print 10/3

3}

Se almeno uno dei due operandi è un numero in virgola mobile, il risultato sarà in virgola mobile:

>>> print (10./3)

3.33333333333

In Python 3.x la divisione tra due numeri interi dà sempre come risultato un numero in virgola mobile:

>>> print (10/3)

3.33333333333

Per fare in maniera che il programma si comporti allo stesso modo in Python 2.x e in Python 3.x modifichiamo il programma aggiungendo, all'inizio, la seguente istruzione:

from future import division

Riprendiamo ora le due funzioni triangolo(lato) e quadrato(lato), confrontandole possiamo osservare che l'unica differenza è un “3” in triangolo che diventa “4” in quadrato, è facile generalizzare la funzione, possiamo scrivere la funzione poligono con due parametri: “numlati” e “lunglato” e far disegnare a questa funzione poligoni regolari qualunque.

Ora il programma sarà:

# <data> # <nome> # Poligoni regolari con la grafica della tartaruga: parametri

# lettura delle librerie from pyturtle import TurtlePlane, Turtle

# Definizioni di funzioni def triangolo(lato):

"""Disegna un triangolo dato il lato."""

for cont in range (3):

tina.forward(lato)

tina.left(120)

def quadrato(lato):

"""Disegna un triangolo dato il lato."""

for cont in range (4):

tina.forward(lato)

tina.left(90)

def poligono(numlati,lunglato):

"""Disegna un poligono regolare dati:

il numero di lati: numlati,

la loro lunghezza: lunglato."""

for cont in range (numlati):

tina.forward(lunglato)

tina.left(360/numlati)

# Programma principale foglio = TurtlePlane()

tina = Turtle()

poligono(3, 20)

poligono(4, 20)

Osservate che è stata aggiunta la lettura di una libreria, è stato cambiato anche il programma principale e ora le due funzioni quadrato(lato) e triangolo(lato) non sono più utilizzate.

Definendo la funzione poligono(numlati, lunglato) abbiamo insegnato a Python a riconoscere ed eseguire un nuovo comando che permette di disegnare qualunque poligono regolare.

Riassumendo

  • È possibile definire delle funzioni con più parametri. Aumentare il numero di parametri permette di rendere la funzione più flessibile, ma, in compenso, può rendere più difficile capire il suo comportamento. È importante scegliere, per i parametri, dei nomi significativi.
  • Quando si vuole eseguire una funzione, si deve porre all'interno delle parentesi che seguono il nome della funzione stessa, tanti argomenti quanti sono i parametri presenti nella definizione della funzione. Ad esempio, se la funzione ha 2 parametri avrà bisogno di 2 argomenti.

Problemi

modifica

Come affrontare e risolvere un problema complesso.

Abbiamo visto come scrivere funzioni sempre più flessibili, ma in genere chi programma si trova a dover risolvere problemi più complessi, cioè costituiti da più parti messe in relazione tra di loro.

La prima attività da compiere quando si affronta un problema complesso è quella di analizzare il problema. Analizzare un problema significa:

  • Scomporlo in problemi più semplici.
  • Studiare le relazioni tra le parti.
  • Analizzare ogni singola parte fino a ottenere parti semplici.
 
FIGURA 4: Analisi del disegno di una casa.

Applichiamo questo metodo a un problema abbastanza semplice: disegnare una casa stereotipata.

Lo strumento che usiamo per l'analisi è il “grafo ad albero”. Il grafo richiama un po' la struttura di un albero rovesciato: la radice in alto e le foglie in basso. Nella radice viene messo il risultato finale, quello che vogliamo ottenere.

Da questa partono alcuni rami che conducono a nodi che contengono le parti in cui il disegno può essere scomposto. A loro volta, queste parti vengono scomposte in altre parti fino ad arrivare a elementi abbastanza semplici da essere disegnati da una sola funzione. In ognuno di questi elementi deve essere indicata la posizione di partenza e di arrivo della tartaruga e un nome.

In questo grafo ad albero:

  • Le varie parti della casetta costituiscono i nodi.
  • Le frecce costituiscono i rami.
  • Il nodo da cui parte tutto l'albero è la radice.
  • I nodi da cui non parte alcun ramo sono le foglie.

La sua analisi è abbastanza semplice, si riduce ad un cespuglio formato da una radice, quattro rami e quattro foglie.

Metodo bottom-up

modifica

Per disegnare la casa bisogna riuscire a disegnare un triangolo, un quadrato, un rettangolo e un parall(elogramma).

Scritta l'intestazione e le due righe che importano le librerie necessarie, passiamo scrivere le quattro funzioni necessarie. Perché tutto si incastri correttamente bisogna anche stabilire le dimensioni delle varie parti in modo coerente. Se costruiamo un triangolo e un quadrato di lato 50 allora il rettangolo dovrà avere una dimensione di 50 e l'altra potrebbe essere di 80. Il parallelogramma dovrà avere i lati di ... e il primo angolo esterno di ... gradi.

Per quanto riguarda la quinta funzione, casa(), si potrebbe pensare di spostare la tartaruga nel punto comune a tutti quattro i moduli che compongono la casa, disegnarli e poi riportare la tartaruga dove era stata presa. La funzione potrebbe somigliare a:

def casa():

"""Disegna una casa."""

tina.forward(50)

tina.left(90)

tina.forward(50)

tina.left(30)

triangolo()

tina.left(60)

quadrato()

tina.left(90)

r e t t angolo ( ) tina.left(90)

parall()

tina.left(90)

tina.back(50)

tina.right(90)

tina.back(50)

Eseguendo questa funzione possiamo vedere che l'analisi presentava un errore. Nella realtà i processi non sono lineari spesso una fase di lavoro comporta una revisione delle fasi precedenti.

Metodo top down

modifica

Ora ci poniamo il problema di scrivere le funzioni che disegnano un orribile ragnetto.

 
FIGURA 5: Un orribile ragnetto.

Per prima cosa analizziamo il problema che è più complesso del precedente. Possiamo suddividere il problema nei due sotto problemi: disegnare il corpo e la testa. Possiamo considerare come primitivi i disegni della circonferenza e dell'arco che disegna la testa quindi restano da disegnare le zampe e le antenne. Da notare però che le zampe destre sono diverse da quelle sinistre. Per complicarci un po' la vita possiamo realizzare un ragno con una dimensione variabile.

 
FIGURA 6: Analisi dell'orribile ragnetto.

A questo punto possiamo passare a scrivere il codice: un nuovo file, salvarlo con un nome adatto, poi:

  • La solita intestazione che dà le informazioni di base.
  • La lettura delle librerie.
  • Lo spazio per le funzioni.
  • Il programma principale.

Dovremmo aver scritto qualcosa che assomiglia a questo: # <data> # <nome>

# Un orribile ragnetto

# lettura delle librerie

from future import division

from pyturtle import TurtlePlane, Turtle

# Definizioni di funzioni

# Programma principale

foglio = TurtlePlane()

tina = Turtle()

ragno(50)

Abbiamo scritto il programma principale che produce il nostro disegno, ora lo proviamo: <F5>.

Effettivamente non è molto credibile che un programma siffatto possa disegnare il ragnetto che abbiamo in mente noi. Analizziamo il messaggio di errore ottenuto:

Traceback (most recent call last):

File .../06ragno.py, line15, in <module>

ragno()

NameError: name 'ragno' is not defined

  • La prima riga ci dice che c'è un errore.
  • La seconda ci dice dove si trova l'errore.
  • La terza riporta la riga di programma incriminata.
  • La quarta ci dice perché Python non è in grado di eseguire il programma.

L'ultima riga del messaggio ci dice che Python non sa eseguire ragno(), bene partiamo da qui e diciamoglielo noi definendo la funzione che disegna tutto il ragno.

Il ragno è costituito dal corpo, da uno spostamento senza lasciare segno, dalla testa e da un altro spostamento, per rimettere la tartaruga al suo posto:

def ragno(dim):

"""Disegna un orribile ragnetto."""

corpo(dim)

tina.up()

tina.forward(dim)

tina.down()

t e s t a (dim/2) tina.up()

tina.back(dim)

tina.down()

<F5> per eseguire il programma, ma non otteniamo ancora nulla se non un altro messaggio che termina con la seguente riga:
NameError: global name 'corpo' is not defined

Python giustamente protesta che non gli abbiamo ancora detto come è fatto il corpo del ragno, non c'è problema, ci mettiamo subito al lavoro. L'addome del ragno può essere disegnato utilizzando il metodo ccircle(raggio, estensione) che permette di disegnare una circonferenza di raggio dato o un arco di circonferenza dati raggio e ampiezza dell'arco:

def corpo(dim) :

"""Disegna il corpo del ragno."""

tina.ccircle(dim) # addome del ragno

tina.left(90)

zampes(dim)

tina.left(180)

zamped(dim)

tina.left(90)

<F5> per eseguire il programma... Questa volta qualcosa viene disegnato: il pancione del ragno, ma Python si interrompe ancora con un errore:

NameError: global name 'zampes' is not defined

Bene, questo messaggio ci dice che che è ora di scrivere la funzione zampes(dim). Questa funzione dovrà disegnare le quattro zampe sinistre che sono uguali tra di loro, solo ruotate di qualche grado:

def zampes(dim):

"""Le quattro zampe sinistre del ragno."""

tina.right(30)

for cont in range (4):

zampas(dim)

tina.left(20)

tina.right(20*4)

tina.left(30)

Facendoci guidare dagli errori che man mano Python rileva, procediamo a scrivere il resto del codice. In particolare la testa usa il metodo ccircle(raggio, estensione) per tracciare un arco di 210 gradi:

def testa(dim):

"""Disegna la testa del ragno."""

tina.right(105)

tina.ccircle(dim, 210)

tina.right(105)

tina.right(30)

antenna(dim, +60)

tina.left(60)

antenna(dim, -60)}

tina.right(30)}

Top down e problemi di matematica

modifica

Normalmente per risolvere problemi di matematica, nella scuola, si propone il metodo bottom up: si parte dai dati, si trova qualcosa di utile e via via ci si avvicina all'incognita.

Questo metodo risulta di poco aiuto nella ricerca della soluzione.

Il metodo top down può fornire una guida preziosa nella soluzione dei problemi.

Prendiamo come esempio un problema di geometria solida:

Il volume di una piramide è 1000cm3 e la base è un rombo il cui perimetro è 52cm e una diagonale è di 24cm. Calcola la misura dell'altezza.

Soluzione

 

Riassumendo

  • Prima di affrontare un problema complesso, bisogna suddividerlo in partifacendone un'accurata analisi.
  • Possiamo risolvere un problema con il metodo “top-down” cioè partire dalla funzione più generale e realizzare man man mano le funzioni che sono richieste dal programma, controllando ogni volta che il programma, fino a quel punto funzioni correttamente.
  • Possiamo risolvere un problema con il metodo “bottom-up” cioè realizzare prima gli elementi più semplici, controllando che funzionino correttamente e metterli poi assieme per realizzare porzioni via via più complesse del problema.