|
|||
Precedente < | Indice ^ |
Prossimo > |
Finora siamo diventati sufficientemente disinvolti nell'uso di espressioni
in Ruby. Dopo tutto, a=b+c
è una cosa abbastanza normale.
Potreste scrivere un intero sacco di codice Ruby senza leggere alcunchè di
questo capitolo.
Ma non sarebbe altrettanto divertente ;-)
.
Una delle prime differenze con Ruby è che tutto può ragionevolemte restituire un valore a qualsiasi cosa sia un'espressione. Che cosa significa in pratica?
Ovviamente bisogna essere capaci a concatenare tra di loro le dichiarazioni.
a = b = c = 0 |
» | 0 |
[ 3, 1, 7, 0 ].sort.reverse |
» | [7, 3, 1, 0] |
Forse meno ovvio, cose che normalmente sono dichiarazioni in C o in Java,
sono espressioni in Ruby. Per esempio, le dichiarazioni if
e
case
restituiscono entrambe il valore dell' ultima espressione
eseguita.
songType = if song.mp3Type == MP3::Jazz if song.written < Date.new(1935, 1, 1) Song::TradJazz else Song::Jazz end else Song::Other &sp; end rating = case votesCast when 0...10 then Rating::SkipThisOne when 10...50 then Rating::CouldDoBetter else Rating::Rave end |
Parleremo più approfonditamente di if
e case
a
partire da pagina 81.
Ruby dispone di un insieme di operatori (+, -, *, /,
e così
via) con qualche sorpresa. Una lista completa degli operatori, e delle loro
rispettive precedenze, è visionabile a pagina 221 all'interno della tabella
18.4.
In Ruby, molti operatori sono chiamate a metodi. Quando scrivete
a*b+c
si interroga l' oggetto referenziato da a
ad
eseguire il metodo ``*
'', passando nel parametro b
.
Si può poi interrogare l'oggetto risultante dal calcolo eseguendo
``+
'', passando c
come parametro. Ciò è esattamente
uguale a scrivere:
(a.*(b)).+(c) |
Poiché qualsiasi cosa é un oggetto e poiché si possono ridefinire metodi di istanza, si può sempre ridefinire i fondamentali aritmetici se non vi piacesse la risposta che state ricevendo.
class Fixnum |
||
alias oldPlus + |
||
def +(other) |
||
oldPlus(other).succ |
||
end |
||
end |
||
|
||
1 + 2 |
» | 4 |
a = 3 |
||
a += 4 |
» | 8 |
Sicuramente più utile è la caratteristica che la classe che state
scrivendo può partecipare nelle espressioni con operatore esattamente come
fossero oggetti integrati. Per esempio, potremmo voler estrarre un numero di
secondi di musica dal mezzo di una canzone. Potremo usare l' operatore di
indicizzazione ``[]
''per specificare la musica che dovrà essere
estrattta.
class Song def [](fromTime, toTime) result = Song.new(self.title + " [extract]", self.artist, toTime - fromTime) result.setStartTime(fromTime) result end end |
Questo frammento di codice estende la classe Song
con il
metodo ``[]
'', il quale prende due parametri (un tempo di
partenza ed uno di fine). Restituisce una nuova canzone la cui musica è
compresa entro un intervallo dato. Potremo poi suonare l' introduzione della
canzone con il codice::
aSong[0, 0.15].play |
Come le ovvie espressioni con operatore e le chiamate al metodo, e (forse)
le meno ovvie espressioni con dichiarazione (come if
e
case
), Ruby possiede qualcosa in più che può essere usato nelle
espressioni.
Se richiudete una stringa all'interno di una virgola rovesciata, o se si
utilizza il modello delimitato preceduto da %x
, ciò sara
eseguito (di default) come un comando del vostro sistema operativo
sottostante. Il valore dell'espressione sarà lo standard output del comando.
Le linee vuote non verranno rimosse se il valore che otterrete avrà portato
con se un carattere di return o di new line.
`date` |
» | "Sun Mar 4 23:23:52 CST 2001\n" |
`dir`.split[34] |
» | "lib_pstore.txi" |
%x{echo "Hello there"} |
» | "Hello there\n" |
Potrete usare l' espansione di espressioni e tutte le normali sequenze di escape nella stringa di comando.
for i in 0..3 status = `dbmanager status id=#{i}` # ... end |
Lo stato di uscita del comando è disponibile nella variabile globale
$?
.
Nella descrizione del'espressione di comando di output, abbiamo detto che
la stringa tra apici inversi sarebbe stata eseguita ``di default'' a guisa di
un comando. Infatti, la stringa viene passata al metodo denominato Kernel::`
(un solo
apice inverso). Se si vuole ciò può essere ignorato.
alias oldBackquote ` def `(cmd) result = oldBackquote(cmd) if $? != 0 raise "Command #{cmd} failed" end result end print `date` print `data` |
produce:
Sun Mar 4 23:23:52 CST 2001 prog.rb:3: command not found: data prog.rb:5:in ``': Command data failed (RuntimeError) from prog.rb:10 |
Ogni esempio che abbiamo fornito finora in questo libro ha la caratteristica dell' assegnamento. A questo punto è giunto il momento di parlarne.
Una asserzione di assegnamento fissa la variabile o l' attributo alla sua sinistra (il lvalue) con il riferimento al valore alla sua destra (il rvalue). Ciò restituisce quel valore come risultato dell' espressione di assegnamento. Ciò significa che si possono concatenare e che si possono effettuare gli assegnamenti in qualche situazione imprevista.
a = b = 1 + 2 + 3 |
||
a |
» | 6 |
b |
» | 6 |
a = (b = 1 + 2) + 3 |
||
a |
» | 6 |
b |
» | 3 |
File.open(name = gets.chomp) |
Esistono due fondamentali forme di assegnamento in Ruby. La prima assegna il riferimento ad un oggetto, ad una variabile o ad una costante. Questa forma di assegnamento è definita intrinseca.
instrument = "piano" MIDDLE_A = 440 |
La seconda forma di assegnamento implica di disporre un attributo ad un oggetto o a un riferimento ad un elemento sul lato sinistro.
aSong.duration = 234 instrument["ano"] = "ccolo" |
Queste forme sono speciali, perchè sono implementate chiamando i metodi nei valori posti a sinistra (lvalues), che significa che potrete anche ignorarle.
Abbiamo già visto come definire un attributo ad un oggetto scrivibile. Semplicemente definite un nome di un metodo che termini con un segno di uguale. Questo metodo riceve come propri parametri l'assegnamento del valore di destra (rvalue).
class Song def duration=(newDuration) @duration = newDuration end end |
Non esiste ragione per cui questi metodi di settaggio degli attributi debbano corrispondere a variabili interne di istanza, o che esista un lettore di attributi per ciascuno scrittore di attributi (o viceversa).
class Amplifier def volume=(newVolume) self.leftChannel = self.rightChannel = newVolume end # ... end |
Nota: Utilizzare inserimenti senza una classe | ||||||||||||||||||||||||||||||||||||
Perchè abbiamo scritto self.leftChannel nell'esempio a
pg. 76? Bè, si tratta di un tranello nascosto con attributi in
scrittura. Normalmente, i metodi all'interno di una classe possono
invocare altri metodi nella stessa classe e nelle sue superclassi in
modo funzionale (cioè con un implicito receiver di
self ). Comunque, ciò non funziona con gli attributi in
scrittura. Ruby vede gli assegnamenti e decide che il nome sulla
sinistra deve essere una variabile locale, non una chiamata ad un
metodo di un attributo in scrittura.
Se dimenticassimo di mettere `` |
Durante la vostra prima settimana del corso di programmazione (o il secondo semestre se i trattasse di una scuola di gruppo ), avreste dovuto scrivere del codice per scambiare il valore di due variabili:
int a = 1; int b = 2; int temp; temp = a; a = b; b = temp; |
Potrete fare ciò più chiaramente con Ruby:
a, b = b, a |
Gli assegnamenti in Ruby sono effettivamente eseguiti in parallelo, così i
valori assegnati non sono influenzabili dall' applicazione in se. I valori
sul lato destro sono assegnati nell'ordine in cui appaiono prima che
qualsiasi assegnamento venga eseguito sulla variabile o sugli attributi sul
lato sinistro. Un qualche tipo di esempio escogitato lo illustra. La seconda
linea assegna alle variabili a
, b
, e c
rispettivamente il valore dell' espressione x
,
x+=1
, e x+=1
.
x = 0 |
» | 0 |
a, b, c = x, (x += 1), (x += 1) |
» | [0, 1, 2] |
Quando un assegnamento ha più di un lvalue, l' espressione di assegnamento
restituisce un array dei rvalues. Se un assegnamento contenesse più valori
rvalues rispetto ai lvalues, i valori eccessivi di lvalues
verrebbero settati a nil
. Se un assegnamento multiplo
contenesse, invece, più valori rvalues rispetto ai lvalues,
i valori in esubero di rvalues verrebbero ignorati.
A partire dalla versione di Ruby 1.6.2 se un assegnamento avesse un solo
valore lvalue e multipli rvalues,
quest'ultimi saranno convertiti in un array ed assegnati ad
lvalue.
Potrete contrarre od espandere gli array utilizzando l' operatore di assegnamento parallelo. Se l' ultimo lvalue fosse preceduto da un asterisco, tutti i rimanenti rvalues verrebbero raggruppati ed assegnati ad lvalue sotto forma di un array. Analogamente, se l' ultimo rvalue fosse un array, potreste prefissarlo con un asterisco, che effettivamente lo espande nei suoi valori costituenti. (Ciò non è necessario se rvalue fosse l'unica cosa sul lato destro --- l' array sarebbe espanso automaticamente)
a = [1, 2, 3, 4] |
|||
b, c = a | » | b == 1, | c == 2 |
b, *c = a | » | b == 1, | c == [2, 3, 4] |
b, c = 99, a | » | b == 99, | c == [1, 2, 3, 4] |
b, *c = 99, a | » | b == 99, | c == [[1, 2, 3, 4]] |
b, c = 99, *a | » | b == 99, | c == 1 |
b, *c = 99, *a | » | b == 99, | c == [1, 2, 3, 4] |
Gli assegnamenti paralleli hanno ancora una caratteristica che vale la pena di menzionare. Il lato sinistro dell' assegnamento potrebbe contenere una lista di valori rinchiusi tra parentesi. Ruby tratta questi termini come se fossero una dichiarazione di assegnazione annidata. Estrae il corrispondente rvalues, assegnandolo ai termini tra parentesi, prima di continuare con un assegnamento di un più alto livello.
b, (c, d), e = 1,2,3,4 | » | b == 1, | c == 2, | d == nil, | e == 3 |
b, (c, d), e = [1,2,3,4] | » | b == 1, | c == 2, | d == nil, | e == 3 |
b, (c, d), e = 1,[2,3],4 | » | b == 1, | c == 2, | d == 3, | e == 4 |
b, (c, d), e = 1,[2,3,4],5 | » | b == 1, | c == 2, | d == 3, | e == 5 |
b, (c,*d), e = 1,[2,3,4],5 | » | b == 1, | c == 2, | d == [3, 4], | e == 5 |
In comune con altri linguaggi, Ruby ha una scorciatoia sintattica:
a=a+2
potrebbe essere scritto a+=2
.
Il secondo form è convertito internamente nel primo. Ciò significa che l' operatore che avete definito come metodo nella propria classe lavora come vi sareste aspettati.
class Bowdlerize |
||
def initialize(aString) |
||
@value = aString.gsub(/[aeiou]/, '*') |
||
end |
||
def +(other) |
||
Bowdlerize.new(self.to_s + other.to_s) |
||
end |
||
def to_s |
||
@value |
||
end |
||
end |
||
|
||
a = Bowdlerize.new("damn ") |
» | d*mn |
a += "shame" |
» | d*mn sh*m* |
Ruby dispone di numerosi meccanismi differenti per esecuzioni condizionali di codice; la maggior parte dovrebbe risultare familiare, e molti avere dei contorcimenti chiari. Prima di innoltrarci nell'argomento, penso, dovremo passare un pò di tempo osservando le espressioni booleane.
Ruby ha una semplice definizione di verità. Qualsiasi valore che non sia
nil
o la costante false
è vero. Scoprirete che le
librerie sulle routine lo utilizzano costantemente. Per esempio, IO#gets
, che restituisce
la prossima linea di un file, restituisce nil
alla fine del
file, autorizzandovi a scrivere dei ciclo simili al seguente:
while line = gets # process line end |
Comunque, esiste una trappola per i programmatori del C, C++ e Perl. Il numero zero non è interpretato come un valore falso. Nulla è una stringa di zero-lunghezza. Ciò può essere un'abitudine dura da interrompere.
Ruby supporta tutti gli operatori booleani standard e definisce il nuovo
operatore defined?
.
Sia ``and
'' che ``&&
'' danno il valore
vero solamente se entrambi gli operandi sono veri. Viene valutato il secondo
operatore solamente se il primo è vero (questa caratteristica è conosciuta
talvolta come valutazione da circuito breve). L' unica differenza tra le due
forme è la precedenza ( ``and
'' lega a un livello più basso
rispetto a ``&&
'').
Similmente, sia ``or
'' che ``||
'' restituiscono
una valutazione a vero se uno o l' altro degli operandi è vero. Valutano il
secondo operando solamente se il primo è falso. Come con
``and
'', l' unica differenza tra ``or
'' e
``||
'' è la loro precedenza.
Giusto per rendere la vita interessante, ``and
'' e
``or
'' hanno la stessa precedenza, mentre
``&&
'' ha un più alto grado di precedenza rispetto a
``||
''.
``not
'' e ``!
'' restituiscono l'opposto del
proprio operando (falso se l' operando è vero, e vero se l' operando è
falso). E, naturalmente, ``not
'' e ``!
''
differiscono solamente sulla precedenza.
Tutte queste norme sono riassunte nella tavola 18.4 a pagina 221.
L'operatore defined?
restituisce il valore nil
se il suo argomento (che potrebbe essere un'espressione arbitraria) non sia
definito, altrimenti restituisce una descrizione di quel argomento. Se l'
argomento fosse yield
, defined?
restituirebbe la
stringa ``yield'' se un blocco di codice fosse associato con il contesto
corrente.
defined? 1 |
» | "expression" |
defined? dummy |
» | nil |
defined? printf |
» | "method" |
defined? String |
» | "constant" |
defined? $& |
» | "$&" |
defined? $_ |
» | "global-variable" |
defined? Math::PI |
» | "constant" |
defined? ( c,d = 1,2 ) |
» | "assignment" |
defined? 42.abs |
» | "method" |
In aggiunta agli operatori booleani, gli oggetti in Ruby supportano la
comparazione fruendo dei metodi ==
, ===
,
<=>
, =~
, eql?
, ed
equal?
(osservate la tabella 7.1 a pagina 81). Tutti ad eccetto
di <=>
sono definiti nella classe Object
ma
sono spesso ignorati dai discendenti per fornire appropriate semantiche. Per
esempio, la classe Array
ridefinisce ==
cosìche due
oggetti array sono uguali se disponessero dello stesso numero di elementi e i
corrispondenti elementi fossero a loro volta uguali.
Comuni operatori di confronto
|
Sia ==
che =~
hanno le forme negative,
!=
e !~
. Comunque, queste sono convertite da Ruby
quando il vostro programma viene letto. a!=b
è equivalente a
!(a==b)
, e a!~b
è lo stesso di
!(a=~b)
. Ciò significa che se scrivete una classe che ignori
==
o =~
fareste lavorare !=
e
!~
per nulla. Ma d'altro canto, ciò significa che non potete
definire !=
e !~
indipendentemente da
==
e =~
, rispettivamente.
Potrete usare i range di Ruby come un'espressione booleana. Un range come
exp1..exp2
sarà valutato come falsa finchè exp1
diventa vera. Il range verrà poi valutato come vero finchè exp2
diventa vera. Una volta che ciò avvenga, il range si resetta, pronto per un
altro ciclo. Mostreremo alcuni esempi a pagina 84.
Per ultimo, potrete usare una semplice espressione regolare come un'
espressione booleana. Ruby lo espande in $_=~/re/
.
In Ruby un' espressione if
è assai simile alla dichiarazione
``if'' di altri linguaggi.
if aSong.artist == "Gillespie" then handle = "Dizzy" elsif aSong.artist == "Parker" then handle = "Bird" else handle = "unknown" end |
Se sistemate la vostra asserzione if
su diverse linee,
potrete non impiegare la parola chiave then
.
if aSong.artist == "Gillespie" handle = "Dizzy" elsif aSong.artist == "Parker" handle = "Bird" else handle = "unknown" end |
Comunque, se sistemate il codice in modo serrato, la parola
then
si rende necessaria per separare un'espressione booleana
dall'asserzione seguente.
if aSong.artist == "Gillespie" then handle = "Dizzy" elsif aSong.artist == "Parker" then handle = "Bird" else handle = "unknown" end |
Potrete avere nessuna o molte proposizioni elsif
e un
opzionale else
.
Come detto precedentemente, if
è un'espressione, non una
asserzione --- restituisce una valore. Non dovete usare il valore di
un'espressione if
, ma può tornare utile.
handle = if aSong.artist == "Gillespie" then "Dizzy" elsif aSong.artist == "Parker" then "Bird" else "unknown" end |
Ruby utilizza anche forme negative di un'asserzione if
:
unless aSong.duration > 180 then cost = .25 else cost = .35 end |
Finalmente, per i fan del C, Ruby supporta anche le espressioni condizionali in style C:
cost = aSong.duration > 180 ? .35 : .25 |
Un' espressione condizionale restituisce il valore di una delle due
espressioni, prima o dopo i due punti, a seconda che l'espressione booleana
valuti prima o dopo il punto interrogativo il valore true
o
false
. In questo caso, se la durata della canzone fosse
superiore ai tre minuti, l'espressione restituirebbe .35. Per canzoni più
corte, il valore sarà .25. Qualsiasi risultato sarà assegnato a
cost
.
Ruby condivide con il Perl una semplice caratteristica. I modificatori di dichiarazioni vi permettono di aggiungere una dichiarazione condizionale alla fine di una normale dichiarazione.
mydate = "01-01-60" |
Per un modificatore if
, l' espressione che precede sarà
valutata solamente se la condizione risulti vera. unless
lavora
in modo contrario.
while gets next if /^#/ # Salta i commenti nel file parseLine unless /^$/ # non controlla le linee vuote end |
Poichè if
è esso stesso un'espressione, potreste renderla
davvero incomprensibile con dichiarazioni come la seguente:
if artist == "John Coltrane" artist = "'Trane" end unless nicknames == "no" |
Questo strada porta ai cancelli della follia.
.
L'espressione case
in Ruby è una bestia potente: un
if
poliedrico pompato di steroidi.
case inputLine when "debug" dumpDebugInfo dumpSymbols when /p\s+(\w+)/ dumpVariable($1) when "quit", "exit" exit else print "Illegal command: #{inputLine}" end |
Come con if
, case
restituisce il valore
dell'ultima espressione eseguita, e potreste anche aver bisogno delle parola
then
se l' espressione fosse sulla stessa linea della
condizione.
kind = case year when 1850..1889 then "Blues" when 1890..1909 then "Ragtime" when 1910..1929 then "New Orleans Jazz" when 1930..1939 then "Swing" when 1940..1950 then "Bebop" else "Jazz" end |
case
opera comparando il target ( l'espressione dopo la
parola case
) con ognuna delle espressioni di comparazione dopo
le parole when
. Questo test è stato fatto usando
comparison ===
target. Sempre che
una classe definisca l'eloquente semantica per ===
(e tutte le
classi incorporate lo fanno) gli oggetti di quella classe potranno essere
usati nelle espressioni case.
Per esempio, le espressioni regolari definiscono ===
con un
semplice modello di confronto.
case line when /title=(.*)/ puts "Title is #$1" when /track=(.*)/ puts "Track is #$1" when /artist=(.*)/ puts "Artist is #$1" end |
Le classi in Ruby sono istanze della classe Class
, la quale
definisce ===
come un test per vedere se l'argomento sia
un'istanza della classe o una delle sue superclassi. Così (abbandonando i
benefici del polimorfismo e passando a quelli del refactoring afflizione per
le vostre orecchie), potrete testare la classe degli oggetti:
case shape when Square, Rectangle # ... when Circle # ... when Triangle # ... else # ... end |
Non ditelo a nessuno, ma Ruby risponde di un primitivo costrutto di looping.
Il ciclo while
esegue le proprie istruzioni nessuna o più
volte finchè la sua condizione risulti vera. Per esempio, questo comune
idioma legge finchè l'input risulti svuotato.
while gets # ... end |
Esiste anche una forma negativa che esegue il corpo fino a che la condizione diventa vera.
until playList.duration > 60 playList.add(songList.pop) end |
Come con if
e unless
, entrambi i ciclo possono
essere utilizzati come modificatori di dichiarazioni.
a *= 2 while a < 100 a -= 10 until a < 100 |
A pagina 80 nella sezione delle espressione booleane, abbiamo detto che un range può essere usato come una specie di flip-flop, restituendo vero quando certi avvenimenti accadono e poi rimanere veri fino a chè il secondo evento avvenga. Questo accade normalmente con i ciclo. Nell' esempio che segue, leggiamo un file di testo contenente i primi dieci numeri ordinali (primo, secondo, terzo, etc) ma stampiamo solamente le linee che iniziano con ``terzo'' e finiscono con ``quinto'''
file = File.open("ordinal") while file.gets print if /third/ .. /fifth/ end |
produce:
third fourth fifth |
Gli elementi di un range usati nelle espressioni booleane possono essi
stessi essere un espressione. Saranno valutate ogni volta che l'espressione
booleana nella sua globalità verrà valutata. Per esempio, il codice seguente
usa il fatto che la variabile $.
contenga il corrente input di
numero di linea per mostrare i numeri di linea da uno a tre e quelli entro un
confronto di /eig/
e /nin/
.
file = File.open("ordinal") while file.gets print if ($. == 1) || /eig/ .. ($. == 3) || /nin/ end |
produce:
first second third eighth ninth |
C'è un solo consiglio quando while
e until
sono
usati come modificatori di dichiarazioni.Se la dichiarazione che stanno
modificando è un blocco begin
/end
, il codice nel
blocco sarà sempre eseguito almeno una volta, privo di rigurdi per il valore
dell'espressione booleana.
print "Hello\n" while false begin print "Goodbye\n" end while false |
produce:
Goodbye |
Se leggete l'inizio della sezione precedente, potreste essere scoraggiati. Diceva, ``Ruby disponde di un primitivo costrutto di looping''. Non disperate, gentili lettori, ci sono buone nuove. Ruby non ha bisogno di alcun sofisticato costrutto di looping, poichè tutte le cose strane si possono implementare usando gli iteratori.
Per esempio, Ruby non dispone di un ciclo ``for'' --- non almeno del tipo che trovereste nel C, C++ e Java. Invece, Ruby usa i metodi definiti in svariate classi pre costruite per fornire funzionalità equivalenti, ma meno soggetti agli errori.
Osserviamo alcuni esempi
.
3.times do print "Ho! " end |
produce:
Ho! Ho! Ho! |
E' facile evitare errori del tipo fencepost e off-by-1; questo ciclo verrà
eseguito tre volte, punto e basta. In aggiunta a times
, gli
interi possono effettuare i ciclo all' interno di valori specificati
semplicemente chiamando downto
, upto
, e
step
. Per esempio, un ciclo tradizionale ``for'' che viene
eseguito da 0 a 9 (qualcosa come i=0; i < 10; i++
) è scritto
nel modo seguente.
0.upto(9) do |x| print x, " " end |
produce:
0 1 2 3 4 5 6 7 8 9 |
Un ciclo da 0 a 12 con un passo 3 con il seguente esempio.
0.step(12, 3) {|x| print x, " " } |
produce:
0 3 6 9 12 |
Similmente, l'esecuzione di cicli su insiemi di dati od altri contenitori
risulta semplice usando il loro metodo each
.
[ 1, 1, 2, 3, 5 ].each {|val| print val, " " } |
produce:
1 1 2 3 5 |
E una volta che una classe supporti each
, i metodi
addizionali nel modulo Enumerable
(documentato all'inizio di
pagina 407 e sommarizzato nelle pagine 104--105) diventano disponibili. Per
esempio, la classe File
fornisce un metodo each
,
la quale restituisce ogni linea di un file a rotazione. Usando il metodo
grep
in Enumerable
, potrete iterare solamente sopra
quelle righe che soddisfano una certa condizione.
File.open("ordinal").grep /d$/ do |line| print line end |
produce:
second third |
Ultimo, e probabilmente senza importanza, è il ciclo più di base di tutti.
Ruby fornisce un iteratore chiamato ciclo
.
loop { # block ... } |
L' iteratore ciclo
chiama il blocco associato per sempre (o
almeno finchè non lo bloccate, ma dovrete leggere fino in fondo per sapere in
che modo farlo).
Precedentemente abbiamo detto che gli unici cicli primitivi di ciclo erano
while
e until
. Che cos' è questa cosa
``for
'', allora? Bè, for
è all'incirca una zoletta
di zucchero sintattico. Quando scrivete
for aSong in songList aSong.play end |
Ruby lo traduce in qualcosa di simile
songList.each do |aSong| aSong.play end |
L' unica differenza tra il ciclo for
e la forma
each
è lo scopo delle variabili locali che sono definite nel
corpo. Ciò e discusso a pagina 89.
Potrete usare for
per iterare sopra qualsiasi oggetto che
risponda al metodo each
, esattamente come un Array
o un Range
.
for i in ['fee', 'fi', 'fo', 'fum'] print i, " " end for i in 1..3 print i, " " end for i in File.open("ordinal").find_all { |l| l =~ /d$/} print i.chomp, " " end |
produce:
fee fi fo fum 1 2 3 second third |
Finchè la vostra classe definisce un metodo sensibile each
,
potrete usare un ciclo for
per esaminarlo.
class Periods def each yield "Classical" yield "Jazz" yield "Rock" end end periods = Periods.new for genre in periods print genre, " " end |
produce:
Classical Jazz Rock |
I controlli di ciclo break
, redo
, e
next
vi permettono di alterare il normale flusso attraverso un
ciclo o un iteratore.
break
termina immediatamente il ciclo ancora aperto; il
controllo ritorna alla dichiarazione che segue il blocco. redo
ripete il ciclo dall'inizio, ma senza rivalutare la condizione o andando a
prendere il prossimo elemento (in un iteratore). next
salta alla
fine del ciclo, partendo dalla sucessiva iterazione.
while gets next if /^\s*#/ # salta i commenti break if /^END/ # stop at end # substitute stuff in backticks and try again redo if gsub!(/`(.*?)`/) { eval($1) } # process line ... end |
Queste parole chiave possono anche essere usate con qualsiasi degli iteratori base nei meccanismi di looping:
i=0 loop do i += 1 next if i < 3 print i break if i > 4 end |
produce:
345 |
L'affermazione redo
crea un ciclo per ripetere l'iterazione
corrente. Talvolta, penso, avreste bisogno di far scorrere il ciclo
all'indietro, sino all'inizio. La dichiarazione retry
è quello
che vi serve. retry
ricomincia qualsiasi tipo di iteratore di
ciclo.
for i in 1..100 print "Now at #{i}. Restart? " retry if gets =~ /^y/i end |
Lanciandolo in modo iterattivo potreste ottenere
Now at 1. Restart? n Now at 2. Restart? y Now at 1. Restart? n . . . |
retry
rivaluterà qualsiasi argomento all'iteratore prima di
ricominciare . La documentazione online fornisce il seguente esempio di un
ciclo until .
def doUntil(cond) yield retry unless cond end i = 0 doUntil(i > 3) { print i, " " i += 1 } |
produce:
0 1 2 3 4 |
I ciclo while
, until
, e for
sono
già incorporati nel linguaggio e non introducono nuove sfere d' azione;
quelli locali preesistenti possono essere usati nel ciclo, e qualsiasi nuovo
ciclo locale creato sarà disponibile successivamente.
Il blocco usato dall'iteratore (alla stregua di loop
e
each
) sono un poco differenti. Normalmente, la variabile locale
creata in questi blocchi non risulta accessibile all'esterno del blocco
stesso.
[ 1, 2, 3 ].each do |x| y = x + 1 end [ x, y ] |
produce:
prog.rb:4: undefined local variable or method `x' |
Comunque, se al momento il blocco esegue una variabile locale già esistente con lo stesso nome d quella della variabile nel blocco, la variabile locale esistente potrà essere utilizzata nel blocco. I suoi valori quindi saranno disponibili successivamente alla fine del blocco. Come il seguente esempio mostra, questo si applica sia alle variabili locali nel blocco come ai parametri del blocco stesso.
x = nil |
||
y = nil |
||
[ 1, 2, 3 ].each do |x| |
||
y = x + 1 |
||
end |
||
[ x, y ] |
» | [3, 4] |
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.xml:space="default"