Ruby For Javanese

Vedi tutte le pagine e le modifiche recenti o scarica i sorgenti nella pagina


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+b 
ma 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.

L’interprete infatti si preoccupa di definire una classe automaticamente al posto nostro. Ad esempio, se lanciate la shell interattiva di ruby, IRB, potete provare a fare questo:
 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.

Qui vedete subito in uso tre concetti fondamentali di ruby:

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).

Rispettando il concetto del “fai tu che a me non va”, avremmo potuto scrivere anche:
 irb(main):002:0> puts self
 main
 => nil
insomma, 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..
 => nil
Potete 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
 => nil
Gli 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 
 => nil
Abbiamo 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 presto
potete 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.

In pratica si tratta del solito for comune nei linguaggi di scripting:
 >> for i in 1..10
 >>  print i
 >> end
 12345678910=> 1..10
La 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 rollback
oppure 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
  >> end
cioe’ : 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
 => nil
come 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
 => nil
L’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
 2
Se non gli passiamo un blocco abbiamo un errore:
>> due
LocalJumpError: no block given
        from (irb):26:in `due'
        from (irb):30
        from &amp;#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

altri esempi:
 >> 5.is_a? Numeric
 => true
 >> String.respond_to? '+'
 => false
 >> "String".respond_to? '+'
 => true
notate 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
 => nil
semplice 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):6
Infatti, 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
 => Foo
ed 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):12
come 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

Updated on January 24, 2007 12:06 by Ruby Fan (unknown)