|
|||
Precedente < | Indice ^ |
Prossimo
> |
Altri linguaggi hanno funzioni, procedure, metodi o routines, ma in Ruby esiste solo il metodo---un blocco di espressioni che restituisce un valore.
In un latro punto di questo libro si é parlato della definizione e dell'uso dei metodi, senza considerare altro. Ora ci occuperemo dei dettagli.
Come abbiamo visto altrove, un metodo viene definito usando la parola
chiave def
. I nomi di metodo possono iniziare con una lettera
minuscola.[Non otterrete immediatamente un errore se usaste una
maiuscola, ma quando Ruby vi vedrà chiamare il metodo, per prima cosa sarà
portato a credere che si tratti di una costante e non dell'invocazione di un
metodo e, come risultato, potrebbe processare la chiamata in modo
errato.]
I metodi che comportano un'azione come le queries vengono spesso chiamate
con un ``?
'', come in instance_of?
. I metodi
``critici,'' o che possono modificare il receiver, dovrebbero essere chiamati
con con un ``!
''. Ad esempio, String
ammette sia
chop
che chop!
. Il primo restituisce una stringa
modificata, il secondo modifica il receiver immediatamente.
``?
'' e ``!
'' sono i soli caratteri accettati come
suffisso del nome di un metodo.
Ora che si é specificato un nome per un nuovo metodo, potremmo dover dichiarare qualche parametro. Questi sono semplicemente una lista di nomi di variabile locale entro parentesi. Ecco qualche esempio di dichiarazione di metodo:
def myNewMethod(arg1, arg2, arg3) # 3 argomenti # Il codice per il metodo dovrebbe essere quì end def myOtherNewMethod # Nessun argomento # Il codice per il metodo dovrebbe essare quì end |
Ruby consente di specificare valori di default per l'argomento di un metodo ---valori che saranno utilizzati se non venissero compresi esplicitamente nella chiamata. Ciò viene fatto usando operatori di assegnazione
def coolDude(arg1="Miles", arg2="Coltrane", arg3="Roach") |
||
"#{arg1}, #{arg2}, #{arg3}." |
||
end |
||
|
||
coolDude |
» | "Miles, Coltrane, Roach." |
coolDude("Bart") |
» | "Bart, Coltrane, Roach." |
coolDude("Bart", "Elwood") |
» | "Bart, Elwood, Roach." |
coolDude("Bart", "Elwood", "Linus") |
» | "Bart, Elwood, Linus." |
Il corpo del metodo contiene normali espressioni di Ruby, eccetto che non
si possa definire un metodo d'istanza, di classe o di modulo senza un metodo.
Il valore di ritorno é il valore dell'ultima espressione eseguita o il
risultato di un esplicita espressione di return
.
Ma se voleste passare ad una variabile diversi argomenti o catturare molti argomenti entro un singolo parametro? Piazzando un asterisco prima del nome del parametro dopo i parametri ``normal'' si ottiene proprio ciò.
def varargs(arg1, *rest) |
||
"Got #{arg1} and #{rest.join(', ')}" |
||
end |
||
|
||
varargs("one") |
» | "Got one and " |
varargs("one", "two") |
» | "Got one and two" |
varargs "one", "two", "three" |
» | "Got one and two, three" |
In questo esempio il primo argomento é assegnato al primo parametro di
metodo come al solito. Comunque il prossimo paragrafo viene preceduto da un
asterisco, così tutti i restanti argomenti vengono compresi in un nuovo
Array
, che viene allora assegnato a quel parametro.
Come descritto nella sezione blocchi ed iteratori, che comincia a pagina
38, quando un metodo viene invocato può essere associato ad un blocco.
Normalmente potete invocare il blocco, senza il metodo, usando
yield
.
def takeBlock(p1) if block_given? yield(p1) else p1 end end |
takeBlock("no block") |
» | "no block" |
takeBlock("no block") { |s| s.sub(/no /, '') } |
» | "block" |
Se l'ultimo parametro nella definizione di un metodo é preceduto da una
"e" commerciale ogni blocco associato viene convertito in un oggetto
Proc
e questo oggetto viene assegnato al parametro.
class TaxCalculator |
||
def initialize(name, &block) |
||
@name, @block = name, block |
||
end |
||
def getTax(amount) |
||
"#@name on #{amount} = #{ @block.call(amount) }" |
||
end |
||
end |
||
|
||
tc = TaxCalculator.new("Sales tax") { |amt| amt * 0.075 } |
||
|
||
tc.getTax(100) |
» | "Sales tax on 100 = 7.5" |
tc.getTax(250) |
» | "Sales tax on 250 = 18.75" |
Chiamate un metodo specificando un receiver, il nome del metodo e, se volete, qualche parametro ed un blocco associato.
connection.downloadMP3("jitterbug") { |p| showProgress(p) } |
In questo esempio connection
é il receiver,
downloadMP3
é il nome del metodo "jitterbug"
é il
parametro e quanto compreso tra parentesi é il blocco associato.
Per i metodi di classi e di modulo, il receiver sarà la classe o il nome del modulo.
File.size("testfile") Math.sin(Math::PI/4) |
Se omettete il receiver, esso diviene per default self
,
l'oggetto corrente.
self.id |
» | 537790064 |
id |
» | 537790064 |
self.type |
» | Object |
type |
» | Object |
Questo meccanismo é come Ruby implementa i metodi privati di default. I metodi privati non possono essere chiamati con un receiver, così debbono esservi metodi validi nell'oggetto corrente.
Il parametro opzionale segue il nome del metodo. Se non ci fosse ambiguità si possono omettere le parentesi attorno alla lista degli argomenti quando si invoca un metodo. [Altri documenti su Ruby chiamano queste invocazioni di metodo senza parentesi ``commands.''].
Comunque a parte i casi più semplici non ve lo consigliamo --- ci sono molti problemi poco evidenti che vi possono portare fuori strada. [Particolarmente dovete usare parentesi in una chiamata di metodo che sia essa stessa un parametro rivolto ad un'altra chiamata di metodo (se non é l'ultimo parametro).] La regola é semplice: se vi fossero dubbi usate le parentesi.
a = obj.hash # Lo stesso di a = obj.hash() # questo obj.someMethod "Arg1", arg2, arg3 # Stessa cosa come obj.someMethod("Arg1", arg2, arg3) # con le parentesi. |
Precedentemente abbiamo visto che se inserite un asterisco davanti ad un parametro formale in una definizione di metodo, argomenti multipli nella chiamata al metodo saranno raccolti al' interno di un array. Bene, la stessa cosa funziona al contrario.
Quando chiamate un metodo, potrete esplodere un array, in modo che ogni sua parte sia preso come un parametro separato. Per fare ciò prefisserete l' argomento dell' array (che dovrà seguire tutti gli argomenti regolari) con un asterisco.
def five(a, b, c, d, e) |
||
"I was passed #{a} #{b} #{c} #{d} #{e}" |
||
end |
||
|
||
five(1, 2, 3, 4, 5 ) |
» | "I was passed 1 2 3 4 5" |
five(1, 2, 3, *['a', 'b']) |
» | "I was passed 1 2 3 a b" |
five(*(10..14).to_a) |
» | "I was passed 10 11 12 13 14" |
Abbiamo già visto come si può associare un blocco ad una chiamata di un metodo.
listBones("aardvark") do |aBone| # ... end |
Normalmente, questo è sufficientemente corretto --- assocerete un blocco
fisso di codice con un metodo, ma nello stesso modo potreste avere un grosso
pezzo di codice dopo un affermazione if
o while
.
Talvolta, in qualche modo, vorreste essere maggiormente flessibili. Per esempio, possiamo insegnare tecniche di matematica. [Naturalmente, Andy e Dave dovrebbero impararle prima. Conrad Schneiker ci ricordò che esistono tre specie di persone: coloro che possono contare e coloro che non possono.] Lo studente potrebbe chiedere una tabella alla n-potenza o a base n. Se lo studente chiedesse una tabella a base due, dovremmo dare come risposta 2, 4, 6, 8 e così via (Questo codice non verifica gli errori di input).
print "(t)imes or (p)lus: " times = gets print "number: " number = gets.to_i if times =~ /^t/ puts((1..10).collect { |n| n*number }.join(", ")) else puts((1..10).collect { |n| n+number }.join(", ")) end |
produce:
(t)imes or (p)lus: t number: 2 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 |
Funziona, ma è brutto, con un identico codice virtuale in ogni
ramificazione dell affermazione if
. Se volessimo renderlo bello
dovremmo estrarre il blocco che esegue il calcolo.
print "(t)imes or (p)lus: " times = gets print "number: " number = gets.to_i if times =~ /^t/ calc = proc { |n| n*number } else calc = proc { |n| n+number } end puts((1..10).collect(&calc).join(", ")) |
produce:
(t)imes or (p)lus: t number: 2 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 |
Se l' ultimo argomento al metodo è preceduto da una ``&'', Ruby
considera che sia un oggetto Proc
. Lo rimuove dalla lista dei
parametri, converte l' oggetto Proc
in un blocco, e lo associa
al metodo.
Questa tecnica può anche essere usata per aggiungere un preziosismo sintattico al blocco da usare. Per esempio, talvolta potreste voler prendere un iteratore e salvare ogni valore che produce all'interno di un array. Riutilizzeremo il nostro generatore di numeri Fibonacci utilizzato a pagina 40.
a = [] |
||
fibUpTo(20) { |val| a << val } |
» | nil |
a.inspect |
» | "[1, 1, 2, 3, 5, 8, 13]" |
Funziona, ma la nostra intenzione non è così trasparente come vorremo.
Invece definiremo un metodo denominato into
, che restituisce il
blocco che riempie l' array. ( Notate che nello stesso momento in cui il
blocco ritorna è realmente chiuso --- fà riferimento al parametro
anArray anche dopo che il metodo into
è stato
reinviato.)
def into(anArray) |
||
return proc { |val| anArray << val } |
||
end |
||
|
||
fibUpTo 20, &into(a = []) |
||
a.inspect |
» | "[1, 1, 2, 3, 5, 8, 13]" |
Alcune caratteristiche dei linguaggi ``parole chiave'' --- che sarebbe, invece di passare gli argomenti in un determinato ordine e quantitativo, passate il nome dell' argomento con il suo valore, in qualsiasi ordine. Ruby, dalla versione 1.6, non ha ``parole chiave'' (sebbene siano previste nell'implementazione della versione 1.8).'
Nel frattempo, le persone utilizzano gli hash per ottenere lo stesso
effetto. Per esempio, potremo considerare di aggiungere una caratteristica
maggiormente potente sulla ricerca dei nomi nella nostra
SongList
.
class SongList def createSearch(name, params) # ... end end aList.createSearch("short jazz songs", { 'genre' => "jazz", 'durationLessThan' => 270 } ) |
Il primo parametro è il nome da ricercare, ed il secondo è un hash
letterale contenente i parametri di ricerca. L' utilizzo di un hash significa
che possiamo simulate le parole: cercare canzoni con un valore di ``jazz'' e
una durata inferiore ai quattro minuti e mezzo (270 secondi). In qualche
modo, questo approccio è un pò complesso, e che quel gruppo di parentesi
potrebbe essere scambiato con un blocco associato al metodo. Per questa
ragione, Ruby, dispone di una scorciatoia. Potrete posizionare un
valore =>
in una lista di argomenti, per
tutta la lunghezza che seguono qualsiasi argomento normale e precedono
qualsiasi array e blocco di argomenti. Tutte queste coppie saranno
memorizzate in un un singolo hash e passate come un argomento del metodo.
Nessuna parentesi è necessaria.
aList.createSearch("short jazz songs", 'genre' => "jazz", 'durationLessThan' => 270 ) |
Precedente < | Indice ^ |
Prossimo > |
Extracted from the book "Programming Ruby - The Pragmatic Programmer's
Guide"
Copyright © 2000 Addison Wesley Longman, Inc. Released under the terms of the
Open Publication License
V1.0.
This reference is available for download.