Vous êtes sur la page 1sur 62

1 Premessa al corso di programmazione

elementare

Il mondo moderno è immerso nella tecnologia. Oggi sarebbe impensabile pensare ad


una vita slegata dagli strumenti tecnologici che usiamo tutti i giorni, che siano semplici
interruttori o automobili. I computer oggi sono ubiquitari, che ce ne accorgiamo o no;
sono nelle nostre lavatrici, nelle automobili, negli aerei, nei telefoni fino dal più semplice
e non smart.
La popolazione è sostanzialmente divisa in nativi e non nativi digitali, persone che hanno
potuto accedere al computer in età adulta o post-adolescenziale e persone che sono
nate e si sono formate in piena rivoluzione informatica. Gli approcci delle due fasce di
popolazione sono sostanzialmente diversi: La prima generalmente possiede una certa
diffidenza naturale verso il mezzo informatico, mentre la seconda lo usa con naturalezza
e propensione.
Il computer è un superbo mezzo di comunicazione, di lavoro e svago; sempre più potente
ed inserito nella vita normale. È un mezzo, per cui né buono né cattivo, dipende solo
dall'uso che ne viene fatto. Il nativo digitale a causa della sua naturalezza all'uso del
mezzo informatico è oggi un soggetto potenzialmente a rischio. La maggior parte di noi
è consapevole, per esempio, dell'approccio passivo alla televisione; della comunicazione
a senso unico che porta e di come in taluni casi questa informazione possa essere
manipolata e non corretta. Nessuno di noi però, è ancora propriamente consapevole dei
pericoli insiti nell'uso acritico e non corretto dei nostri piccoli ed ubiquitari computer.
Abbiamo telefoni intelligenti, tablet o notebook che portiamo ovunque andiamo. I nostri
figli li hanno, mini e microcomputer perennemente connessi ad Internet, dove possono
fruire più o meno liberamente dei contenuti più disparati. Vietare non è la soluzione,
impedirgli di usarli non è la soluzione ma solo un tampone temporaneo che presto
imparerà a scavalcare. Si dovrebbe insegnare ai nostri figli, nativi digitali, l'approccio
consapevole ed informato.
Gli approcci agli insegnamenti informatici vanno rivisti e va rivista l'età in cui iniziare. Le
lezioni di avviamento al computer che venivano e vengono utilizzati oggi non sono più
adatti per i nativi digitali. Accendere un computer e lanciare una applicazione per i nostri
figli è naturale come per noi accedere il televisore e cambiare canale. Usare un gioco,
che è un software, è naturale per loro. Passivamente naturale, il mito della interattività è
solo un percorso preordinato tra tanti progettati a monte dell'utente e si è passivi
nell'illusione di non esserlo. Bisogna passare dalla semplice visione strumentale e
alfabetizzazione informatica alla fase di acquisizione delle competenze digitali.
Per questo motivo, si dovrebbe insegnare la programmazione dei computer ai bambini in
età scolare. Non per creare una generazione di programmatori (come mi si è accusato),
ovviamente, ma per rendere delle persone consapevoli ed attive di fronte al mezzo
informatico.
Programmare un computer è affrontare e risolvere problemi, problem posing and
solving, è imparare a ordinare i pensieri per poter realizzare le idee. L'approccio può
essere multidisciplinare e il metodo applicabile in molti campi e non necessariamente
informatici. Lo studio della programmazione è quindi, oltre alla sua mera applicazione,
una formidabile palestra di logica che guida il ragazzo attraverso un percorso che passa
per l'analisi, la scomposizione dei problemi, la verifica dei risultati e l'organizzazione del
pensiero.
Imparare a programmare, insegna la collaborazione e la condivisione (se insegnata nella
maniera adeguata) tramite un processo di trial and error e permette allo studente di
esprimersi in molti modi. L'approccio produttivo ed anche ludico, potrebbe permettere
di attirare e coinvolgere studenti problematici e creare interesse.
Quando i bambini creano un progetto come questo, imparano anche a programmare,
ma, cosa ancora più importante, programmano per imparare. Perché imparando a
programmare, imparano mille altre cose, aprendo nuove opportunità di apprendimento.
Ripeto, è utile fare un'analogia con la lettura e la scrittura. Quando si impara a leggere e
scrivere, si aprono nuove opportunità per imparare molte altre cose. Quando si impara a
leggere, allora si potrà anche leggere per imparare. E imparare a programmare è la
stessa cosa. Se si impara a programmare, allora si potrà anche programmare per
imparare. Ora, alcune cose che si imparano sono piuttosto ovvie. Si impara qualcosa di
più su come funzionano i computer. Ma questo è solamente il punto di partenza. Quando
si impara a programmare, si apre la possibilità di imparare molte altre cose.
As kids are creating projects like this, they're learning to code, but even more
importantly, they're coding to learn. Because as they learn to code, it enables them to
learn many other things, opens up many new opportunities for learning. Again, it's
useful to make an analogy to reading and writing. When you learn to read and write, it
opens up opportunities for you to learn so many other things. When you learn to read,
you can then read to learn. And it's the same thing with coding. If you learn to code, you
can code to learn. Now some of the things you can learn are sort of obvious. You learn
more about how computers work. But that's just where it starts. When you learn to
code, it opens up for you to learn many other things.
Mitch Resnick
(http://www.ted.com/talks/mitch_resnick_let_s_teach_kids_to_code/transcript)
È nel contempo importante, insegnare come l'approccio al software ed alla sua
produzione debba essere morale spingendo lo studente verso il concetto di Open
Source, spostando il valore dal prodotto in sé al produttore (il programmatore);
combattendo la visione monopolistica delle grandi software house o software service
verso un approccio sostanziale alle tipologie software (anche solo nel rispetto delle
direttive ministeriali in materia di software da utilizzare nelle pubbliche
amministrazioni).
Non va dimenticato inoltre, all'atto pratico, come in futuro la richiesta di programmatori
diventerà altissima e quindi si potrebbe fornire agli studenti uno strumento in più.
Esistono numerose esperienze, specialmente negli U.S.A., di insegnamento della
programmazione a bambini in età scolare (k-12); mentre per l'Europa si potrebbero
citare le esperienze fatte in UK ed Estonia.

1.1 Ambienti liberi per l'insegnamento della


programmazione a bambini e ragazzi.

Per avvicinare gli studenti alla programmazione informatica esistono già numerosi
strumenti con un approccio più o meno visuale. In questa sede si prenderanno in
considerazione solo strumenti Open Source per i motivi espressi sopra.

1.1.1 Scratch

È una piattaforma sviluppata al MIT (Massachusetts Institute of Technology) nel Lifelong


Kindergarten Group (http://scratch.mit.edu/) e progettata specificatamente per
bambini di età scolare e prescolare. L'approccio è totalmente visuale con blocchi logici
colorati e di facile comprensione. È adatto per insegnare la strutturazione del flusso
delle istruzioni rendendola facilmente identificabile con risultati rapidi e non frustranti
per il bambino. Ne esistono attualmente due versioni una scritta in Smalltalk
(http://scratch.mit.edu/scratch_1.4), quella originale, mentre una seconda fruibile online
e scritta in ActionScript (http://scratch.mit.edu/projects/editor/?tip_bar=getStarted) o
scaricabile (http://scratch.mit.edu/scratch2download/).
Scratch ha dato vita a numerosi progetti simili, come per esempio AppInventor
(http://appinventor.mit.edu/explore/), Blockly (https://code.google.com/p/blockly/),
Stencyl (http://www.stencyl.com/).

1.1.2 Alice

Alice è un ambiente visuale per la creazione di mondi virtuali in 3D ed è il risultato di un


progetto della Carnegie Mellon University di Pittsburgh (Pennsylvania).

1.1.3 Hackety Hack

È un piccolo ambiente (http://hackety.com/) per l'insegnamento alla programmazione


nel linguaggio Ruby (https://www.ruby-lang.org/it/). Il linguaggio di programmazione
Ruby è nato nel 1992 scritto da Yukihiro Matsumoto. Oggi è un linguaggio molto
popolare (anche se non popolarissimo in Italia). Le sue peculiari caratteristiche di
immediatezza nell'utilizzo ne fanno un buon candidato per l'insegnamento anche a
bambini in età scolare.

1.1.4 KidsRuby

KidsRuby (http://www.kidsruby.com/) è una evoluzione di Hackety Hack. Come il suo


ispiratore usa Ruby come linguaggio.

1.1.5 Kojo
1.1.5 Kojo

Kojo è sviluppato da Lalit Pant (Himjyoti school, Dehradun - India) ed è utilizzato in varie
scuole indiane, statunitensi, inglesi e svedesi. L'approccio usato nella piattaforma Kojo
(http://www.kogics.net/kojo) è più ampio dei precedenti. Può essere rivolto a più livelli
di apprendimento ed è dotato di parti specifiche, per esempio per la sperimentazione in
ambito matematico con un laboratorio basato su GeoGebra
(http://www.geogebra.org/cms/it/). Il linguaggio utilizzato è Scala (http://www.scala-
lang.org/). Scala è un linguaggio estremamente potente e multiparadigma (Orientato
agli oggetti, funzionale) che può essere utilizzato a vari livelli, sufficientemente semplice
da poter essere insegnato in età scolare (dalla classe 4° primaria). La sua caratteristica di
linguaggio funzionale lo fa particolarmente utile nella risoluzione di problemi
matematici. I linguaggi funzionali sono una modalità di programmazione in forte ascesa
in questi ultimi anni e sicuramente lo saranno anche per i prossimi.

2 Perché Ruby direttamente e non un ambiente


facilitato.

È una scelta difficile non partire con un ambiente facilitato, come Scratch per esempio.
Scratch è sicuramente un ottimo ambiente per l'insegnamento della logica di
programmazione ai bambini. Scratch è colorato e simpatico, ha una mascotte (un gatto)
e permette velocemente di ottenere risultati con grafica e suoni, sembra un gioco. Il suo
sembrare troppo un gioco tende però a fuorviare i bambini dallo scopo primario. I nostri
ragazzi non hanno una mentalità anglosassone e sono meno organizzati e disciplinati
degli anglosassoni, il che rende ambienti troppo ludici dispersivi. I bambini nativi digitali
tenderanno a giocare troppo con gli strumenti; potendo, per esempio, facilmente
disegnare sprite (oggetti grafici) e farli muovere manualmente.
Scopo di un corso di programmazione è anche aumentare la percezione del computer
stesso, come un insieme di hardware e sistema operativo con software applicativo.
L'utente dovrebbe distinguere ed utilizzare le varie parti e capirne le relazioni, altrimenti
si potrebbe incorrere nello stesso errore: il nascondere parti sostanziali; che renderà
l'utilizzo dello strumento informatico più o meno passivo.
Inoltre, questi ambienti sono troppo orientati ad una logica di tipo imperativo e legati ad
un diagramma di flusso di, ormai, vecchia concezione. Un problema tipico dei
programmatori di computer è il cambio di paradigma come molti studi ed esperienze
dimostrano. Cambiare stile di programmazione è difficile e quindi introdurre i giovani
verso linguaggi multiparadigma potrà solo renderli più flessibili in futuro.

3 Programmare in Ruby

3.1 Premessa
Questo sarà un piccolo corso per l'avvio alla programmazione in Ruby, un linguaggio
estremamente semplice e divertente ma non per questo banale. Ruby ha in sé tutte le
caratteristiche per essere un ottimo linguaggio di programmazione multiparadigma ed
essere adatto per l'insegnamento delle basi della moderna programmazione dei
computer.
Ruby è divertente.

3.2 cosa è Ruby

Questo è copiato semplicemente dal sito web di Ruby:


Ruby è un linguaggio di equilibrio e armonia. Il suo creatore, Yukihiro “Matz” Matsumoto,
ha fuso insieme parti dei suoi linguaggi di programmazione preferiti (Perl, Smalltalk,
Eiffel, Ada e Lisp) per creare un nuovo linguaggio in grado di bilanciare programmazione
funzionale con programmazione imperativa.
Lui stesso ha detto più volte che sta continuamente "provando a rendere Ruby naturale,
non semplice", in un modo che rispecchia la vita.
Chiarificando questo concetto, Matz aggiunge:
Ruby è apparentemente semplice, ma al suo interno è molto complesso, proprio come il
corpo umano.

3.3 Cosa serve

Ruby è un linguaggio di programmazione. Un linguaggio di programmazione è, come un


linguaggio naturale, un insieme di regole e convenzioni per permetterci di parlare con i
computer. I computer sono macchine di calcolo stupide e devono essere istruite. Per
utilizzare un linguaggio di programmazione serve un software, un programma per
computer. È il problema del cane che si morde la coda, per scrivere un software ci vuole
un altro software. Quindi per usare Ruby dobbiamo installare l'interprete Ruby. Qui
potremmo entrare in una distinzione inizialmente un po' difficile: cosa è un interprete e
cosa è un compilatore. La differenza oggi è molto labile, ma comunque in generale:

l'interprete è un software che legge, interpreta direttamente ed esegue un


programma scritto in un linguaggio.
un compilatore è un software che legge, interpreta e trasforma in dati binari un
programma scritto in un linguaggio, salvando sul disco un file detto eseguibile che
potrà essere lanciato (eseguito) direttamente.

Quindi abbiamo bisogno dell'interprete Ruby.


Questo software è multipiattaforma, il che vuol dire che può funzionare (più o meno
bene) su molti sistemi operativi.
Cosa è un sistema operativo:

Un sistema operativo è il software di base del computer, senza esso non si potrebbe
utilizzare.
Di sistemi operativi ne esistono molti. Forse il più conosciuto è Microsoft Windows(tm)
ma non è il solo. Esistono le distribuzioni Linux, per esempio, il MacOSX(tm) o l'Android
che avete probabilmente nei tablet o telefoni. Ne esistono davvero molti, a pagamento
o gratuiti e per molteplici utilizzi.
Ruby funziona in molti di essi.
Ruby ha un sito web dedicato: https://www.ruby-lang.org, dove ci sono tantissime
informazioni su di esso, dalla sua storia alla documentazione e per finire al software
stesso e le indicazioni su come installarlo (cioè metterlo) nel computer che possedete.
L'interprete può essere prelevato come codice sorgente, ha una licenza Open Source,
compilarlo ed installarlo; altrimenti se ne può utilizzare una versione già pronta per il
sistema operativo che volete. Come si può vedere dalle pagine web relative:
https://www.ruby-lang.org/it/downloads/ e https://www.ruby-lang.org/it/installation/,
ci sono varie implementazioni dell'interprete.
Installate o fatevi installare la più adatta.
Vi dovete anche procurare un editor di testo per programmatori. È un più o meno
semplice programma per scrivere, diverso dal word processor che usate per scrivere.
Spesso questi editor hanno la possibilità di evidenziare la sintassi (le parole) del
linguaggio in colori diversi, permettendo di poter trovare le parti del programma che
state scrivendo a colpo d'occhio e limitandone anche gli errori di battitura.
Qui: https://www.ruby-lang.org/it/documentation/, ne trovate una lista in fondo alla
pagina, sappiate comunque che esistono tantissimi editor per programmatori che lì non
sono elencati. Alcuni sono a pagamento ed altri sono gratuiti, open Source o no.
Ruby è un linguaggio diffuso oggi, quindi un qualunque editor per programmatori avrà
una evidenziazione colorata per la sua sintassi.

3.4 Le basi

In questo capitolo vedremo la base della programmazione in Ruby, alcuni elementi


chiave e tipi di dati più diffusi.

3.4.1 Iniziamo con i numeri e le stringhe

Ruby ha anche un REPL, Read Eval Print Loop, che a parte la parola difficile sta a
significare che si possono mandare direttamente comandi all'interprete. Nella
distribuzione di Ruby c'è un programma (scritto in Ruby) che si chiama irb (interactive
ruby). Per lanciarlo dovete saper usare un minimo un terminale dei comandi. Un
terminale dei comandi è, nel vostro sistema operativo moderno, una finestra in cui lo
sfondo è spesso nero con un cursore che lampeggia. Ci potete scrivere dentro e
mandare comandi al sistema operativo.
Cercate nei vai menù del sistema qualcosa del tipo: prompt dei comandi o terminale…
Lanciata la console dei comandi ci scrivete dentro irb e premete invio.

ruby source.

irb(main):001:0> 
irb(main):001:0> 

A questo punto potete inviare comandi all'interprete e confermare premendo invio.

ruby source.

irb(main):001:0>  1 + 2 
=> 3 
irb(main):002:0> 

Se scrivete 1 + 2 avrete il risultato. Ruby è anche una calcolatrice. Provate a fare dei
calcoli con le operazioni che conoscete, saranno rispettate le regole matematiche di
precedenza degli operatori che vi sono state insegnate:

+ addizione
- sottrazione
* moltiplicazione
/ divisione
% modulo
** esponente

Bisogna stare attenti alla divisione, come la maggior parte dei linguaggi di
programmazione. Se per esempio:

ruby source.

irb(main):013:0> 10 / 7 
=> 1 
irb(main):014:0> 

potete vedere come il risultato è 1. Per trovare il resto della divisione dovete effettuare
una operazione di modulo che vi restituisce il resto:

ruby source.

irb(main):014:0> 10 % 7 
=> 3 
irb(main):015:0> 

che ovviamente è 3. Quindi l'operatore / nel caso in cui i due numeri (operandi) siano di
tipo intero eseguirà una divisione intera, troncando la parte decimale.
Per avere la parte decimale si deve fare così:

ruby source.

irb(main):015:0> 10 / 7.0 
=> 1.4285714285714286 
irb(main):016:0> 

e cioè scrivere almeno uno degli operandi nella forma cosiddetta a virgola mobile o
flottante (float). L'interprete allora saprà che dovrà eseguire una divisione non intera.
Nel caso in cui facciate una operazione di modulo con almeno un operando di tipo float

ruby source.

irb(main):016:0> 10 % 7.0 
=> 3.0 
irb(main):017:0> 

il risultato sarà come per la divisione intera ma con il tipo del risultato in virgola (float).
Ruby supporta le parentesi tonde che possono essere annidate (scritte le une dentro le
altre) liberamente. Come per gli altri linguaggi di programmazione le parentesi graffe e
quadre sono usate per altri scopi e non quello di alterare le precedenze degli operatori o
raggruppare i calcoli.

ruby source.

irb(main):017:0> (2 + 3) * 2 
=> 10 
irb(main):018:0> 

I linguaggi di programmazione sono in grado di manipolare numerosi tipi di dati e fino a


qui ne abbiamo visti due:

numeri interi (integer)


numeri in virgola mobile (float)

questi sono chiamati in Ruby Fixnum e Float rispettivamente.


Per esempio:

ruby source.

irb(main):019:0> 1.class 
=> Fixnum 
irb(main):020:0> 1.0.class 
=> Float 
irb(main):021:0> 

Vedremo in seguito cosa significa 1.class e 1.0.class, per adesso lasciamola come una
cosa magica di Ruby: la possibilità di sapere quando serve il tipo di un certo dato (la
riflessione).
Un tipo di dato importantissimo in cui Ruby eccelle sono le stringhe (String). Le stringhe
sono una sequenza di caratteri, un testo insomma, una dietro l'altra compresi gli spazi ed
i vari segni di punteggiatura.

ruby source.

irb(main):021:0> "ciao mondo" 
=> "ciao mondo" 
irb(main):022:0> 'ciao modo' 
=> "ciao modo" 
irb(main):023:0> 
Come potete vedere si possono usare sia le virgolette singole che quelle doppie. In
questo caso non c'è differenza ma in altri sì e vedremo in seguito. Le stringhe come i
numeri possono essere sommate (concatenate):

ruby source.

irb(main):024:0> "ciao" + "mondo" 
=> "ciaomondo" 
irb(main):025:0> 

ma non sottratte (almeno non così):

ruby source.

irb(main):025:0> "ciao" ‐ "mondo" 
NoMethodError: undefined method `‐' for "ciao":String 
        from (irb):25 
        from /home/nissl/bin/ruby‐2.1/bin/irb:11:in `<main>' 
irb(main):026:0> 

Come vedete l'interprete ha dato un errore e si è fermato comunicandolo. In questa


maniera possiamo sapere dove è l'errore e che tipo di errore è.
Le stringhe possono però essere moltiplicate per un numero ma non per un'altra stringa:

ruby source.

irb(main):028:0> 'pippo' * 5 
=> "pippopippopippopippopippo" 
irb(main):029:0>  

irb(main):029:0> 'pippo' * 'pippo' 
TypeError: no implicit conversion of String into Integer
        from (irb):29:in `*' 
        from (irb):29 
        from /home/nissl/bin/ruby‐2.1/bin/irb:11:in `<main>' 
irb(main):030:0> 

In questo esempio ho moltiplicato una stringa per un numero e sommata ad un'altra:

ruby source.

irb(main):031:0> "pippo" * 5 + "pluto" 
=> "pippopippopippopippopippopluto" 
irb(main):032:0> 

Come potete vedere, le regole di precedenza sono state applicate: prima la


moltiplicazione e poi la somma. Nelle stringhe è possibile fare degli errori strani.
Guardate questa:

ruby source.

'pippo è andato dall'altra parte' 

Questo è un errore. Come potete vedere la stringa inizia con una virgoletta singola
(apice singolo) e termina con una virgoletta singola. Dove termina però? La colorazione
della sintassi può aiutare a capire. Per l'interprete la stringa termina dopo la parola dall.
Il resto non è nella stringa. Per superare problemi come questo ci sono due modi
principali:

ruby source.

'pippo è andato dall\'altra parte' 
"pippo è andato dall'altra parte"

Ho usato nella prima riga il carattere \ (escape) che dice a Ruby di trattare il carattere
che subito lo segue come un carattere della stringa e non come un terminatore della
stessa. Nel secondo caso, ho racchiuso la stringa tra virgolette doppie (apice doppio) e
quindi ho potuto usare l'apice singolo liberamente.
Ora che ci siamo va capita bene la differenza tra numeri e stringhe:

25 è un numero
'25' è una stringa
"25" è una stringa
'25 * 25' è una stringa e non una operazione
25 * 25 è una moltiplicazione tra due numeri
"pippo" * 5 va bene e verrà ripetuta la parola pippo per cinque volte
5 * "pippo" non va bene perché significa pippo volte il numero 5.

Provate tutte le possibili varianti che vi vengono in mente.

3.4.2 Le variabili e gli assegnamenti

Ogni linguaggio d programmazione che si rispetti, ci permette di calcolare cose che non
sappiamo a priori, altrimenti non servirebbero a niente. Fino ad adesso avete visto come
utilizzare dei valori, ora vediamo come creare valori e recuperarli per quando ci servono.
Le variabili sono come delle scatole dove mettete delle cose e l'assegnamento è l'azione
del mettercele dentro. Direi che è semplice.

ruby source.

numero_delle_pere = 12 
numero_delle_pere_vendute = 5 

Ho messo nella variabile numero_delle_pere la quantità di pere del contadino prima di


andare al mercato. Al mercato il contadino fa il conto e mette nella variabile
numero_delle_pere_vendute le pere che ha venduto e poi:

ruby source.

numero_delle_pere_rimaste = numero_delle_pere ‐ numero_delle_pere_vendute
Il contadino ora sa che la variabile numero_delle_pere_rimaste contiene il numero 7.

ruby source.

irb(main):045:0> numero_delle_pere = 12 
=> 12 
irb(main):046:0> numero_delle_pere_vendute = 5 
=> 5 
irb(main):047:0> numero_delle_per_rimaste = numero_delle_pere ‐ numero_delle_pere_ve
=> 7 
irb(main):048:0> 

Possiamo mettere dentro la nostra scatola quello che vogliamo. La comodità è che così
noi ricordiamo più facilmente il nome piuttosto che il numero contenuto. È come quando
vogliamo telefonare alla mamma, basta col nostro telefono scrivere mamma o dire
mamma, il telefono saprà il numero.
Possiamo fare con le variabili quello che facciamo con i valori: moltiplicarle, dividerle,
sommarle o sottrarle… e tante altre cose che vedremo.

3.4.3 Scriviamo un programma

Fino ad adesso abbiamo usato per fare gli esperimenti il REPL. Adesso scriveremo un
programma.
Lanciate l'editor di testo e create un nuovo file. Sarete davanti ad un programma che vi
permetterà di scrivere.
Abbiamo un problema:
Ci sono dieci bambini che devono portare delle pere ad una festa, ognuno ha 3 pere. Tre
di questi bambini ne mangiano due ciascuno. Quante pere arriveranno alla festa.
Scrivete nel vostro editor di testo.
Questi sono i dati:

ruby source.

bambini = 10 
pere_per_bambino = 3 
bambini_che_hanno_mangiato_le_pere = 3 
pere_mangiate_per_bambino = 2 

Ora un po' di calcoli:

ruby source.

pere_totali = bambini * pere_per_bambino 
pere_mangiate = bambini_che_hanno_mangiato_le_pere * pere_mangiate_per_bambino
pere_arrivate_alla_festa = pere_totali ‐ pere_mangiate 

Aggiungete poi questo:


ruby source.

puts "Alla festa sono arrivate:" 
puts pere_arrivate_alla_festa 
puts "pere" 

Ora potete salvare il file come pere.rb


Il vostro programma completo sarà questo:

ruby source.

bambini = 10 
pere_per_bambino = 3 
bambini_che_hanno_mangiato_le_pere = 3 
pere_mangiate_per_bambino = 2 

pere_totali = bambini * pere_per_bambino 
pere_mangiate = bambini_che_hanno_mangiato_le_pere * pere_mangiate_per_bambino
pere_arrivate_alla_festa = pere_totali ‐ pere_mangiate 

puts "Alla festa sono arrivate:" 
puts pere_arrivate_alla_festa 
puts "pere" 

Avete scritto un programma per risolvere il problema delle pere. Ora dovete farlo
funzionare. Ritorniamo al terminale di prima e nella cartella dove avete salvato il file
scrivete:

sh source.

ruby pere.rb 

Il file che contiene il programma sarà caricato da ruby ed eseguito. Se tutto è andato
bene avrete avuto questo:

sh source.

➜  source  ruby pere.rb  
Alla festa sono arrivate: 
24 
pere 
➜  source 

Complimenti!
Certo non è proprio bello, il risultato lo abbiamo su tre righe, non sarebbe meglio avere:
Alla festa sono arrivate 24 pere? Direi di si.
Allora facciamo in questo modo:

ruby source.

bambini = 10 
pere_per_bambino = 3 
bambini_che_hanno_mangiato_le_pere = 3 
pere_mangiate_per_bambino = 2 
pere_mangiate_per_bambino = 2 

pere_totali = bambini * pere_per_bambino 
pere_mangiate = bambini_che_hanno_mangiato_le_pere * pere_mangiate_per_bambino
pere_arrivate_alla_festa = pere_totali ‐ pere_mangiate 

puts "Alla festa sono arrivate:" + pere_arrivate_alla_festa + "pere" 

Lo salviamo come pere2.rb e come prima scriviamo nel terminale:

sh source.

ruby pere2.rb 

Però c'è un problema:

sh source.

➜  source  ruby pere2.rb 
pere2.rb:11:in `+': no implicit conversion of Fixnum into String (TypeError)
        from pere2.rb:11:in `<main>' 
➜  source 

Ruby non è riuscito a utilizzare la variabile pere_arrivate_alla_festa come se fosse una


stringa e quindi ci avverte. Per non incorrere in questo errore dobbiamo convertire il
numero contenuto nella variabile in una stringa:

ruby source.

puts "Alla festa sono arrivate:" + pere_arrivate_alla_festa.to_s + "pere"

A pere_arrivate_alla_festa aggiungiamo .to_s e magicamente diventa una stringa.


Combiamo il programma così e salviamo come pere3.rb

ruby source.

bambini = 10 
pere_per_bambino = 3 
bambini_che_hanno_mangiato_le_pere = 3 
pere_mangiate_per_bambino = 2 

pere_totali = bambini * pere_per_bambino 
pere_mangiate = bambini_che_hanno_mangiato_le_pere * pere_mangiate_per_bambino
pere_arrivate_alla_festa = pere_totali ‐ pere_mangiate 

puts "Alla festa sono arrivate: " + pere_arrivate_alla_festa.to_s + " pere"

Eseguite il programma come prima:

sh source.
➜  source  ruby pere3.rb 
Alla festa sono arrivate: 24 pere
➜  source 

Meglio no?

3.4.4 Riflessioni fino qui

In questa ultima parte abbiamo visto delle cose strane, delle istruzioni che sicuramente
non abbiamo capito. Per primo vediamo l'istruzione puts. È un metodo. Nei linguaggi di
programmazione si sente spesso parlare di funzioni, procedure o metodi; sono tutti un
modo per indicare dei piccoli pezzi codice (il linguaggio di programmazione scritto) che
possono essere riutilizzati tramite un nome che gli abbiamo dato. In Ruby, come in altri
linguaggi simili, le funzioni vengono chiamate metodi. Il metodo puts serve per scrivere
del testo in uscita. Spieghiamo questa cosa complicata con un esempio.
Immaginate una stanza con due porte, da una si entra e da una si esce. Quella da cui si
entra, si chiama: input; quella da cui si esce: output. Mettiamo che una sera la mamma
debba uscire col babbo per una cena. La mamma si vuole truccare per farsi bella e quindi:

entra dalla porta input


dentro la stanza viene truccata da una estetista e diventa più bella
esce dalla porta output

Insomma, la mamma sono i dati del metodo (la stanza dell'estetista) che entra struccata
ed esce truccata.
I dati (numeri o stringhe) entrano come parametri nel metodo e ne escono modificati o
no.
Il metodo puts è un metodo che Ruby fornisce da solo, come molti altri, ma noi li
possiamo anche scrivere e cioè definire quando vogliamo. La definizione di puts ed il suo
uso, per esempio, potrebbe essere come questa:

ruby source.

def puts(testo_in_output) 
  testo_in_output 
end 

#si può invocare come: 
puts "ciao mondo" 

#oppure: 

puts("ciao mondo") 

Come vedete possiamo invocare (cioè chiamare o usare) il metodo in due modi. Ruby
permettere di non scrivere (omettere) le parentesi in alcuni casi. Le righe che
cominciano con # sono considerate dei commenti, del testo che possiamo scrivere
dentro il codice senza che esso venga letto dall'interprete. Il commento va dal carattere
# fino alla fine della riga. Se andate a capo senza mettere come primo carattere # Ruby
vi darà un errore. Si possono fare anche commenti su più righe.
Per ricapitolare:

ruby source.

#questo è un commento che arriva fino in fondo alla riga 

#questo commento è 
sbagliato 

=begin 
questo 
commento 
va 
bene 
=end 

La possibilità di commentare un codice è molto importante, perché così possiamo


scrivere del testo per ricordarci cosa stiamo facendo o per descriverlo ad altri che lo
leggeranno in seguito.
Vediamo ora l'altro metodo che abbiamo usato: to_s
Lo abbiamo usato in questo modo:

ruby source.

pere_arrivate_alla_festa.to_s 

per convertire un numero intero in una stringa. Il metodo to_s significa semplicemente:
to string (in stringa). Ricordate che la lingua inglese è la lingua ufficiale per i linguaggi di
programmazione, quindi le cose si chiameranno sempre in inglese.
Il metodo to_s non ha dati in entrata (input), ma ugualmente fa qualcosa. Come vedete
viene applicato invece a certi dati tramite il punto: 10.to_s fa diventare il 10 una stringa,
"10".
Dovete sapere che Ruby è un linguaggio /orientato agli oggetti (Object Oriented) e cioè
un linguaggio in cui noi possiamo descrivere o definire degli oggetti che comunicano con
altri oggetti.
Complicato?
Vediamo un po'.
Molti di voi giocheranno coi mattoncini Lego(tm) o almeno saprete sicuramente cosa
sono. Sapete che ci sono molti mattoncini di forme diverse che si attaccano insieme.
Bene! I mattoncini Lego(tm) sono come un linguaggio Object Oriented. I mattoncini sono
oggetti. Se guardate un mattoncino, noterete che è diverso sopra e sotto, per
permettervi di attaccarli insieme. La parte dove si attaccano è una interfaccia di
collegamento. I rilievi rotondi su una delle superfici sono i metodi. In Ruby si dice che
tutto è un oggetto (è un linguaggio ad oggetti puro), quindi anche le stringhe ed i
numeri.

L'oggetto delle stringhe si chiama: String.


L'oggetto dei numeri si chiama: Fixnum.

Il metodo to_s è quindi un metodo dell'oggetto Fixnum. È un metodo però che tutti gli
oggetti in Ruby ha, come molti altri; ma li vedremo in seguito.
Il modo di invocare (chiamare o call) è quello tramite la dot notation, la notazione a
punto. Insomma per farla semplice: mettete un punto tra l'oggetto e il metodo.
Ricordate come lanciare irb?

ruby source.

irb(main):001:0> 10.to_s 
=> "10" 
irb(main):002:0> io_sono_un_numero = 12 
=> 12 
irb(main):003:0> io_sono_un_numero.to_s 
=> "12" 
irb(main):004:0> 

Questi per esempio sono i metodi di Fixnum:

ruby source.

irb(main):004:0> Fixnum.methods 
=> [:allocate, :superclass, :freeze, :===, :==, :<=>, :<, :<=, :>, :>=, :to_s
irb(main):005:0> 

Come vedete sono parecchi ed alcuni anche molto strani… Cercate to_s.
Quello che ho chiamato per sapere questa lista è methods. Il metodo methods fa
questo: restituisce una lista dei nomi dei metodi dell'oggetto su cui è invocato.
Nel gergo dei linguaggi orientati agli oggetti (puri) si dice: mandare un messaggio ad un
oggetto. Quindi in pratica ho chiesto a Fixnum di dirmi tutti i nomi dei suoi metodi. Come
quando chiedete ad un amico di darvi il suo quaderno di matematica e lui ve lo da.

3.4.5 Confrontare le cose e decidere cosa fare

Cominciamo a vedere le cose serie. Un programma non è un programma se non


prendiamo decisioni. Fino ad adesso abbiamo solo dato comandi ed è il momento di
decidersi a decidere.
Per prima cosa guardiamo gli operatori booleani (si chiamano booleani da George Bool
un matematico inglese dell'800):

> maggiore di…


< minore di…
>= maggiore od uguale di…
<= minore od uguale di…
== uguale a…
!= non uguale a…
ruby source.

irb(main):001:0> 5 > 2 
=> true 
irb(main):002:0> 5 < 2 
=> false 
irb(main):003:0> 5 >= 5 
=> true 
irb(main):004:0> 5 <=5 
=> true 
irb(main):005:0> 5 == 5 
=> true 
irb(main):006:0> 5 != 5 
=> false 
irb(main):007:0> 

Con questi operatori possiamo sapere quindi se un numero è maggiore, minore od


uguale ad un altro.
Lo possiamo fare anche con le stringhe:

ruby source.

irb(main):001:0> 5 > 2 
=> true 
irb(main):002:0> 5 < 2 
=> false 
irb(main):003:0> 5 >= 5 
=> true 
irb(main):004:0> 5 <=5 
=> true 
irb(main):005:0> 5 == 5 
=> true 
irb(main):006:0> 5 != 5 
=> false 
irb(main):007:0> 

Questo non vi fa domandare cosa succede con le stringe? È interessante perché il


confronto è fatto sul valore lessicale delle stringhe.
Guardate:

ruby source.

irb(main):013:0> 'a' > 'b' 
=> false 
irb(main):014:0> 'b' > 'a' 
=> true 
irb(main):015:0> 

Capito? L'ordine lessicale è praticamente quello come nel dizionario con però un
problema:

ruby source.

irb(main):014:0> 'b' > 'a' 
=> true 
irb(main):015:0> 'B' > 'a' 
irb(main):015:0> 'B' > 'a' 
=> false 
irb(main):016:0> 

Come sarebbe? b è maggiore di a ma B no?


I computer ordinano in maniera diversa da noi, sono ancora più stupidi di quello che
avevamo fino ad ora pensato, loro ordinano prima le lettere maiuscole e poi le
minuscole (per la verità prima ordinano i numeri, poi le maiuscole e dopo le minuscole).
Questo è un problema.
Dobbiamo quindi stare attenti, ma fortunatamente Ruby ci aiuta con il metodo
downcase che mette la stringa tutta in minuscolo o upcase che la rende tutta maiuscola.

ruby source.

irb(main):018:0> 'B'.downcase 
=> "b" 
irb(main):019:0> 'B'.downcase > 'a'.downcase 
=> true 
irb(main):020:0> 'B' > 'a' 
=> false 
irb(main):021:0> 'a'.upcase 
=> "A" 
irb(main):022:0> 

Ora abbiamo capito gli operatori booleani e che ci facciamo? Li usiamo per decidere cosa
fare.
Prendete l'editor che scriviamo un programma nuovo.

ruby source.

puts "Quanti anni hai?" 

eta = gets 

if eta == 9 
  puts "fai la quarta elementare" 
else 
  puts "non fai la quarta elementare" 
end 

Salvate come avete imparato con il nome test.rb e poi eseguitelo:

sh source.

➜  source  ruby test.rb  
Quanti anni hai? 

non fai la quarta elementare 

Ora però proviamo scrivendo nove quando lo chiede:

sh source.
➜  source  ruby test.rb  
Quanti anni hai? 

non fai la quarta elementare 

Qualcosa non va.


Intanto capiamo cosa è gets. Ricordate puts e gli input e output… La mamma che va a
cena? Bene. Il metodo gets carica i dati dall'input', cioè ci chiede di scrivere qualcosa e
premere il tasto invio. I dati che prende li può caricare dentro una variabile. Noi abbiamo
messo l'età dentro la variabile /eta (non ha l'accento perché come vi ho già detto i
linguaggi di programmazione sono in inglese e l'inglese non ha accenti di questo tipo,
quindi non tutti i caratteri possiamo usare per i nomi delle variabili o dei metodi. Non si
possono usare nemmeno i caratteri degli operatori e se ci fate caso neanche gli spazi… ),
poi con un test abbiamo controllato il valore:

ruby source.

if eta == 9 
  puts "fai la quarta elementare" 
else 
  puts "non fai la quarta elementare" 
end 

cioè: se (if) eta è uguale a 9 scrivi fai la quarta elementare altrimenti (else) scrivi non fai
la quarta elementare. Noi però abbiamo scritto 9, perché non ha funzionato? Ora ve lo
spiego: il metodo gets legge i caratteri che scriviamo ma non sa che quelli sono numeri;
per lui sono stringhe.
Ruby è un linguaggio dinamico e permette di valutare e confrontare le pere con le mele.
La maestra vi avrà sicuramente detto che non si confrontano le mele con le pere… La
maestra ha ragione, ma i linguaggi di programmazione spesso non vanno proprio a
braccetto con la matematica.
Scrivere così è perfettamente lecito:

ruby source.

irb(main):023:0> "pere" == 10 
=> false 
irb(main):024:0> 

e giustamente restituisce il valore falso (false). Come mai?


Perché una stringa non è un numero.
Ruby, se i tipi del confronto non sono uguali o riconducibili, non confronta i valori ma i
tipi stessi. Sono sicuro che vi cominci ad arrivare un po' di mal di testa, quindi vediamo.
La stringa pere è di tipo String, il numero 10 è di tipo Fixnum (tutto questo lo abbiamo
visto prima), Ruby lo sa che i tipi sono diversi e non può confrontare i valori, quindi ci
dice che un tipo String non è un tipo Fixnum.
Lo so che la storia dei tipi è un po' complicata, ma piano piano la capirete e vedrete che è
ganzissima.
Ritornando al problema di prima, quello del 9. Per far funzionare il programma noi
dobbiamo convertire eta in un numero con il metodo to_i (to integer):

ruby source.

puts "Quanti anni hai?" 

eta = gets.to_i 

if eta == 9 
  puts "fai la quarta elementare" 
else 
  puts "non fai la quarta elementare" 
end 

Però sarebbe meglio anche aggiungere un altro metodo: chomp; che rimuove il carattere
di invio. Quando scrivete 9 e date invio, anche il carattere di invio viene mandato al
programma. Ecco, chomp rimuove il carattere di invio dalla stringa (il carattere invisibile
newline ovvero nuova riga).

ruby source.

puts "Quanti anni hai?" 

eta = gets.chomp.to_i 

if eta == 9 
  puts "fai la quarta elementare" 
else 
  puts "non fai la quarta elementare" 
end 

Qui vedete anche un'altra cosa interessante, i metodi possono essere chained
(concatenati); messi uno dopo l'altro e concatenati con il punto. Ogni metodo passerà a
quello dopo il valore che ha elaborato:
gets legge '9' + newline, lo passa a chomp che leva il newline e passa '9' a to_i che lo
trasforma in un intero dal valore 9
La condizione if valuta sempre una espressione che restituisce un valore booleano: true
o false, vero o falso; quindi possiamo utilizzare una qualunque espressione che
restituisce vero a falso. Ricordatelo, servirà. La condizione if può essere semplice:

ruby source.

if a > 1 
  puts 'a è maggiore di uno' 
end 

o come abbiamo visto prima una alternativa contrassegnata dalla parola chiave
(keyword) else (altrimenti). La cosa interessante è che dentro un blocco if..end o
if..else..end possiamo mettere dentro, annidare, altre condizioni:

ruby source.

if a > 1 
if a > 1 
  puts 'a è maggiore di 1' 
else 
  if a == 0 
    puts 'a è uguale zero' 
  else 
    puts 'a è minore di zero' 
  end 
end 

Si potrebbe anche scrivere meglio, ma va bene per capire. Come vedete il test prima
controlla se a è maggiore di 1, se non lo è ci chiediamo: a è uguale a zero? se lo è lo
scriviamo, altrimenti sarà per forza minore di zero. In questa maniera, annidando delle
condizioni abbiamo valutato i tre possibili stati di un numero:

è maggiore di zero
è zero
è minore di zero

Se modifichiamo il programma di prima e lo salviamo come test2.rb:

ruby source.

puts "Quanti anni hai?" 

eta = gets.to_i 

if eta == 9 
  puts "fai la quarta elementare" 
else 
  if eta < 9 
    puts "sei ancora piccolo e fai la prima o la seconda o la terza" 
  else 
    puts "Sei già laureato?" 
  end 
end 

Pensate a quanti giochini potremmo fare con la possibilità di annidare le condizioni.


Questo in gergo si chiama: ramo decisionale. Visto che parliamo di ramo decisionale
prima di guardare i cicli (loop) vediamo un altro modo per prendere le decisioni. È un
modo molto potente ed utile, che ci permette di scrivere meglio il programma di prima.
Salvatelo come test3.rb ed eseguite come avete imparato:

ruby source.

puts "Quanti anni hai?" 

eta = gets.to_i 

case eta 
when 6 
  puts 'fai la prima elementare' 
when 7 
  puts 'fai la seconda elementare' 
when 8 
when 8 
  puts 'fai la terza elementare' 
when 9 
  puts 'fai la quarta elementare' 
when 10 
  puts 'fai la quinta elementare' 
else 
  if eta > 10 
  puts 'Vai alle medie?' 
  else 
    puts "Fai ancora l'asilo" 
  end 
end 

Ho usato l'espressione condizionale case..when..end. È nella sua forma completa:


if..when..else..end. Vediamo che succede in questo caso. Questa espressione è come una
cassettiera con i cassetti numerati, ci sono cinque cassetti numerati 6,7,8,9,10 e un
ultimo dove si mette tutto quello che non va negli altri. Qui ho usato anche una
condizione if..else..end per diramare ulteriormente il test.
Eseguendolo potete vedere i risultati:

sh source.

➜  source  ruby test3.rb  
Quanti anni hai? 

Fai ancora l'asilo 
➜  source  ruby test3.rb 
Quanti anni hai? 

fai la prima elementare 
➜  source  ruby test3.rb 
Quanti anni hai? 
11 
Vai alle medie? 
➜  source  ruby test3.rb 
Quanti anni hai? 
10 
fai la quinta elementare 
➜  source 

L'espressione case..when..end è davvero molto utile, cose come queste ci sono i tutti i
linguaggi di programmazione anche se a volte meno potenti. In Ruby potete valutare
praticamente qualunque cosa.
Ecco una stringa:

ruby source.

case nome 
  when 'pippo' 
    puts 'ti chiami pippo' 
  else 
    puts 'non ti chiami pippo' 
end 
Simuliamo una condizione if..else..end:

ruby source.

test_booleano = true 

case test_booleano 
  when true 
    puts 'vero' 
  when false 
    puts 'falso' 
end 

L'espressione case..when..end è utilissima e spesso mantiene il codice pulito e


facilmente leggibile, provate e divertitevi.
Nelle espressioni condizionali possiamo fare più test insieme. Come? Con gli operatori
logici:

or che si può scrivere spesso anche come ||


and che si può scrivere spesso anche come &&
not che si può scrivere spesso anche come !

La differenza tra quello scritto e quello a simbolo (and e &&) sta nel livello di precedenza
di valutazione: la versione scritta ha una precedenza più bassa. Normalmente non ve ne
curate, ma può causare a volte problemi subdoli. Nel dubbio è preferibile la versione
simbolica.

ruby source.

if a >= 0 && a <= 9 
  puts 'la variabile a è compresa tra 0 e 9' 
end 

Qui abbiamo detto a Ruby: se la variabile a è maggiore od uguale 0 e la variabile a è


minore od uguale a 9, scrivi…

ruby source.

if nome == 'pippo' || nome == 'pluto' 
  puts 'ti chiami pippo o pluto' 
else 
  puts 'il tuo nome è un altro' 
end 

Qui invece: se la variabile nome contiene la stringa pippo o la variabile nome contiene la
stringa pluto, scrivi…
Si possono mettere più condizioni su uno stesso if..else..end, certo bisogna fare
attenzione, molta attenzione. Se avete condizioni multiple, dato che hanno la stessa
precedenza e vengono valutate da sinistra verso destra, a volte avrete bisogno delle
parentesi per raggruppare le valutazioni. L'operatore not è di tipo unario e quindi si
applica solo ad un operando: !true è uguale a false come non vero è uguale a falso. Gli
altri invece si dicono binari perché si applicano a due operandi. Ne esiste anche uno
terziario che serve per esprimere una condizione su una sola riga:

ruby source.

#questa riga è equivalente al test if..else..end 
condizione ? puts('vera') : puts('falsa') 

if condizione 
  puts 'vera' 
else 
  puts 'falsa' 
end 

I due test sono equivalenti.


Ruby ha anche un'altra sintassi per i test negativi: unless..else..end. In pratica: se non è
vero fai qualcosa altrimenti fai altro.

ruby source.

if a != 1 
  puts 'a è diverso da 1' 
end 

unless a == 1 
  puts 'a è diverso da 1' 
end 

I due test sono equivalenti. Il test unless vi assicuro che è davvero comodo, perché più
spesso di quanto possiate credere è più chiaro e facilmente capibile il controllare che
una cosa non sia vera.

3.4.6 Cicli, ripetere le cose da fare molte volte

I cicli ci permettono di ripete delle istruzioni più volte. Ruby ha alcuni tipi di cicli come la
maggior parte dei linguaggi di programmazione.

while..end finché la condizione è vera fai qualcosa


until..end finché la condizione è falsa fai qualcosa
for..in..end per ogni valore all'interno di un insieme di valori fai qualcosa

È un po' come if e unless.


Ora vi dico una cosa, è abbastanza raro usare questi cicli in Ruby e lo vedrete più avanti.
Scrivete nel vostro editor il programma che segue, salvatelo come loop.rb ed eseguitelo:

ruby source.

puts 'Inserisci un numero' 
numero = gets.chomp.to_i 

while numero != 10 
while numero != 10 
  puts 'il mumero è diverso dal numero segreto' 
  puts 'Inserisci un numero' 
  numero = gets.chomp.to_i 
end  

puts 'hai trovato il numero segreto. Complimenti!' 

Il ciclo eseguirà le istruzioni date dentro di lui finché il numero inserito non sarà 10.
Se adesso uso until invece di while:

ruby source.

puts 'Inserisci un numero' 
numero = gets.chomp.to_i 

until numero != 10 
  puts 'il mumero è diverso dal numero segreto' 
  puts 'Inserisci un numero' 
  numero = gets.chomp.to_i 
end  

puts 'hai trovato il numero segreto. Complimenti!' 

Visto che viene valutata la falsità funzionerà al contrario.


Il ciclo for invece prenderà un valore da un insieme di valori assegnandolo ad una
variabile fino ad esaurire tutti i valori. Sembra complicato ma non lo è molto.

ruby source.

numeri = [0,1,2,3,4,5,6,7,8,9] 

for numero in numeri 
  puts numero 
end 

Se lo eseguite come sapete:

sh source.

➜  source  ruby loop2.rb 










➜  source 

Il programma ha stampato tutti i numeri compresi nell'array chiamato numeri. Vediamo


però subito cosa è un array (tipo Array). È una collezione di elementi, in questo caso
numeri. Non l'ho chiamato insieme perché esiste nella libreria Ruby un tipo Set che serve
apposta per gli insiemi matematici. È più corretto chiamare questi tipi di dati collezioni e
noi vogliamo imparare il modo più corretto possibile di nominare le cose.
Agli elementi contenuti in una collezione si può accedere direttamente se si vuole:

ruby source.

array = [1,2,3,4,5,6] 

primo_elemento = array[0] 
secondo_elemento = array[1] 

Vi si accede tramite quello che si chiama indice dell'array. Gli array sono indicizzati a
partire da zero, questo lo dovete ricordare bene. Ci sono anche i metodi first e last che
come dicono i loro nomi accedono al primo e l'ultimo elemento. A volte ci interessa
anche sapere quanti elemeti ci sono nell'array e quindi abbiamo size.
Qualcuno furbo ha capito l'inghippo della indicizzazione da zero?

ruby source.

irb(main):003:0> array = [0,1,2,3,4,5,6,7,8,9] 
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):004:0> array.first 
=> 0 
irb(main):005:0> array.last 
=> 9 
irb(main):006:0> array.size 
=> 10 
irb(main):007:0> array[0] 
=> 0 
irb(main):008:0> array[3] 
=> 3 
irb(main):009:0> array[array.size] 
=> nil 
irb(main):010:0> array[array.size ‐ 1] 
=> 9 
irb(main):011:0> 

Capito?
Una nota: il valore nil è un valore particolare che ha un significato analogo al latino nihil
cioè il nulla. È il valore che si ha quando si leggono cose che non esistono: variabili,
valori…
Insomma un array è come una scatola con gli scompartimenti ed ogni scompartimento
può contenere qualsiasi tipo di dato supportato da Ruby: numeri, stringhe, array… Non
solo, un array in Ruby contrariamente ad altri linguaggi può mischiare i tipi.

ruby source.

array = [1, 2, "pippo", 3, "pluto", ['a','b','c']] 

Immaginate adesso le odiose tabelline, quelle che la maestra vi chiede continuamente…


Bene, la tabella pitagorica è un array (in questo caso a due dimensioni).
Qualcuno furbo ha già capito come far fare una tabella pitagorica a Ruby?

ruby source.

numeri = [1,2,3,4,5,6,7,8,9,10] 

for numero in numeri 
  riga = '' 
  for moltiplicatore in numeri 
    valore = numero * moltiplicatore 
    if valore < 10 
      separatore = '  ' 
    else 
      separatore = ' ' 
    end 
    riga = riga + valore.to_s + separatore 
  end 
  puts riga 
end 

Eseguitelo…

sh source.

➜  source  ruby tabellina.rb  
1  2  3  4  5  6  7  8  9  10  
2  4  6  8  10 12 14 16 18 20  
3  6  9  12 15 18 21 24 27 30  
4  8  12 16 20 24 28 32 36 40  
5  10 15 20 25 30 35 40 45 50  
6  12 18 24 30 36 42 48 54 60  
7  14 21 28 35 42 49 56 63 70  
8  16 24 32 40 48 56 64 72 80  
9  18 27 36 45 54 63 72 81 90  
10 20 30 40 50 60 70 80 90 100  
➜  source 

Che ne dite? Va bene? (Si potrebbe anche fare meglio, ma per ora va bene così).
Guardate bene il codice e cercate di capire, avete tutte le informazioni per farlo.
Come vi ho già detto, i cicli in Ruby non sono molto usati perché ha anche altri modi di
fare la stessa cosa in maniera più compatta e leggibile.

ruby source.

numeri = [1,2,3,4,5,6,7,8,9,10] 

tabella = numeri.map do |numero| 
  riga = numeri.map do |moltiplicatore| 
    valore = numero * moltiplicatore 
    separatore = valore < 10 ? '  ' : ' ' 
    valore.to_s + separatore 
  end 
  riga.join 
end 
puts tabella.join("\n")
puts tabella.join("\n")

Questo ne è un esempio, anche se non pare adesso quando sarete più bravi vi sembrerà
meglio di quello sopra:

ruby source.

numeri = [1,2,3,4,5,6,7,8,9,10] 

puts (numeri.map { |numero| 
        (numeri.map { |moltiplicatore| 
          valore = numero * moltiplicatore 
          separatore = valore < 10 ? '  ' : ' ' 
          valore.to_s + separatore 
        }).join 
      }).join("\n") 

Per arrivare magari a questo:

ruby source.

numeri = [1,2,3,4,5,6,7,8,9,10] 
puts (numeri.map { |numero| 
        (numeri.map { |moltiplicatore|  
          (valore = numero * moltiplicatore).to_s + (valore < 10 ? '  ' : 
        }).join 
      }).join("\n") 

Forse troverete anche altri modi… Negli ultimi due esempi, ci sono delle cose da notare
che fanno di Ruby un linguaggio particolare (detto funzionale). Il concetto non è
semplice.
Cercate di stare attenti, questa cosa che dirò adesso non è facile. Le istruzioni, le
variabili ed i metodi che usiamo in Ruby vivono dentro un contesto. Cosa è? Pensate a
casa vostra, quello è il vostro contesto. Pensate alla scuola, l'aula. L'aula è il vostro
contesto e cioè la zona chiusa dove state e dove fate qualcosa. In Ruby si possono fare
queste stanze in vari modi: con le parentesi per esempio ma anche con parole chiave
(keywords) particolari come do..end.
Le keywords do..end corrispondono a {..} e definiscono quello che viene chiamato blocco
ma anche closure (chiusura).

ruby source.

#prima forma 
do |parametro| 
  ... 
end 

seconda forma 
{ |parametro| 
  ... 

Queste forme sono closure con un valore in entrata.
Vediamo il metodo each che è un metodo del tipo Array (ma anche altri tipi lo hanno):

ruby source.

[1,2,3,4,5,6,7,8,9,10].each {|numero| 
  puts numero 

for numero in [1,2,3,4,5,6,7,8,9,10] 
  puts numero 
end 

Le due forme sono equivalenti, cioè portano allo stesso risultato e stamperanno in
output i valori contenuti nell'array. La differenza è di tipo paradigmatico. Vedo già le
vostre facce sconvolte. - E che vuol dire? -.
Un paradigma è un modo, un metodo per fare qualcosa. Delle regole. Un paradigma di
calcolo è quello che usate per fare le divisioni o altri calcoli per esempio. Si potrebbero
fare in molti modi e voi ne usate uno di questi. Nei linguaggi di programmazione ci sono
molti modi di fare le cose, molti paradigmi di programmazione.
Se noi scriviamo con il for..in..end usiamo quello che viene detto paradigma imperativo,
se usiamo il metodo each quello detto paradigma funzionale. Ruby ci permette di farlo
nei due modi e ci lascia liberi di usare quello che ci piace di più. Vedrete poi che quello
funzionale è generalmente migliore, ma lo capirete da soli.
Insomma, each è un metodo del tipo array che come parametro in input prende una
closure o una lambda o un oggetto Proc passando nel parametro di questi il valore di
ogni elemento dell'array su cui è chiamato.
Bene, ora ci vuole un mese di vacanza per riposarci!
A parte le chiacchiere e le definizioni da secchioni, una volta usata è più semplice del
previsto. Se guardate il codice sopra, immaginate che dentro numero ci vada finire
dentro di volta in volta 1 poi 2 poi 3 poi 4 e così via fino a 10 che è l'ultimo elemento
della collezione. Nel codice dentro la closure potere usare poi numero e fare quello che
volete. Qui lo stampiamo in output.
Negli esempi io ho usato il metodo map, che è simile ad each, ma mentre each invoca la
closure per ogni elemento della collezione senza fare altro, map cosa fa… restituisce
un'altra collezione coi dentro i risultati della elaborazione della closure. Nell'esempio poi
trasformo una collezione in una stringa usando il metodo join (unisci), un metodo che
prende tutti gli elementi della collezione e li concatena come una stringa.
Questo modo di ciclare (brutta parola e si dice solo qui… è come le parolacce) si chiama
più giustamente iterazione e questi metodi si chiamano iteratori. Un altro metodo
iteratore è per esempio times che però è dei numeri e non degli array:

ruby source.

10.times do  
  puts "ciao!" 
end 
Verrà scritto in output dieci volte ciao. Usare gli iteratori è molto importante e rende il
codice meglio organizzabile e leggibile.
Questa è la lista dei metodi disponibili per il tipo Array:

ruby source.

irb(main):001:0> Array.methods.sort 
=> [:!, :!=, :!~, :<, :<=, :<=>, :==, :===, :=~, :>, :>=, :[], :__id__, :__send__
irb(main):002:0> 

Ho fatto la stessa cosa fatta indietro con Fixnum, ricordate? Ho qui però usato un nuovo
metodo: sort. Il metodo sort mette in ordine un array.

3.4.7 Scriviamo i nostri metodi, altrimenti a che serve?

Fino ad adesso abbiamo visto a grandi linee cosa sono i metodi e ne abbiamo usati
alcuni. I metodi, si possono definire come le variabili. Altrimenti potremmo anche andare
a coltivale le cipolle (che sono buone e le adoro anche crude nell'insalata).
Come si fa a definire un metodo? Per prima cosa decidiamo un bel nome, un nome
importante… Soprattutto un nome che dica cosa fa, per ricordarlo meglio.

ruby source.

def somma(x, y) 
  x + y 
end 

Abbiamo definito un metodo che prende due valori in input, x ed y, che si chiama
somma. Lo possiamo usare così:

ruby source.

def somma(x, y) 
  x + y 
end 

risultato = somma(2, 3) 

puts risultato 

Fate nell'editor, salvate come metodi.rb ed eseguite. Vedrete il risultato di 2 + 3. Invece


di assegnare il valore di ritorno del metodo ad una variabile, in questo caso avreste
anche potuto scrivere:

ruby source.

puts somma(2, 3) 

Ricordate? puts è un metodo, quindi vuol dire che noi possiamo passare un metodo ad
un altro metodo come suo parametro in input.
ruby source.

puts somma(somma(2, 3), 5) 

Che farà? Quanto farà? Chi lo sa? Al primo che risponde niente compiti.
I metodi, come abbiamo già visto, servono per scrivere delle istruzioni che poi possiamo
riutilizzare. Pensate se ogni volta dovessimo riscrivere le stesse cose. Programmare è
spesso un lavoro ripetitivo e potenzialmente si scrivono le stesse cose centinaia se non
migliaia di volte. Senza i metodi (almeno quelli) saremmo perduti (anche se pensate
esistono linguaggi che non li hanno).
I metodi sono raggruppati in librerie generalmente, anche Ruby ha le sue librerie.
Insomma i metodi sono come delle parole o meglio dei capitoli dentro dei libri, che sono
sugli scaffali di una libreria. È importante ricordare questo perché vedremo in seguito
che per chiamare un metodo dobbiamo trovarlo come un libro nello scaffale:
scaffale_destro::libro_primo.capitolo_secondo.
Trasformiamo il codice per scrivere la tabella pitagorica come metodo:

ruby source.

# prepara una tabella (array bidimensionale) 
# con i valori 
def tabella_pitagorica(numeri) 
  numeri.map { |numero| 
    numeri.map { |moltiplicatore|  
      numero * moltiplicatore 
    } 
  } 
end 

# stampa un array bidimensionale 
def stampa_tabella(tabella) 
  puts (tabella.map { |riga|  
    (riga.map {|numero|  
      numero.to_s + (numero < 10 ? '  ' : ' ') 
    }).join 
  }).join("\n") 
end 

stampa_tabella(tabella_pitagorica([1,2,3,4,5,6,7,8,9,10])) 

Al solito salviamo, come metodi1.rb ed eseguiamo.

sh source.

➜  source  ruby metodi2.rb 
1  2  3  4  5  6  7  8  9  10  
2  4  6  8  10 12 14 16 18 20  
3  6  9  12 15 18 21 24 27 30  
4  8  12 16 20 24 28 32 36 40  
5  10 15 20 25 30 35 40 45 50  
6  12 18 24 30 36 42 48 54 60  
7  14 21 28 35 42 49 56 63 70  
8  16 24 32 40 48 56 64 72 80  

9  18 27 36 45 54 63 72 81 90  
9  18 27 36 45 54 63 72 81 90  
10 20 30 40 50 60 70 80 90 100  
➜  source 

Come vedete il risultato è quello. Adesso abbiamo un metodo chiamato


tabella_pitagorica che prenderà in input come parametro un array di numeri e costruirà
un array di array di numeri.

ruby source.

[[1,2,3,4,5,6,7,8,9],[2,4,6,8,10,12,14,16,18,20], [3,6,9,12,15,18,21,24,27,30], ...]

L'altro metodo, stampa_tabella, prende in entrata un array bidimensionale (la tabella coi
valori) e lo stampa in output. La cosa interessante è che stampa_tabella stampa in quel
modo, qualunque array bidimensionale e non solo la nostra tabella pitagorica.

ruby source.

# stampa un array bidimensionale 
def stampa_tabella(tabella) 
  puts (tabella.map { |riga|  
    (riga.map {|numero|  
      numero.to_s + (numero < 10 ? '  ' : ' ') 
    }).join 
  }).join("\n") 
end 

stampa_tabella([[1,10,30,4,50,6,7,8,9,0,9,0],[2,4,6,81,17,12,14,1,1,2], [3,6,9,12,15

Salvate come metodi3.rb ed eseguite:

sh source.

➜  source  ruby metodi3.rb 
1  10 30 4  50 6  7  8  9  0  9  0   
2  4  6  81 17 12 14 1  1  2   
3  6  9  12 15 18 21 24 27 30  
➜  source 

I metodi possono avere più parametri ma come consiglio limitatevi al meno possibile.

3.5 Le cose serie

Ora cominceremo ad affrontare alcune delle caratteristiche di Ruby che ne fanno un


linguaggio agile moderno.

3.5.1 Raggruppiamo i metodi in moduli

Ruby è un linguaggio estremamente modularizzabile il che significa che ha numerosi


modi di riutilizzare le cose già scritte. Questo ne fa un linguaggio molto adatto al lavoro
in collaborazione, cioè tra più persone che lavorano a parti diverse del codice.
Uno dei meccanismi di raggruppamento sono i moduli:

ruby source.

module Pippo 

  def dice(cosa) 
    puts cosa 
  end 

end 

I moduli sono dei contesti dove i metodi (ed altre cose) vivono. Prima però di usare i
metodi di un modulo, dobbiamo includerlo. Avete presente quando un amichetto viene a
casa vostra e giocate insieme? Uguale. Dovete prima invitare il modulo nel vostro
contesto. A casa vostra, insomma.

ruby source.

module Pippo 

  def dice(cosa) 
    puts "Pippo dice: " + cosa 
  end 

end 

include Pippo 

dice("ciao") 

Salvate come moduli.rb ed eseguite, vedrete che scriverà: Pippo dice ciao. Avete invitato
il modulo Pippo nel vostro contesto con la parola chiave (keyword) include. Da lì in poi
potete usare il metodo dice semplicemente. Se non includete il modulo, Ruby si
lamenterà dicendo che non trova il metodo ciao

sh source.

➜  source  ruby moduli.rb 
moduli.rb:20:in `<main>': undefined method `dice' for main:Object (NoMethodError)
➜  source 

Includere un modulo significa copiare i metodi del modulo nel contesto dove si include,
questo può sembrare difficile ma non lo è (non molto almeno) ed ha delle conseguenze.

ruby source.

module Pippo 

  def dice(cosa) 
    puts "Pippo dice: " + cosa 
  end 
  end 

end 

module Pluto 

  def dice(cosa) 
    puts "Pluto dice: " + cosa 
  end 

end 

include Pippo 
include Pluto 

dice("ciao") 

Salvate come moduli2.rb ed eseguite:

sh source.

➜  source  ruby moduli2.rb 
Pluto dice: ciao 
➜  source 

Il problema è: quale metodo dice viene chiamato? Come vedete è quello di Pluto perché
il modulo Pluto è incluso dopo il modulo Pippo. Ci sono varie implicazioni in questo
comprese delle cose strane:

ruby source.

module Pippo 

  def dice(cosa) 
    puts "Pippo dice: " + cosa 
  end 

end 

module Pluto 

  def dice(cosa) 
    puts "Pluto dice: " + cosa 
  end 

end 

include Pippo 
include Pluto 

Pippo.dice("ciao") 
Pippo::dice("ciao") 

Salvate come moduli3.rb ed eseguite:

sh source.
➜  source  ruby moduli3.rb 
Pluto dice: ciao 
Pluto dice: ciao 
➜  source 

Come si può vedere anche usando gli operatori di visibilità di Ruby :: o . non si riesce ad
invocare il dice racchiuso dentro Pippo. Questo è spiegabile ma non adesso. Per ora
ricordate che: se includete un modulo i metodi di questo sono copiati dove li avete
inclusi e che i metodi con lo stesso nome e lista di prametri si sovrascrivono.
Adesso vediamo come rendere un modulo una libreria.
Scrivete e salvate questi due file, il primo chiamatelo pippo.rb ed il secondo moduli4.rb.

ruby source.

module Pippo 

  def dice(cosa) 
    puts "Pippo dice: " + cosa 
  end 

end 

ruby source.

require './pippo' 

include Pippo 

dice("ciao") 

Eseguite moduli4.rb:

sh source.

➜  source  ruby moduli4.rb 
Pippo dice: ciao 
➜  source 

Abbiamo trasformato il modulo Pippo in una libreria. Potete mettere quello che volete
dentro il file della libreria e chiamarlo come volete. Lo dovete prima richiedere con la
keyword require ed il percorso nel file system del file. La sintassi del percorso segue lo
standard Unix quindi non avete come su Microsoft Windowstm le barre rovesciate \ ma
invece avete le barre normali / .
In questo caso, require './pippo', significa: richiedi il file pippo.rb che si trova nella
cartella corrente. Una volta richiesto, il file potrà essere usato come se il suo codice
fosse scritto direttamente.
Il dividere un programma in più file, consente di riutilizzare il codice scritto in
precedenza da noi o da altri e di averne anche una organizzazione spaziale (e non parlo
di astronavi).

3.5.2 Le classi (non quelle della scuola, o forse si?)


3.5.2 Le classi (non quelle della scuola, o forse si?)

Cominciamo a vedere le classi, questi oggetti misteriosi. Non ha caso ho detto classi e
oggetti.
Ruby è un linguaggio di programmazione Object Oriented, l'ho già detto prima.
Ricordate?
Vuol dire: linguaggio di programmazione orientato agli oggetti. Che vuol dire: un
linguaggio di programmazione in cui gli oggetti sono la cosa più importante. Che vuol
dire: un linguaggio di programmazione dove si usano delle descrizioni degli oggetti,
chiamate classi, per modellare il dominio del problema. Continuo? - Sempre più difficile,
Signore e Signori! -
Facciamola semplice e partiamo da qualche esempio.
Intorno a noi ci sono tante cose, pensateci bene. Imparare a programmare i computer fa
bene anche perché insegna a ragionare e ad affrontare i problemi in maniera analitica.
Insomma, intorno a noi abbiamo tante cose: viviamo dentro delle case, guardiamo il
televisore, ascoltiamo la musica col giradischi (che voi forse non sapete nemmeno cosa
sia, ma io sono vecchio e lo so), camminiamo con le scarpe, ci mettiamo i pantaloni e ci
sediamo sulla sedia, leggiamo libri… Potrei continuare all'infinito.
Intorno a noi ci sono oggetti. Questi oggetti servono a qualcosa, fanno qualcosa o
subiscono qualcosa. Gli oggetti possono essere formati da altri oggetti, ricordate
quando prima ho detto dei mattoncini Lego(tm)? Il Meccano(tm), quando ero piccolo c'era
questo, oggetti in metallo, viti e bulloni, motori elettrici… Ci ho costruito tante cose. Chi
di voi è uno smontatore professionista? Io da piccolo smontavo praticamente tutto:
volevo vedere come funzionasse dentro.
Con i linguaggi di programmazione avete gli strumenti per scatenare la curiosità, se
volete. Ritorniamo ai nostri oggetti ed alla definizione di un linguaggio di
programmazione orientato agli oggetti (come è Ruby, che però non è solo questo):

è un linguaggio che permette di descrivere il dominio del problema definendo gli


oggetti che lo compongono e le loro interazioni

Il dominio del problema significa solo quello che volete fare, perché usare delle parole
difficili? Primo è perché vogliamo imparare le parole giuste, secondo perché, in realtà,
c'è di più di quello che volete fare. Per adesso però, va bene così.
Per fare quello che volete fare dovete analizzare tutti i piccoli pezzi che compongono o
servono per fare quello che volete fare. Tra un po' comincio con gli scioglilingua…
Ma cosa volete fare?.
Pensate alla macchina, che poi si dice automobile… Anche se è una macchina.
Elenchiamo i pezzi che la compongono: sedili, sportelli, volante, ruote, vetri, ingranaggi,
motore, viti, bulloni e chi più ne ha ne metta…
Ho puntualizzato che l'automobile è una macchina e vedrete che questa affermazione
banale, così semplice, ha una sua ragione di esistere.
Cominciamo a descrivere meglio una automobile e facciamolo in Ruby.
ruby source.

class Automobile 

end 

Abbiamo definito una classe e cioè una classe di oggetti. La classe (class) è la descrizione
dell'oggetto con le sue proprietà o attributi (gli oggetti che la compongono) ed i metodi
che sono il come interagisce col mondo esterno, cosa fa o cosa subisce.
L'automobile ha le ruote prima di tutto:

ruby source.

class Automobile 

  @ruote = 4 

end 

@ruote si chiama variabile di istanza. È una variabile come le abbiamo già viste, ma vive
dentro un oggetto che sarebbe una istanza di una classe.
Le variabili di istanza cominciano per @.
Ricapitoliamo:

la classe descrive l'oggetto nella sua forma e funzione


l'istanza rende viva la classe come oggetto

Una variabile di istanza non è ancora un attributo, anche se ancora non sappiamo cosa
significhi un attributo. Per farla semplice, diciamo che una classe ha delle parti nascoste
e delle parti visibili, l'automobile ha di visibile la carrozzeria ma non il motore. Per
vedere quello dovete aprire il cofano, guardare dentro. È la stessa cosa, una classe in
Ruby ha delle parti nascoste e delle parti visibili: @ruote è ancora nascosta. Direi anche
di lasciarla nascosta, mica vogliamo che mentre siamo in corsa a duecento chilometri
all'ora verso una curva ci cambino il numero delle ruote? Se di punto in bianco diventano
tre? O due? o Niente?
Lasciamola nascosta che è meglio (citazione dai Puffi). Però, il numero delle persone
potrebbe cambiare no?
Aggungiamo le persone, le auto portano le persone:

ruby source.

class Automobile 

  @ruote = 4 
  attr_accessor :persone 

end 

Ora noi abbiamo che persone è accessibile e si vede dall'esterno dell'oggetto.

ruby source.
irb(main):001:0> class Automobile 
irb(main):002:1> @ruote = 4 
irb(main):003:1> attr_accessor :persone 
irb(main):004:1> end 
=> nil 
irb(main):005:0> Auto = Automobile.new 
=> #<Automobile:0x007f8995f94a90> 
irb(main):006:0> Auto.persone = 4 
=> 4 
irb(main):007:0> Auto.persone 
=> 4 
irb(main):008:0> 

Ho rifatto il codice dentro irb per farvi vedere? Capito? L'attributo persone è visibile
dal'esterno dell'oggetto. Analizziamo il codice meglio.
Dopo aver definito la classe Automobile ho costruito l'oggetto Automobile con il
metodo new (nuovo). Si dice che ho istanziato la classe. L'oggetto appena creato l'ho
immagazzinato dentro una variabile che ho chiamato Auto.
Cominciamo a capire cosa è la classe? La classe è come il progetto di un oggetto, che poi
va costruito. Infatti nel gergo dei linguaggi di programmazione orientati agli oggetti,
metodi come new sono chiamati costruttori (anche se con Ruby è impreciso e sarebbe
meglio chiamarlo metodo istanziatore).

ruby source.

irb(main):005:0> Auto = Automobile.new 
=> #<Automobile:0x007f8995f94a90> 

Quello che vedete sotto ad Auto = Automobile.new è il nome dell'istanza della classe
Automobile che ho appena creato. Il nome vero, ma noi ci riferiremo a questa con Auto.
Adesso cerco di cambiare il numero delle persone e quello delle ruote:

ruby source.

irb(main):009:0> Auto.persone 
=> 4 
irb(main):010:0> Auto.persone = 5 
=> 5 
irb(main):011:0> Auto.ruote = 2 
NoMethodError: undefined method `ruote=' for #<Automobile:0x007f8995f94a90 @persone=
        from (irb):11 
        from /home/nissl/bin/ruby‐2.1/bin/irb:11:in `<main>' 
irb(main):012:0> 

Sono riuscito a farlo per le persone ma non per le ruote. L'attributo persone è visibile
all'esterno e si può cambiare: è accessibile in lettura e scrittura; ruote no.
Ruby però, in certe cose è davvero strano ed è nella sua natura esserlo. In Ruby tutto è
un oggetto.
Non vorrei complicarvi la vita troppo, e la faccio breve con un esempio:
ruby source.

irb(main):017:0> Auto.instance_variables 
=> [:@persone] 
irb(main):018:0> Automobile.instance_variables 
=> [:@ruote, :@persone]
irb(main):019:0> 

Ho invocato il metodo instance_variables (che mi elenca dentro un array le variabili di


istanza di un oggetto) sull'oggetto Auto e sulla classe Automobile e… Auto ha una
variabile di istanza mentre Automobile ne ha due. Automobile ha due variabili di istanza?
Finora vi ho detto che Automobile non era una istanza ma una classe! Vi ho perso in giro?
No, non l'ho fatto ma non sono stato preciso. In Ruby, tutto è un oggetto, anche le classi.
Questo porta ad interessanti implicazioni e possibilità del linguaggio che però vanno
oltre lo scopo di questo piccolo corso.
Sappiate questo: se vogliamo utilizzare una variabile di istanza dentro una istanza
dobbiamo scriverla in un altro modo.

ruby source.

class Automobile   

  attr_reader :ruote 
  attr_accessor :persone 

  def initialize 
    @ruote = 4 
  end 

end 

Ho aggiunto attr_reader ed un metodo importantissimo: initialize. Il metodo inizialize


viene invocato automaticamente da Ruby quando usiamo il metodo costruttore new. Il
nome stesso spiega cosa fa: inizializza l'istanza; cioè fa qualcosa mentre l'oggetto è
preparato per essere usato. Le variabili di istanza di un oggetto devono essere definite e
dichiarate qui dentro se le vogliamo avere disponibili alla creazione. La dichiarazione
attr_reader :ruote dice che la variabile di istanza @ruote sarà solo disponibile in lettura.
Questi i dichiaratori di accesso delle variabili di istanza:

attr_reader accesso in sola lettura


attr_writer accesso in sola scrittura
attr_accessor accesso in lettura e scrittura

Questi dichiaratori, sono dei metodi di convenienza se non usassi attr_accessor dovrei
scrivere così:

ruby source.

class Automobile   

  def initialize 

    @ruote = 4 
    @ruote = 4 
  end 

  def persone 
    @persone 
  end 

  def persone=(numero) 
    @persone = numero 
  end 

  def ruote 
    @ruote 
  end 

end 

Come vedete ho scritto di più di prima ed il codice è meno leggibile e più complicato.
Ruby ha molti metodi di convenienza (detti helpers) quindi usateli.
Cerchiamo di migliorare la nostra Automobile. In fondo, scusate cosa è una Automobile?
È un veicolo a motore… Invece un veicolo a motore non è un generico veicolo? Un'altra
cosa interessante dei linguaggi di programmazione orientati agli oggetti è che
supportano l'ereditarietà. Avete presente voi ed i vostri genitori? A chi assomigliate? Da
chi avete ereditato il naso o gli occhi?
Partendo da un generico Veicolo andiamo verso un Veicolo a motore e poi ad una
Automobile.

Veicolo -> VeicoloAMotore -> Automobile

Scrivete e salvate questo file come automobile.rb.

ruby source.

class Veicolo 
end 

class VeicoloAMotore < Veicolo 

  attr_reader :motore 

  def initialize 
    @motore = true 
  end 

end 

class VeicoloARuoteConMotore < VeicoloAMotore 

  attr_reader :ruote 

  def initialize(numero_ruote = 4) 
    super() 
    @ruote = numero_ruote 
  end 
end 

class Automobile < VeicoloARuoteConMotore 

  attr_accessor :persone 

  def initialize 
    super(4) 
    @persone = 0 
  end 

end 

Auto = Automobile.new 

Auto.persone = 5 

puts "Auto ha il motore: " + (Auto.motore ? "si" : "no") 
puts "Ospita quante persone? " + Auto.persone.to_s 
puts "Quante ruote? " + Auto.ruote.to_s 
puts 
puts "Variabili di istanza sono:" 
puts Auto.instance_variables 

Se lo fate girare:

sh source.

➜  source  ruby automobile.rb 
Auto ha il motore: si 
Ospita quante persone? 5 
Quante ruote? 4 

Variabili di istanza sono: 
@motore 
@ruote 
@persone 
➜  source 

Abbiamo creato la nostra prima gerarchia di classi.


Come vedete: Veicolo -> VeicoloAMotore -> VeicoloARuoteConMotore -> Automobile.
Ogni classe ha un initialize che noi dovremmo richiamare nella classe derivata per essere
sicuri che il genitore sia inizializzato a dovere. Questo si fa con super. Il metodo super si
assicura di richiamare il metodo inizialize del padre, infatti noi dobbiamo scriverlo e
passargli eventuali parametri. Il suo funzionamento è abbastanza magico nel senso che i
parametri dell'initialize del figlio vengo passati automaticamente a super. Per questo
motivo nella classe VeicoloARuoteConMotore è scritto super(), per evitare che a
inizialize del genitore venga passato un parametro che non è richiesto. Questo sarebbe
stato l'errore:

sh source.

➜  source  ruby automobile.rb 
automobile.rb:8:in `initialize': wrong number of arguments (1 for 0) (ArgumentError)
automobile.rb:8:in `initialize': wrong number of arguments (1 for 0) (ArgumentError)
        from automobile.rb:19:in `initialize' 
        from automobile.rb:30:in `initialize' 
        from automobile.rb:36:in `new' 
        from automobile.rb:36:in `<main>' 
➜  source 

Dimenticavo, come forse avete notato il metodo initialize della classe


VeicoloARuoteConMotore è così: def initialize(numero_ruote = 4). In Ruby i parametri
possono avere dei valori già impostati (di default) e ciò permette di ometterli quando si
invoca un metodo. Non abusate di questa facilitazione può essere molto pericolosa.
So di essere stato contorto ma non è semplice da spiegare. Dovete riflettere bene su
questo e cercare di capire. Ruby ha, come già detto altre volte, delle cose che sembrano
a prima vista strane, ma vedrete che invece sono la sua forza.
Cambiamo le classi però, che così non vanno tanto bene:

ruby source.

class Veicolo 
end 

class VeicoloAMotore < Veicolo 

  @@motore = true 

  def motore 
    @@motore 
  end 

end 

class VeicoloARuoteConMotore < VeicoloAMotore 

  def initialize(numero_ruote = 4) 
    @@ruote = numero_ruote 
  end 

  def ruote 
    @@ruote 
  end 

end 

class Automobile < VeicoloARuoteConMotore 

  attr_accessor :persone 

  def initialize 
    super(4) 
    @persone = 0 
  end 

end 

Auto = Automobile.new 
Auto = Automobile.new 

Auto.persone = 5 

puts "Auto ha il motore: " + (Auto.motore ? "si" : "no") 
puts "Ospita quante persone? " + Auto.persone.to_s 
puts "Quante ruote? " + Auto.ruote.to_s 
puts 
puts "Variabili di istanza sono:" 
puts Auto.instance_variables 

Ho scritto delle variabili con due @ (@@). Queste sono variabili di classe e si propagano
per tutta la gerarchia degli oggetti. La differenza tra le class variables e le instance
variables è che le seconde nelle istanze figlie sono delle copie, la prime le stesse.
Esempio: due bambini comprano due cacciaviti sonici del Doctor Who, oppure ne
comprano uno solo e se lo passano. Nel primo caso, se uno si rompe, è solo quel
bambino a piangere. Nel secondo piangono tutti e due.
Chiaro?
Classi ne abbiamo molte in Ruby, già di suo. Array è una classe, Fixnum un'altra, String.
Abbiamo poi una classe Time per gestire il tempo o Hash che è importantissima. Ce ne
sono davvero tante:

La documentazione base: http://www.ruby-doc.org/core-2.1.3/


La documentazione della libreria: http://www.ruby-doc.org/stdlib-2.1.3/

Ruby ha una libreria molto estesa per fare davvero molte cose. Oltre alla sua, ha un
sistema di gestione delle librerie che si chiama: RubyGems, https://rubygems.org/; dove
sono reperibili migliaia di ulteriori librerie. Oltre a questo ne scriverete anche di vostre,
no?

3.5.3 Blocks e Procs, che non è il Rock'n'Roll ma ci si avvicina.

Ruby ha delle fantastiche caratteristiche, questa è una di quelle. Li abbiamo già visti, nei
metodi each o map che abbiamo usato nei cicli. Possiamo scrivere metodi come quelli.
Blocks, Procs, Closure e lambda, sono sostanzialmente simili e scritti in maniera simile.
Le differenze a volte sono solo di fino come si dice.
Vediamo come si scrive una Proc:

ruby source.

ciao = Proc.new do 
  puts "ciao" 
end  

ciao.call 
ciao.call 
ciao.call 

Questo scriverà tre volte ciao.


sh source.

ciao 
ciao 
ciao 

Una Proc può prendere parametri:

ruby source.

ciao = Proc.new do |a_chi| 
  puts "ciao" 
end  

ciao.call('Mondo') 
ciao.call('Carlo') 
ciao.call('Camilla') 

Il suo output sarà:

sh source.

ciao Mondo 
ciao Carlo 
ciao Camilla 

La cosa fantastica delle Proc è che possiamo passarle come valori dei parametri e
restituirle come valori dai metodi. Questo è un concetto complesso che a prima vista
sembra non portare benefici ma non è così. È una caratteristi importante che fa di Ruby
anche un linguaggio funzionale oltre che orientato agli oggetti, pur con alcune
limitazioni.
Questo frammento definisce un metodo che prende come parametro una Proc:

ruby source.

ciao = Proc.new do 
  "ciao!" 
end 

buongiorno = Proc.new do 
  "buongiorno!" 
end 

def saluta_con_un(proc)
  puts "Ti saluto con un #{proc.call}" 
end 

saluta_con_un(ciao) 
saluta_con_un(buongiorno) 

In questo esempio ho usato un nuovo modo per concatenare le stringhe detto


interpolazione. Funziona con le stringhe tra doppi apici (quelle in apici singoli non vanno
bene) e praticamente il codice compreso tra #{…} viene valutato ed il risultato sostituito
in quel punto. Ci permette di costruire stringhe con elementi da valutare lì per lì.
Il suo output sarà:

sh source.

Ti saluto con un ciao! 
Ti saluto con un buongiorno! 

La Proc è stata passata come parametro ed invocata, questo perché la Proc è un valore,
è un oggetto a tutti gli effetti. Un oggetto che contiene del codice.

ruby source.

ciao = lambda do 
  "ciao!" 
end 

buongiorno = Proc.new do 
  "buongiorno!" 
end 

def saluta_con_un(proc)
  puts "Ti saluto con un #{proc.call}" 
end 

saluta_con_un(ciao) 
saluta_con_un(buongiorno) 

Qui ho usato una lambda, Proc e lambda sono molto simili, la sintassi è praticamente
identica. Ci sono differenze molto sottili di funzionamento che però vanno oltre lo scopo
di questo minicorso.
Per finire un esempio di un metodo che prende come parametro un Block:

ruby source.

def saluta(persone, &saluto) 
  persone.each { |nome| 
    salut = yield saluto 
    puts "#{ nome } #{ salut }" 
  } 
end 

saluta(['Marco', 'Giovanni', 'Benedetta']) { 
  "ciao" 

Scriverà:

sh source.

Marco ciao 
Giovanni ciao 
Benedetta ciao 

Queste possibilità di Ruby sono solo state scalfite. Sono molto potenti.
3.5.4 Il mondo esterno

Fino ad adesso non abbiamo fatto altro che stare comodamente in casa, ma ogni tanto
bisognerà uscire e parlare con la gente di fuori e magari non dimenticarsi quello che si è
fatto. Uno dei vari modi per uscire all'aperto è leggere e scrivere file.
Lo avete fatto anche voi con il vostro editor, avete creato dei file sul disco del vostro
computer e li avete letti, eseguiti con Ruby.
Ruby, come molti linguaggi ha la possibilità di aprire file, scrivere o leggere nei o dai file,
chiudere i file. Non è un caso che abbia scritto: aprire, leggere o scrivere, chiudere; sono
queste le fasi che servono per gestire i file.
Per fare questo abbiamo una classe che, ovviamente, si chiama File. File non fa solo
quello, ha anche altri metodi che servono per esempio per avere informazioni sui file ma
anche per gestirne i percorsi.
Un percorso serve per ritrovare un file nel disco del computer. Ci sono diverse
nomenclature in base al sistema operativo che si sta usando e questo potrebbe rendere
un po' complicato il gestirle.
Un percorso è formato da una serie di nomi di cartelle divisi da un separatore (qui è la
differenza dei vari sistemi operativi) ed un nome di file.

sh source.

/home/nissl/Documenti/Ruby/il_mio_file.txt 

Questo percorso è di tipo Unix.

sh source.

c:\Documenti\Ruby\il_mio_file.txt

Questo si usa con Microsoft Windowstm.


Senza entrare nelle differenze complicate, ne vediamo subito una e cioè la differenza di
separatore (slash e backslash). Fortunatamente Ruby, che è nato sui sistemi di tipo Unix,
considera la forma con la barra normale (slash) / la forma corretta e quindi ragiona in
questo modo facendolo fa anche su Windowstm.
Quindi:

sh source.

c:/Documenti/Ruby/il_mio_file.txt

Per Ruby questo è corretto e lui troverà il file. Per il sistema operativo no però e questo
potrebbe avere delle conseguenze in certi casi.
Vediamo come si apre un file:

ruby source.

file = File.open('miofile.txt', 'r') 
In questo modo, ho aperto un file in sola lettura, vuol dire che potrò leggerne il
contenuto ma non lo potrò scrivere.
Ci sono vari parametri di permesso per i file e come si può vedere si indicano nel secondo
valore dato al metodo mentre il primo è il nome del file.

r il file è aperto in sola lettura dal suo inizio.


r+ il file è aperto in lettura e scrittura dal suo inizio.
w il file è aperto in sola scrittura e tronca il file a zero, cioè elimina il contenuto del
file o ne crea uno se questo non esiste.
w+ il file è aperto in lettura e scrittura e tronca il file a zero, cioè elimina il contenuto
del file o ne crea uno se questo non esiste.
a il file è aperto in sola scrittura e aggiunge alla fine del file, appende.
a+ Il file è aperto in lettura e scrittura e aggiunge alla fine del file, appende.

Queste sono le possibilità e l'uso dipende da quello che vogliamo fare con il file. Per
esempio, se vogliamo solo leggere un file dobbiamo aprirlo in sola lettura per non
rischiare di scriverci qualcosa inavvertitamente.
Esistono altre impostazioni da passare al metodo open o new oltre a queste, ma si
rimanda alla documentazione ufficiale.
Ricordate sempre: i file sono preziosi e vanno gestiti in maniera adeguata.
Come ho accennato i file devono essere: aperti, letti o scritti, chiusi.

ruby source.

#apro il file 
file = File.open('miofile.txt', 'r') 

#leggo tutto il contenuto e lo metto nella variabile contenuto. 
# contenuto avrà dentro tutto il testo come stringa (se il file è di testo)
contenuto = file.read 

#ho finito di leggerlo e lo chiudo: mai lasciare aperti i file. 
file.close 

Qui sopra ci sono le fasi. Una cosa molto importante è: chiudere i file il prima possibile
dopo averci lavorato.
Chiudere i file è importante per vari motivi, due di questi:

è pericoloso tenere aperto un file, si potrebbe distruggere o alterare


i file sono una risorsa onerosa per il sistema operativo.

Il sistema operativo, può tenere aperti solo un certo numero di file alla volta e per tutto
il sistema. Questo è un limite che può essere impostato, ma sappiate che comunque ha
un impatto sulla velocità con cui viene poi gestito il computer: l'hardware del computer.
Ogni volta che aprite un file, si occuperà una parte di memoria da un oggetto del sistema
operativo chiamato descrittore. Questo succede perché il vostro programma non
gestisce direttamente il file, ma parla col sistema operativo. Quando aprite un file
chiedete al sistema operativo di aprirvelo e quando ci scrivete o leggete chiedete al
sistema di leggerlo o scriverlo, poi dite al sistema di chiuderlo.
Lui vi risponderà: - Era ora! -.

ruby source.

#apro il file 
file = File.open('miofile.txt', 'r') 

#leggo tutto il contenuto e lo metto nella variabile contenuto. 
# contenuto sarà un Array di stringhe, un elemento per riga (se il file è di testo)
contenuto = file.readlines 

#ho finito di leggerlo e lo chiudo: mai lasciare aperti i file. 
file.close 

Adesso ho letto il contenuto con il metodo readlines che mi restituisce un Array di


String. Come vedete ci sono vari modi di leggere il contenuto di un file.
Questi sono modi adatti per file di piccole dimensioni, altrimenti si dovranno adottare
diverse strategie sempre per non caricare troppo il sistema operativo.
I file, per esempio, si potrebbero leggere a pezzi più o meno grandi…
Il nostro file lo abbiamo letto, ma per scriverlo?

ruby source.

#apro il file 
file = File.open('miofile.txt', 'w') 

#scrivo dentro il file (se il file è di testo) 
file.write('Ciao Mondo') 

#ho finito di scrivere e lo chiudo: mai lasciare aperti i file. 
file.close 

Ho scritto la stringa Ciao Mondo nel file. Adesso sul disco avremo un file dal nome
miofile.txt con dentro la frase: Ciao Mondo.
Visto che aprire e chiudere i file è così importante, Ruby ci mette a disposizione una
versione del metodo open molto interessante:

ruby source.

contenuto = '' 
#apro il file 
File.open('miofile.txt', 'r') do |file| 

#leggo tutto il contenuto e lo metto nella variabile contenuto. 
# contenuto avrà dentro tutto il testo come stringa (se il file è di testo)
contenuto = file.read 

end 
Come si può vedere, al metodo è associata una closure. Questa versione
sostanzialmente si prende cura di chiudere il file all' uscita del blocco della closure.
Insomma se scriviamo così non ci dobbiamo preoccupare di chiudere il file perché sarà
automatico. Non è un vantaggio?
La classe File, come vi ho già detto, ha molti metodi per la gestione dei file. Alcuni
interessanti sono questi:

File.exist?('miofile.txt') controlla se il file esiste.


File.file?('miofile.txt') controlla se un file è un file.
File.directory?('miadirectory') controlla se un file è una cartella (directory).
File.ftype('miofile.txt') controlla che tipo di file è, per esempio se è un file o una
directory.
File.size('miofile.txt') controlla quando è grande, quanto spazio occupa sul disco.
File.join('cartella1', 'cartella2', 'miofile.txt') costruisce un percorso di file
concatenando due o più stringhe.
File.basename('/cartella1/cartella2/miofile.txt') ritornerà solo il nome del file
miofile.txt.
File.dirname('/cartella1/cartella2/miofile.txt') ritornerà solo il percorso delle
directory senza il nome del file.
File.extname('miofile.txt') ritornerà l'estensione del nome del file, cioè il testo dopo
il punto ma col punto compreso (.txt).

Ce ne sono altri di metodi e ci sono, inoltre, anche altre classi per la gestione dei file. I
file non si creano, leggono o scrivono soltanto; ma si rinominano, spostano, cancellano e
così via.
Quello che abbiamo visto qui serve per i file di testo, cioè quelli che contengono
caratteri e che quando si leggono vi si accede come fossero delle stringhe. I file oltre che
di testo possono essere anche binari e quindi codificati direttamente in byte. Vi si accede
praticamente nello stesso modo, ma bisogna usare con i metodi che vi ho detto altri
parametri. Non è particolarmente semplice, quindi per adesso va bene così.

3.5.5 I dizionari

Conoscerete il dizionario, quel librone grosso con tante parole in fila… I significati delle
parole. Quello che va da abaco a zuzzurellone (per la verità, se controllate comincia con
la a, però è così che corre voce).
Sappiate che Ruby ha i dizionari, la classe si chiama Hash (il motivo c'è ma è complicato).
In altri linguaggi di programmazione si chiamano Map (mappa) ma il concetto è lo stesso.
Un dizionario o mappa o Hash è un tipo di dato estremamente utile.

ruby source.

dizionario = { 'nome' => 'massimo', 'cognome' => 'ghisalberti'} 

Questo è come si dichiara velocemente. La variabile dizionario contiene un hash con due
chiavi: nome e cognome. Se volessi accedere al valore indicato dalla chiave nome:

ruby source.

il_mio_nome = dizionario['nome'] 

La variabile il_mio_nome a questo punto conterrà massimo.

ruby source.

irb(main):001:0> dizionario = { 'nome' => 'massimo', 'cognome' => 'ghisalberti'
=> {"nome"=>"massimo", "cognome"=>"ghisalberti"} 
irb(main):002:0> dizionario['nome'] 
=> "massimo" 
irb(main):003:0> 

Vi assicuro che degli Hash non potrete farne a meno, sono utilissimi. In qualche maniera
sono simili agli Array, nel senso che hanno metodi simili. Anche gli Hash sono collezioni:

ruby source.

dizionario = { 'nome' => 'massimo', 'cognome' => 'ghisalberti'} 
dizionario.each do |chiave,valore| 
  puts "chiave: " + chiave 
  puts "valore: " + valore 
end 

Salvate come dizionario.rb ed eseguite:

sh source.

➜  ruby dizionario.rb  
chiave: nome 
valore: massimo 
chiave: cognome 
valore: ghisalberti 

come vedete, potete accedere in questo modo alle coppie chiave -> valore. I dizionari di
Ruby sono estremamente flessibili, la chiave può essere di qualunque tipo supportato da
Ruby e così i valori. Potete mischiare le cose…

ruby source.

dizionario = { 1 => 'massimo', 'cognome' => 'ghisalberti'} 
dizionario.each do |chiave,valore| 
  puts chiave 
  puts valore 
end 

Eseguendolo, produrrà questo:

sh source.
➜  ruby dizionario.rb  
chiave: 1 
valore: massimo 
chiave: cognome 
valore: ghisalberti 

Adesso una chiave è un Fixnum, un numero, mentre l'altra una stringa.


Adesso mi direte: - Perché i parli adesso degli Hash se sono così utili? -
La risposta è che, perché vi ho parlato di come si salva un file. Questa è la risposta corta.
Quella lunga invece è che un hash (ma anche un Array) può essere facilmente
serializzato. Che vuol dire?

ruby source.

require 'yaml' 

rubrica = [ 

  {:nome => 'Massimo', :cognome => 'Ghisalberti', :telefono => '1234567890'
  {:nome => 'Mario', :cognome => 'Rossi', :telefono => '1234567890'} 

puts "La rubrica come array" 
p rubrica 
puts 

puts "la rubrica serializzata come YAML" 
p rubrica.to_yaml 
puts 

puts "Salvo la rubrica su un file" 
puts 
File.open('rubrica.yml', 'w') do |file| 
  file.write(rubrica.to_yaml) 
end 

puts "Leggo la rubrica su un file" 
puts 
dati = [] 
File.open('rubrica.yml', 'r') do |file| 
  yaml = YAML.load(file.read) 
  dati = yaml.to_a 
end 

puts "La rubrica ricaricata dal file" 
p dati 

Salvate come dizionario2.rb ed eseguite.

sh source.
➜  ruby dizionario2.rb 
La rubrica come array 
[{:nome=>"Massimo", :cognome=>"Ghisalberti", :telefono=>"1234567890"}, {:

la rubrica serializzata come YAML
"‐‐‐\n‐ :nome: Massimo\n  :cognome: Ghisalberti\n  :telefono: '1234567890'\n‐ :nome:

Salvo la rubrica su un file 

Leggo la rubrica su un file 

La rubrica ricaricata dal file 
[{:nome=>"Massimo", :cognome=>"Ghisalberti", :telefono=>"1234567890"}, {:

È un piccolo programma che salva un array di hash e cioè una collezione di dizionari su un
file e la ricarica. Ho utilizzato un formato di dati molto comune nel mondo Ruby, lo YAML
(http://yaml.org/). È un formato di struttura dati abbastanza semplice e versatile e
soprattutto testuale. Se aprite il file rubrica.yml vedrete che è così:

yaml source.

‐‐‐ 
‐ :nome: Massimo 
  :cognome: Ghisalberti 
  :telefono: '1234567890' 
‐ :nome: Mario 
  :cognome: Rossi 
  :telefono: '1234567890' 

Se volete potete aggiungere direttamente qui:

yaml source.

‐‐‐ 
‐ :nome: Massimo 
  :cognome: Ghisalberti 
  :telefono: '1234567890' 
‐ :nome: Mario 
  :cognome: Rossi 
  :telefono: '1234567890' 
‐ :nome: Pico 
  :cognome: De paperis 
  :telefono: '0234567890' 

Quando lo rileggerete nel modo indicato avrete un dizionario in più nel vostro array.
Utilizzando il metodo to_yaml ho convertito, in questo caso l'array ma anche tutti i tipi di
dati in esso contenuti, in una struttura dati Yaml che ho salvato sul disco. Dopo ho
ricaricato i dati dal file aperto come struttura dati Yaml attraverso YAML.load e
convertito di nuovo in dati Ruby con to_a (to array). Sembra contorto ed un po' lo è, ma
questa /"cosa"/ vi permette di avere facilmente il modo di poter salvare velocemente
dati sul disco e di recuperarli in seguito.
Pensate anche che lo Yaml non lo gestisce solo Ruby, ma si può aprire e leggere da molti
altri linguaggi di programmazione.
Per amor di cronaca esiste anche un altro sistema di strutture dati testuali che oggi va
per la maggiore ed è il JSON (http://json.org/). Ruby ha un serializzatore e
deserializzatore anche per il JSON.

ruby source.

require 'json' 

rubrica = [ 

  {:nome => 'Massimo', :cognome => 'Ghisalberti', :telefono => '1234567890'
  {:nome => 'Mario', :cognome => 'Rossi', :telefono => '1234567890'} 

puts "La rubrica come array" 
p rubrica 
puts 

puts "la rubrica serializzata come YAML" 
p rubrica.to_json 
puts 

puts "Salvo la rubrica su un file" 
puts 
File.open('database.json', 'w') do |file| 
  file.write(rubrica.to_json) 
end 

puts "Leggo la rubrica su un file" 
puts 
dati = [] 
File.open('database.json', 'r') do |file| 
  json = JSON.load(file.read) 
  dati = json.to_a 
end 

puts "La rubrica ricaricata dal file" 
p dati 

Come vedete i metodi sono molto simili.


Sia per il JSON che lo Yaml dovete, prima di usarli, richiederli con la require.
Come ultima cosa sugli Hash voglio farvi notare una cosa: ho usato per le chiavi il tipo
Symbol. Il simbolo è un tipo particolare simile ad una stringa, ma non è mutabile, non si
possono cioè manipolare i caratteri che lo compongono. Ha una sua importanza in molti
casi, ma soprattutto come chiavi dentro i dizionari dove si suppone che la chiave non
cambi mai, ma cambi solo il valore. Ruby fa della magia sua interna con i simboli ma
spiegarla va oltre lo scopo di questo piccolo corso.

3.5.6 Le finestre che si aprono o si chiudono se fa freddo.


3.5.6 Le finestre che si aprono o si chiudono se fa freddo.

Apriamo le finestre, oggi si arieggia.

3.5.6.1 Divagazioni sul tema

Una delle parti più complicate e noiose del programmare sono le interfacce grafiche.
Tutti voi avete dei sistemi operativi grafici, più o meno basati sul paradigma della
finestra o sul padadigma della vista.
Sappiate che contrariamente a quello che sapete o avete sentito più spesso dire, Apple o
Microsoft in questo non si sono inventati niente. Insomma Jobs e Gates hanno solo
rubacchiato qua e là.
Orrore! Qualcuno si sentirà male, qualcuno mi vorrà picchiare per aver insultato i suoi
idoli (specialmente quelli a cui piacciono le mele).
Ragazzi, tenetevi forte…
Tutto quello che conoscete è stato inventato dalla Xerox in certi laboratori, il PARC, che
aveva negli anni '70. A Palo Alto in California, i ricercatori geniali della Xerox hanno
inventato l'informatica moderna.

Il mouse (questo lo conoscete tutti)


la stampa laser (anche questa dovrebbe essere chiara)
l'ethernet (ogni computer oggi ha una scheda ethernet per collegarsi a Internet)
la grafica bitmap (le fotografie le fate?)
le interfacce grafiche (Le finestre di Microsoft Windowstm o di Apple MacOSXtm?)
gli editor WYSIWYG, What You See Is What You Get (i word processor, avete
presente Microsoft Wordtm?)
Interpress (poi evoluto da Adobe nel postscript e finito nel PDF)
la programmazione orientata agli oggetti (con Smalltalk)
l'architettura MVC, Model View Controller (che oggi è usatissima)
gli LCD (gli schermi a cristalli liquidi)
i dischi ottici di memorizzazione (Philips poi fece il CD su queste ricerche nell'80)
ubiquitous computing (il calcolo distribuito)
la programmazione ad aspetti (Aspect Oriented Programming)
IPv6 (il nuovo protocollo per la comunicazione tra computer che ancora non
abbiamo, Internet per adesso è ancora basato sull IPv5)

Tutto questo è stato studiato e, o, inventato tra la fine degli anni '60 ed i primi anni '80.
Alan Key, uno di questi pionieri ed inventore Smalltalk (implementato da Dan Ingalls),
propose nel 1972 il Dynabook. Dynabook sarebbe dovuto essere un computer portatile a
batteria (simile ad un tablet odierno, quindi Apple non ha inventato il tablet), con una
batteria virtualmente eterna, collegato in rete wireless, con un sistema operativo
grafico.
Durante questa ricerca, Key, inventò Smalltalk ed i sistemi operativi grafici. Stimò allora
(1972) per la costruzione del Dynabook un costo di 6000 dollari.
Alan Key è fortemente impegnato nell'insegnamento della programmazione ai bambini
e nella divulgazione dell'informatica a tutti i livelli, per esempio nel progetto: One
Laptop Per Child (http://en.wikipedia.org/wiki/One_Laptop_per_Child) che si prefiggeva
la costruzione e distribuzione a basso costo di computer adatti all'insegnamento e per
zone disagiate (XO-1 è uscito intorno al 2008).

3.5.6.2 Facciamole queste finestre, che ci siamo annoiati!

Dopo questa divagazione sul tema lunga e noiosa veniamo al dunque.


Programmare l'interfaccia grafica di un programma è un lavoro lungo e spesso noioso,
non è difficile ma ripetitivo e dispersivo. Se prima lanciare a linea di comando vi era
sembrato brutto e noioso dopo questo lo amerete!

3.5.6.2.1 il problema

Esistono molte librerie per la definizione delle interfacce grafiche, il sistema operativo
grafico ne ha una sua che è spesso abbastanza complicata. Microsoft Windowstm ha la
sua, il MacOSX la sua, i vari desktop Linux (KDE, Gnome per citarne solo due) la loro,
Android la sua, IOS la sua. È un vero caos (ricordate che casino è una parolaccia e non si
può dire).
Pensate poi al problema di un programma fatto per funzionare su tutti questi sistemi.
Non è più un caos, è un vero incubo… Altro che i mostri sotto il letto o dentro l'armadio.
Per cercare di facilitarsi la vita, molti programmatori hanno scritto delle librerie che però
hanno le loro regole. Insomma caos aggiunto al caos.
La scelta di una buona libreria è importante per la durata e la mantenibilità (un software
va mantenuto, curato e ci vanno aggiunte funzionalità o tolte) di un programma per
computer.
Ci sono linguaggi di programmazione migliori di altri in questo, alcuni sono stati
appositamente progettati per la definizione delle interfacce e quindi alcune cose sono
facilitate.
Sfortunatamente Ruby non è uno di questi, ma ha una cosa molto importante: è Ruby.
Poi, forse, questa affermazione sarà più chiara.
Per descrivere una interfaccia grafica abbiamo bisogno di usare una libreria apposta.

3.5.6.2.2 Le scarpette rosse

In questo mini corso vedremo come usare Shoes (http://shoesrb.com/).


Dovete scaricare una distribuzione di Shoes per il vostro sistema
http://shoesrb.com/downloads/). Una volta installata avrete un eseguibile che si chiama
shoes con cui potete lanciare i vostri programmi. Se volte potete lanciare anche i vecchi
programmi che avete fatto fino ad adesso, Shoes è una libreria per Ruby che fornisce un
comando per convenienza. Ruby c'è ancora e funziona.
sh source.

shoes tabellina.rb 

L'unica cosa è che il programma non termina da solo ma dovete fermarlo con la
combinazione di tasti: CRTL+c (CTRL è il tasto Control, cercatelo sulla tastiera e premete
prima quello e tenendolo premuto battete il tasto c, poi lasciateli tutti e due).
Vi ho fatto scaricare una distribuzione di Shoes completa perché non è facile da
installare dentro Ruby specialmente se usate Microsoft Windowstm.
Cominciamo con una cosa semplicissima:

ruby source.

Shoes.app {  
  button "Ciao, premi..."  

Salvatelo come scarpe.rb ed eseguitelo con: shoes scarpe.rb.


Alleluja! Il vostro primo programma grafico! Con Anche un bottone!
La finestra la potete spostare, ingrandire, rimpicciolire… Insomma ha un po' tutto. Tutto
quello che è dentro Shoes.app è la nostra applicazione Shoes.
Aggiungiamo un altro bottone?

ruby source.

Shoes.app {  
  button "Ciao, premi..." 
  button "Io sono il secondo bottone, premi..."  

Salvatelo come scarpe2.rb ed eseguitelo con: shoes scarpe2.rb.


Vogliamo disegnare un cerchio? Via!

ruby source.

Shoes.app {  
  button "Ciao, premi..." 
  button "Io sono il secondo bottone, premi..."  

  oval(left: 20, top: 40, radius: 50) 

Salvatelo come scarpe3.rb ed eseguitelo con: shoes scarpe3.rb.


Lo vogliamo rosso?

ruby source.

Shoes.app {  
  button "Ciao, premi..." 
  button "Io sono il secondo bottone, premi..."  

  fill(red) 
  fill(red) 
  oval(left: 20, top: 40, radius: 50) 

  fill(green) 
  rect(left: 20, top: 100, width: 50) 

Salvatelo come scarpe4.rb ed eseguitelo con: shoes scarpe4.rb.


Ho fatto anche un quadrato verde. Shoes ha numerosi oggetti grafici già disegnati:

oval per disegnare ovali, quindi anche cerchi


rect per i rettangoli e quadrati
star per fare delle stelle
image per caricare immagini, anche via internet
arrow per fare delle frecce
video per guardare dei video

Oltre questi metodi per avere cose già fatte, possiamo sempre disegnarle noi con stroke
(tratto) e fill (riempi). Insomma, con penna e colore potete disegnare.

ruby source.

Shoes.app do 
  @stella = star(points: 5) 
  motion do |sinistra, alto| 
    @stella.move sinistra, alto 
  end 
end 

Salvatelo come scarpe5.rb ed eseguitelo con: shoes scarpe5.rb. Muovete il mouse. la


stella vi viene dietro.

ruby source.

Shoes.app do 
  fill(yellow) 
  @cerchio = oval(left: 40, top: 40, radius: 40) 
  animate do |i| 
    @cerchio.top += (‐20..20).rand 
    @cerchio.left += (‐20..20).rand 
  end 
end 

Salvatelo come scarpe6.rb ed eseguitelo con: shoes scarpe6.rb. Abbiamo animato con il
metodo animate ed avremo un cerchio giallo che ballonzola in giro in maniera casuale.
La forma: (-20..20) è un Range, serve per esprimere un intervallo di numeri. Il metodo
rand ne prende uno a caso dentro questo Range.
L'espressione:
ruby source.

variabile = 1 

#questa forma è equivalente a quella dopo 
variabile += 1 

#questa forma è equivalente a quella prima 
variabile = variabile + 1 

utilizza un operatore che chiameremo di incremento +=. Ruby ha una serie di operatori
misti come questo, per esempio utile è:

ruby source.

variabile ||= 1 

che assegnerà il valore 1 alla variabile solo se non contiene già un valore. Utile per
inizializzare le variabili.
Ritorniamo al nostro bottone di prima:

ruby source.

Shoes.app {  
  button("premi...") { 
    alert("Ciao Mondo!") 
  } 

Salvatelo come scarpe7.rb ed eseguitelo con: shoes scarpe7.rb.


Premete il bottone… Avete aperto una finestra di dialogo, una di quelle che se non le
chiudete non potete fare più niente (in gergo difficile si dice finestra modale).

ruby source.

Shoes.app {  
  button("premi...") { 
    alert("Ciao Mondo1!") 
  } 
  button("premi...") { 
    alert("Ciao Mondo2!") 
  } 

Salvatelo come scarpe8.rb ed eseguitelo con: shoes scarpe8.rb.


Siore e Siori abbiamo due bottoni che funzionano!
Vediamo un campo di input, salvate come scarpe8.rb e lanciate:

ruby source.

Shoes.app do 

  background "#00ffff" 

  border("#ff0000", strokewidth: 6) 
  border("#ff0000", strokewidth: 6) 

  stack(margin: 12) do 
    title("Giochino") 
    para("Inserisci il tuo nome:") 
    flow do 
      @input_field = edit_line 
      button("OK") do 
         alert("Ciao " + @input_field.text) 
      end 
    end 
  end 
end 

Se lo eseguite, scrivete il vostro nome e premete il bottone; una finestra modale vi dirà: -
Ciao … -.
Qui possiamo notare diversi elementi. il metodo stack che impila gli elementi che
contiene, a cui possiamo fornire un margine e cioè di quanto è rientrato rispetto
all'elemento che lo contiene (in questo caso la finestra principale). Il metodo flow invece
permette di allineare, far fluire, i due elementi edit_line e button mantenendoli uno
accanto all'altro. Il contenuto del campo di input che ho chiamato edit_field è
recuperato con il metodo text del campo di input. Il metodo para sta per paragrafo
(paragraph) e serve per scrivere una linea di testo, mentre title mette del testo in un
carattere più grande.
Per colorare la finestra abbiamo usato background e border, il primo riempe di colore lo
sfondo e il secondo costruisce un bordo intorno (la proprietà strokewidth (larghezza del
tratto) è lo spessore del bordo.
Ci sono altri metodi per i testi che possono essere usati insieme, salvate come
scarpe10.rb:

ruby source.

Shoes.app { 
  stack(margin: 6) { 
    title("Formattazione del testo") 
    para(strong("Domanda"), " Quanto sei alto?") 
    para(em(strong("Risposta"), " Sono alto un metro e mezzo.")) 
  } 

strong metterà il testo in grassetto mentre em lo metterà in corsivo.

ruby source.

Shoes.app(title: "Il bottone") { 

  stack { 
    @bottone = button( "Premi")  
    @note = para(em("Non hai ancora premuto")) 
  } 

  num = 0 
  @bottone.click { 
    num += 1 
    @note.text = "Hai premuto " + num.to_s + " volte" 
  } 

Se salvate come scarpe11.rb ed eseguite, vedrete che vi comunicherà il numero delle


volte che avete premuto il bottone.
Adesso riprendiamo del codice che avevamo già scritto, le nostre tabelline:

ruby source.

Shoes.app(title: "la tabellina", width: 340, height: 280) { 

def tabellina 
  numeri = [1,2,3,4,5,6,7,8,9,10]
  (numeri.map { |numero| 
          (numeri.map { |moltiplicatore| 
             valore = numero * moltiplicatore 
            (valore < 10 ? '0' : '') + valore.to_s + ' | ' 
          }).join 
        }).join("\n") 
 end 

  stack(margin: 10) { 
    @tabellina = para(strong(tabellina)) 
    @tabellina.style(fill: red, stroke: white) 
  } 

Scrivete, salvate come scarpe12.rb ed eseguite. Qui ho usato anche il metodo style per
colorare il paragrafo: di rosso lo sfondo e bianco il testo.
Adesso rifacciamola ancora, un po' meglio (forse).

ruby source.

Shoes.app(title: "la tabellina", width: 412, height: 412, resizable: true

  def tabellina 
    numeri = [1,2,3,4,5,6,7,8,9,10] 
    numeri.map { |numero| 
      numeri.map { |moltiplicatore| 
        numero * moltiplicatore 
      } 
    } 
  end 

  width = 40 
  height = 40 
  top = 0 

  tabellina.each_index { |numero_riga| 
    stack(margin_left: 2) {  
    stack(margin_left: 2) {  
      flow { 
        left = 0         
        tabellina[numero_riga].each_index { |numero_colonna| 
          valore =  tabellina[numero_riga][numero_colonna] 
          pad = if valore < 10 
                  '  ' 
                elsif valore >= 10 && valore < 100 
                  ' ' 
                else 
                  '' 
                end           
          button = button(valore.to_s + ' ') 
          button.style(top: top, left: left, width: width, height: height)
          button.instance_variable_set(:@coord, [numero_riga + 1, numero_colonna + 1
          button.click { |btn| 
            coord = btn.instance_variable_get(:@coord)       
            alert("Si ottiene moltiplicando #{ coord.join(' x ')}, oppure 
          } 

          left += width + 1 
        } 
      } 
      top += height + 1 
    } 
  } 

Salvate come scarpe13.rb ed eseguite.


Qui ho usato anche delle caratteristiche particolari di Ruby, la sua dinamicità. Ho creato
una variabile di istanza mentre il programma funziona. Può sembrare normale, ma vi
assicuro che non lo è nei linguaggi di programmazione. Ruby, per questo (ma ce ne sono
anche altri) è considerato un linguaggio dinamico. Con il metodo instance_variable_set
ho impostato la variabile ed il suo valore mentre con instance_variable_get l'ho letto in
seguito.
Si possono fare molte cose con le caratteristiche dinamiche, come per esempio creare al
volo metodi, o eliminarli da una istanza.
Guardate come una espressione if..else..end restituisca un valore e per questo la chiamo
espressione. Guardate come si possono fare test multipli con la sintassi elsif che sta per
else if.
Con questo esempio abbiamo concluso questa superficiale panoramica su Shoes e vi
rimando al sito ufficiale per imparare ulteriormente.
Shoes è decisamente lenta, ve ne sarete accorti, ma era nata per una scopo bene preciso
che non è ovviamente quello di creare applicazioni complesse. È interessante per
imparare alcune delle regole base della programmazione ad eventi per le interfacce
grafiche; le cose più serie sono possibili anche in Ruby, ma bisogna andare su librerie
notevolmente più complesse.

3.6 Conclusioni
Abbiamo solo grattato la superficie e spero di avervi almeno fatto venire la curiosità su
cosa voglia dire programmare. Ora qui, abbiamo terminato.
Vi rimando alla documentazione ufficiale dove troverete molte cose interessanti:
http://www.ruby-doc.org/
Ruby è un linguaggio potente, adatto a molteplici usi. Se vogliamo trovare un difetto è
che non è veloce, anche se nelle varie versioni lo è sempre di più.
Ruby ha caratteristiche che non fanno rimpiangere quello, perché rende programmare
divertente e produttivi fin da subito il che non è niente male.
Potete fare della Metaprogrammazione per esempio e fare cose davvero magiche.
Potete creare metodi al volo mentre il programma sta funzionando, estendere classi
esistenti senza derivarle. Modificare comportamenti prefissati.
Il limite è davvero solo la fantasia.
Vorrei ringraziare Yukihiro Matsumoto, per averlo pensato ed inizialmente creato ed
aver aperto le porte ad una selva di programmatori contenti e rilassati.
Lo dico molto spesso, Ruby è magico e divertente.

Vous aimerez peut-être aussi