Académique Documents
Professionnel Documents
Culture Documents
elementare
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
1.1.2 Alice
1.1.4 KidsRuby
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.
È 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.
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
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>
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>
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>
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>
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>
ruby source.
irb(main):031:0> "pippo" * 5 + "pluto"
=> "pippopippopippopippopippopluto"
irb(main):032:0>
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.
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
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.
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
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
puts "Alla festa sono arrivate:"
puts pere_arrivate_alla_festa
puts "pere"
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"
sh source.
ruby pere2.rb
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 source.
puts "Alla festa sono arrivate:" + pere_arrivate_alla_festa.to_s + "pere"
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"
sh source.
➜ source ruby pere3.rb
Alla festa sono arrivate: 24 pere
➜ source
Meglio no?
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:
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
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.
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>
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.
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>
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>
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>
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
sh source.
➜ source ruby test.rb
Quanti anni hai?
8
non fai la quarta elementare
sh source.
➜ source ruby test.rb
Quanti anni hai?
9
non fai la quarta elementare
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>
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
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
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
sh source.
➜ source ruby test3.rb
Quanti anni hai?
2
Fai ancora l'asilo
➜ source ruby test3.rb
Quanti anni hai?
6
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
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
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
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.
I cicli ci permettono di ripete delle istruzioni più volte. Ruby ha alcuni tipi di cicli come la
maggior parte dei linguaggi di programmazione.
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!'
ruby source.
numeri = [0,1,2,3,4,5,6,7,8,9]
for numero in numeri
puts numero
end
sh source.
➜ source ruby loop2.rb
0
1
2
3
4
5
6
7
8
9
➜ source
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']]
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")
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.
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
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]))
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
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
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.
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")
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")
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).
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):
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:
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
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>
ruby source.
class Automobile
attr_reader :ruote
attr_accessor :persone
def initialize
@ruote = 4
end
end
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.
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
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
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:
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?
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
ciao
ciao
ciao
ruby source.
ciao = Proc.new do |a_chi|
puts "ciao"
end
ciao.call('Mondo')
ciao.call('Carlo')
ciao.call('Camilla')
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)
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
sh source.
c:\Documenti\Ruby\il_mio_file.txt
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.
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:
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
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:
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']
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
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
sh source.
➜ ruby dizionario.rb
chiave: 1
valore: massimo
chiave: cognome
valore: ghisalberti
➜
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
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'
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
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.
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.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.
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..."
}
ruby source.
Shoes.app {
button "Ciao, premi..."
button "Io sono il secondo bottone, premi..."
}
ruby source.
Shoes.app {
button "Ciao, premi..."
button "Io sono il secondo bottone, premi..."
oval(left: 20, top: 40, radius: 50)
}
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)
}
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
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!")
}
}
ruby source.
Shoes.app {
button("premi...") {
alert("Ciao Mondo1!")
}
button("premi...") {
alert("Ciao Mondo2!")
}
}
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."))
}
}
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"
}
}
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
}
}
}
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.