class Count def initialize @count = 0 end def get @count end def incr(n) @count = @count + n end def Count.test count = Count.new count.incr(1) count.incr(2) end enddefinisce una class Count. Dentro la class sono definiti 4 metodi. Il primo metodo initialize
def initialize @count = 0 endha uno status particolare. Sarà chiamato automaticamente alla creazione di un oggetto della class Count. Ruby utilizza il prefisso @ per indicare un campo di un oggetto e il prefisso @@ per indicare un campo statico di un oggetto. I campi non hanno bisogno di essere dichiarati (la stesso vale per le variabili locali). Dunque il metodo initialize mette a zero il campo count.
Il secondo metodo get
def get @count endpermette di avere il valore di un campo. Un tale metodo è necessario in Ruby perché i campi non sono visibili fuori della class. Questa definizione illustra due aspetti importanti di Ruby. Primo, Ruby non è tipato. La dichiarazione non ha bisogno di dare il tipo del valore restituito dal metodo get. Secondo, tutte le istruzioni di Ruby sono considerate comme delle espressioni. Dunque il body di un metodo restituisce sempre un valore. Quindi un modo equivalente di definire lo stesso metodo sarebbe
def get return @count endIl terzo metodo incr
def incr(n) @count = @count + n endpermette di incrementare il campo count. L'assegnazione in Ruby è un'espressione. Dunque il metodo incr restituisce il nuovo valore del campo count. Il quarto metodo Count.test
def Count.test count = Count.new count.incr(1) count.incr(2) enddefinisce un metodo di class (l'equivalente di un metodo statico in Java). Il body prima definisce una variabile locale count che inizializza con un nuovo oggetto di tipo Count. In Ruby, gli oggetti sono creati chiamando il metodo new. Dopo l'inizializzazione della variabile count, il codice chiama due volte il metodo incr. Dunque il valore restituito è 3.
In Ruby tutti i valori manipolati sono degli oggetti e ci sono solamente chiamate di metodi. Per esempio, l'espressione
1 + 2 * 3è interpretata come
1.plus(2.mult(3))
Una proprietà importante di Ruby è la sua dinamicità. In qualsiasi momento, si può aggiungere un metodo ad una class. La verifica che una chiamata di un metodo è valida è effettuata solamente a tempo di esecuzione. Per esempio, un modo equivalente di definire la class precedente sarebbe
class Count def initialize @count = 0 end def get @count end def incr(n) @count = @count + n end end class Count def Count.test count = Count.new count.incr(1) count.incr(2) end endLa ridichiarazione della class Count non è considerata come una ridefinizione ma come un'aggiunta. Ruby non permette l'overloading dei metodi. Dunque con il codice seguente
class Count def initialize @count = 0 end def get @count end def incr(n) @count = @count + n end end class Count def incr @count = @count + 1 end endLa seconda dichiarazione del metodo incr è considerata come una ridichiarazione. Un oggetto Count avrà in seguito un solo metodo incr senza parametri. Come in Java, Ruby permette di estendere una class per crearne un'altra. Una class può estendere una sola class. Per esempio,
class A def initialize @a = 0 end end class B < A @@c = 0 def initialize super() @b = 0 end endcrea una estensione B della class A aggiungendo un campo b ed un campo statico c.
Un meccanismo molto potente di Ruby è la possibilità di passare una chiusura nella chiamata di un metodo. Tale meccanismo permette non solo di passare una funzione come parametro di un metodo ma anche di assicurarsi che l'esecuzione di tale funzione (tramite la parola chiave yield) si farà nel contesto della creazione della funzione. Quindi una chiusura è composta da una funzione e dal suo contesto. Per esempio,
class A def twice(n) yield(n) yield(n+1) end def test a = 0 twice(5) do |m| a = a + m end a end endil metodo test chiama il metodo twice con il parametro 5 e la chiusura parametrizzata da m
do |m| a = a + m endche fa un'assegnazione. Nella dichiariazione del metodo twice il parametro n è passato in modo esplicito, invece la chiusura è passata in modo implicito. Quando il metodo twice eseguirà la chiusura, la variabile locale a di test sarà modificata in conseguenza. Nel nostro caso, il metodo test è equivalente a
def test a = 0 a = a + 5 a = a + 6 a endQuindi la chiamata del metodo test ritornerà sempre 11. Un'introduzione più completa del linguaggio può essere trovata all'indirizzo http://ruby-doc.com/docs/ProgrammingRuby/.
La base del progetto consiste nell'avere una rappresentazione di un programma Ruby come un oggetto Java. Per esempio, al programma
class A def initialize @a = 0 end endpotrebbe corrispondere l'oggetto
new Programma( new ClassDef(new ClassName("A"), new ClassName("Object"), new ClassBody( new MethodDef(new MethodName("initialize"), new Parameter[]{}, new Body(new Assign(new FieldName("a"), new Int(0))) ) ) ) )La base deve permettere
Esempi di programmi che si dovrebbe essere capace di scrivere sono qui. Un esempio d'inizio di implementazione è qui
La base del progetto dovrà obbligatoriamente essere consegnata prima della prova scritta dell'esame.
A partire da questa base, si può completare il progetto realizzando per esempio:
Lo studente dovrebbe seguire le convenzioni di codifica date durante il corso. Per verificare la conformità del progetto, si può usare l'archivio checkstyle.jar. Eseguendo
java -jar checkstyle.jar File.javasi genera un file report che indica la conformità del codice.