Questa vuole essere una breve introduzione a ruby per chi programma in Java. Non pensate di poter imparare tutto da questa pagina, essa vuole solo essere un piccolo assaggino che possa creare nel lettore almeno un po’ di curiosita’ ;).
Java e Ruby hanno alcuni punti di contatto importanti: anzitutto si tratta di linguaggi di livello alto rispetto al C/C++. Entrambi hanno un sistema di garbage collection automatica, permettono di avere programmi WORA, sono ad oggetti, e permettono solo l’ereditarieta’ singola (ma ruby ha strumenti piu’ potenti delle semplici interfacce).
Inoltre entrambi hanno una libreria “standard” molto vasta, controllo d’accesso per i metodi (private/protected/public) supporto per la programmazione multithread, moduli/namespace per fattorizzare il codice, ed un sistema di gestione degli errori tramite eccezioni.
Le differenze sono nel focus di fondo: ruby e’ fondato sulla semplice idea che le cose debbano essere semplici ed immediate per lo sviluppatore, piu’ che per il compilatore.
Ruby, a differenza di perl e php, è un linguaggio con tipi strong ma dinamici, il che significa poter scrivere:a= 10 # tipi non dichiarati esplicitamente b= 20 c= a+bma non poter scrivere:
a= "10" b= 20 c= a+b # errore!A proposito del type system di ruby è importante notare che ruby ha una OOP più pura di java, in quanto anche
Array, numeri, Classi e l’oggetto null (nil in ruby) sono oggetti di prima classe1.
Un esempio del fatto che ruby sia pensato per essere veloce e semplice e’ subito evidente nel fatto che non e’ necessario definire una classe MiaClasse con un metodo public static void main per eseguire un “hello world” o qualsiasi altra cosa.
irb(main):001:0> puts(self) main => nil
main e’ il nome della classe (in realta’, del modulo) in cui ci troviamo. self è equivalente al this di java, cioe’ e’ un puntatore all’oggetto corrente. Come in java possiamo ometterlo quasi sempre. Ad esempio quel metodo puts in realtà’ è un self.puts.
Infine, nil è il valore speciale simile al null di java, la funzione puts non si preoccupa di restituire un valore utile, e quindi restituisce nil.
- non servono ”;” a fine linea
- tutto ha un valore di ritorno
- ruby è developer oriented
Quello che si intende dire è che ruby si preoccupa di rendere la vita semplice allo sviluppatore, in ogni situazione. Quindi quando qualcosa puo’ essere fatta dall’interprete, come ad esempio definire la classe main, è meglio che lo faccia l’interprete.
Allo stesso modo, dato che una funzione di output come puts è necessaria in praticamente qualunque cosa, ruby la definisce nella classe Object, la “madre” di tutte le classi, in modo da evitare la fatica di un System.out.println(obj).
irb(main):002:0> puts self main => nilinsomma, omettere le parentesi intorno agli argomenti di un metodo, dato che non si crea ambiguità.. Questo e’ un concetto molto piu’ importante di quanto potrebbe sembrare, ad esempio molte cose che sono keyword in altri linguaggi, come
assert o print e persino public/private/protected possono essere semplici metodi in ruby. Inoltre ciò permette di definire dei Domain Specific Language molto facilmente.
Strutture di controllo:
Le strutture di controllo comuni sono disponibili anche in ruby. Notate pero’ che in ruby
vengono considerati falsi solo l’oggetto “false” e “nil”, mentre tutto il resto e’ considerato vero, quindi ad esempio, 0 (zero) o "" (la stringa vuota) sono comunque considerati veri.
I costrutti base:
if then else>> if 5 > 6 >> puts "incredibile, cinque maggiore di sei!" >> end => nil >> if 6 > 5 >> puts "ah, ok, sei maggiore di cinque.." >> end ah, ok, sei maggiore di cinque.. => nilPotete usare
if e unless come statement modifier cioe’ applicarli ad una sola riga, mettendoli alla fine:
>> puts 'ok' if true ok => nil >> puts 'il mondo funziona ancora' unless true == false il mondo funziona ancora => nilGli operatori di confronto sono quelli comuni (
>,<,>=,<=,==,!=) e gli operatori logici anche (!, &&, || o in inglese and, not, or).
operatore ternario:
>> 5 > 6 ? 'maggiore' : 'minore' => "minore"ciclo while:
>> x=0 => 0 >> while x < 5 >> puts x >> x+=1 >> end 0 1 2 3 4 => nilAbbiamo poi il case/switch:
>> while true >> case gets.chomp # gets legge una stringa da input, chomp toglie lo \n >> when 'ciao' >> puts "ciao a te" >> when 'addio' >> puts "a presto" >> end >> end ciao ciao a te addio a prestopotete aggiungere una clausola “
else” per gestire il caso di default.
case in ruby è differente da quello che c’è in java: esso è zucchero sintattico per l’operatore di case equality “===” (in ruby gli operatori sono semplici metodi che è possibile ridefinire per ogni classe) . Dato che questo operatore e’ definito in maniera intelligente per ogni classe, il costrutto case diventa molto piu’ potente di quello di java o c. Ad esempio, per la class Regexp l’operatore rappresenta il match su di una stringa, per una classe il fatto che un oggetto ne sia istanza, e cosi’ via:
>> /^c/ === 'ciao' # /^c/ e' una Regexp, === significa "corrisponde alla Regexp" => true >> String === 'ciao' # String e' una Classe, === significa "istanza della Classe" => true >> (1..10) === 5 # 1..10 e' un oggetto Range, === significa "nel Range" => true
Ovviamente non userete mai l’operatore di case equality da solo come in questo caso, ma quasi unicamente dall’interno di un case.
L’ultima struttura di controllo di cui parlerò è il for..end.
for comune nei linguaggi di scripting:
>> for i in 1..10 >> print i >> end 12345678910=> 1..10La magia sta nel fatto che in realtà il
for fa uso di un meccanismo simile al modello degli iteratori in java.
In pratica ogni classe che rappresenti un oggetto iterabile definisce un metodo each il quale restituisce ogni elemento della collezione. Cosi’, ad esempio un oggewtto Range fornisce i valori da 1 a 10, un Array i suoi elementi, ed un Hash delle coppie chiave/valore.
for di nascosto usa questo metodo. Ma in realtà è comune usarlo direttamente:
>> a=[1,20,3,40,5] # un Array puo' essere definito [elemento,elemento] => [1, 20, 3, 40, 5] >> a.each do |elemento| # tra le | si mettono i nomi degli argomenti passati ?> puts elemento >> end 1 20 3 40 5 => [1, 20, 3, 40, 5]
il blocco do..end e’ la maggior parte della magia di ruby, per approfondirlo leggete blocchi in ruby
In sostanbza essi permettono di salvare iu pezzetto di codice che potete passare qua e là, con il quale potete creare le vostre strutture di controllo, e molte altre cose.
Ad esempio:connessione_a_database.transazione do #inizio transazione ..esegui inserimento.. ..esegui cancellazione.. end # commit o rollbackoppure potete rendere generali dei meccanismi semplici come l’ordinamento:
>> a => [1, 20, 3, 40, 5] >> a.sort # metodo builtin, paragona il semplice valore => [1, 3, 5, 20, 40] >> a.sort_by {|elem| elem % 19 } # ordina basandosi sul valore di ritorno del blocco => [1, 20, 40, 3, 5] >>persone.sort_by {|pers| pers.cognome, pers.nome} => [ "Bianchi Mario", "Bianchi Ugo", "Rossi Ada"]
Non esiste limite a quel che si puo’ fare con i blocchi. Potete implementare la maggior parte dei meccanismi della progrmmazione funzionale, applicare trasformazioni complesse a strutture dati ancor piu’ complesse, localizzare e fattorizzare operazioni ripetitive..
I blocchi sono una cosa talmente utile che esistono persino framework per usarli in java :)(mini esempio: URL:http://c2.com/cgi/wiki?BlocksInJava?
h3. Metodi Abbiamo visto come si crea una classe. Come si fa a definire un metodo? Facile:>> def met (arg1,arg2) >> puts arg1,arg2 >> endcioe’ :
def nomemetodo argomenti end
in realta’ rispettando la regola che le cose ovvie possono essere omesse potreste scrivere:
><pre
> def met arg1, arg2
>> puts arg1,arg2
>> end
cioe’ evitare le parentesi. In generale non e’ uno stile molto diffuso, ma alcuni (ad esempio Martin Fowler) lo usano. De gustibus :)
Notate che non specifichiamo il tipo delle variabili, e quindi non si può avere overload dei metodi. Ok, in realta’ esistono librerie che permettono sia di specificare i tipi che di avere funzioni “overloadate”, ma non sono molto usate.
Nel definire i metodi possiamo anche dire a ruby di accettare un numero variabile di argomenti, come i vararg in java5:>> def met( *args) >> puts args.class >> puts args >> end => nil irb(main):041:0> met 1, 2 ,3 ,4 ,5 Array 1 2 3 4 5 => nilcome vedete ruby raccoglie tutti gli argomenti nella variabile prefissata con un “
*”, mettendoli dentro un Array.
Il fatto di vedere tutti gli elementi stampati uno per linea dipende da puts, che controlla se l’oggetto e’ un Array ed in quel caso ne stampa gli elementi uno per linea.
E’ anche possibile assegnare dei valori di default alle variabili, cosa che in java non è possibile e si ottiene tipicamente definendo un metodo sovraccaricato molte volte:
>> def stampa_persona( nome='mario', cognome='rossi') >> print "il signor " + nome + " " + cognome >> print "\n" >> end => nil >> stampa_persona 'franco', 'bianchi' il signor franco bianchi => nil >> stampa_persona 'franco' il signor franco rossi => nil >> stampa_persona il signor mario rossi => nilL’ultima cosa che dovreste chiedervi e’: come faccio a gestire un blocco? Esistono due modi. L’unico che vi spieghero’ e’ usando la keyword
yield:
>> def due >> yield 1 >> yield 2 >> end => nil >> due {|x| p x } 1 2Se non gli passiamo un blocco abbiamo un errore:
>> due LocalJumpError: no block given from (irb):26:in `due' from (irb):30 from &#9829;:0
nel dare nomi ai metodi non siamo limitati ad usare le lettere [_a-z] nel dare nomi ai metodi, possiamo infatti usare alcuni altri caratteri, come ! e ?.
In generale si usa un metodo che finisce in ? per i metodi che restituiscono valori boleani. In questo caso era:
viene passato un blocco? si o no
>> 5.is_a? Numeric => true >> String.respond_to? '+' => false >> "String".respond_to? '+' => truenotate come ruby fornisca una Reflection molto più immediata di quella di java, altri esempi sono:
>> "ciao".class.ancestors => [String, Enumerable, Comparable, Object, Kernel] >> 5.class.superclass => Integer... e tantissimi altri. I metodi con un punto esclamativo sono ispirati alla tradizione del linguaggio Scheme? ed altri, e rappresentano metodi che in qualche modo sono “pericolosi”. Ad esempio, guardate il metodo
sort degli Array:
>> a=[1,2,23,434,1,23,3,123] => [1, 2, 23, 434, 1, 23, 3, 123] >> a.sort => [1, 1, 2, 3, 23, 23, 123, 434] >> a => [1, 2, 23, 434, 1, 23, 3, 123]come vedete
a non e’ stato modificato. Guardate ora:
>> a.sort! => [1, 1, 2, 3, 23, 23, 123, 434] >> a => [1, 1, 2, 3, 23, 23, 123, 434]In questo caso
a viene modificato, cioe’ sort! fa side effect?.
I punti esclamativi sono li per dire: “ok, magari questo metodo e’ piu’ veloce dell’altro, ma usalo con un po di testa, perche’ potresti fare casino”. Anche in questo caso il linguaggio non ci impone delle cose, semplicemente cerca di darci dei consigli.
Andiamo avanti definendo una semplice classe:irb(main):003:0> class Foo irb(main):004:1> end => nilsemplice semplice. Notate che abbiamo un valore di ritorno anche per la dichiarzione di una classe. Notate anche che non e’ necessario, in ruby, definire una sola classe pubblica per file. In effetti ‘pubblica’ non e’ nemmeno un qualcosa che si applichi ad una classe in ruby. Una cosa importante e’ che una classe deve avere un nome che inizi con la lettera maiuscola:
irb(main):005:0> class foo irb(main):006:1> end SyntaxError: compile error (irb):5: class/module name must be CONSTANT from (irb):6Infatti, ruby adotta il sistema comune di dare un nome maiuscolo alle costanti come regola. In questo modo lo sviluppatore e’ indirizzato verso uno stile comune in modo gentile. Potete sempre scrivere:
irb(main):007:0> miao=Foo => Fooed usare
miao come classe, semplicemente ruby spinge per un’approccio differente. Comunque, a questo punto abbiamo visto altri quattro concetti:
* non esiste una keyword final. Le costanti sono scritte con una lettera maiuscola
* l’assegnazione avviene come in java:
nomevariabile= valore
* una variabile viene dichiarata nel momento in cui gli viene assegnato un valore.
* una variabile non ha un tipo. L’oggetto a cui la variabile si riferisce si:
irb(main):008:0> miao.class => Class irb(main):009:0> a='ciao' => "ciao" irb(main):010:0> a.class => String irb(main):011:0> 5.class => Fixnum irb(main):012:0> 'ciao'+5 TypeError: cannot convert Fixnum into String from (irb):12:in `+' from (irb):12come vedete ruby lancia un’eccezione nel caso si cerchi di mischiare tipi differenti.
Notate che anche se javac fa del suo meglio per cercare errori di tipo in fase di compilazione, alcuni gli sfuggeranno sempre e causeranno eccezioni a runtime, esattamente come in ruby.
Una differenza chiave nell’uso delle classi è che mentre in java potete ereditare l’implementazione di metodi solo da una classe, pur avendo l’ereditarietà multipla di interfaccia, in ruby potete ereditare o dalla superclasse o da dei Mixin. In sostanza potete pensare, che i miixin forniscano un’implementazione di default pr metodi comuni.
1 un esempio dei vantaggi di questo comportamento è ad esempio una classe Pair