Ruby Language
Flusso di controllo
Ricerca…
se, elsif, else e end
Ruby offre le espressioni if
e else
previste per la logica di branching, terminate dalla parola chiave end
:
# Simulate flipping a coin
result = [:heads, :tails].sample
if result == :heads
puts 'The coin-toss came up "heads"'
else
puts 'The coin-toss came up "tails"'
end
In Ruby, le istruzioni if
sono espressioni che valutano un valore e il risultato può essere assegnato a una variabile:
status = if age < 18
:minor
else
:adult
end
Ruby offre anche operatori ternari in stile C ( vedi qui per i dettagli ) che possono essere espressi come:
some_statement ? if_true : if_false
Ciò significa che l'esempio precedente che usa if-else può anche essere scritto come
status = age < 18 ? :minor : :adult
Inoltre, Ruby offre la parola chiave elsif
che accetta un'espressione per abilitare la logica di ramificazione aggiuntiva:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Se nessuna delle condizioni in un if
/ elsif
catena sono vere, e non c'è nessun else
la clausola, quindi l'espressione restituisce a zero. Questo può essere utile all'interno dell'interpolazione delle stringhe, poiché nil.to_s
è la stringa vuota:
"user#{'s' if @users.size != 1}"
Valori di verità e Falsy
In Ruby, ci sono esattamente due valori che sono considerati "falsi" e restituiscono false quando testati come condizione per un'espressione if
. Loro sono:
-
nil
- booleano
false
Tutti gli altri valori sono considerati "veritieri", tra cui:
-
0
- zero numerico (intero o altro) -
""
- Stringhe vuote -
"\n"
- Stringhe contenenti solo spazi bianchi -
[]
- Array vuoti -
{}
- hash vuoti
Prendi, ad esempio, il seguente codice:
def check_truthy(var_name, var)
is_truthy = var ? "truthy" : "falsy"
puts "#{var_name} is #{is_truthy}"
end
check_truthy("false", false)
check_truthy("nil", nil)
check_truthy("0", 0)
check_truthy("empty string", "")
check_truthy("\\n", "\n")
check_truthy("empty array", [])
check_truthy("empty hash", {})
Produrrà:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
mentre, fino a
Un ciclo while
esegue il blocco mentre viene soddisfatta la condizione data:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
Un ciclo until
esegue il blocco mentre il condizionale è falso:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
In linea se / a meno
Un modello comune consiste nell'utilizzare un inline o trailing, if
o unless
:
puts "x is less than 5" if x < 5
Questo è noto come modificatore condizionale ed è un modo pratico per aggiungere codice di protezione semplice e ritorni anticipati:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
Non è possibile aggiungere una clausola else
a questi modificatori. Inoltre, in genere non è raccomandato l'uso di modificatori condizionali all'interno della logica principale - Per i codici complessi si dovrebbe usare normalmente if
, elsif
, else
.
salvo che
Una dichiarazione comune è if !(some condition)
. Ruby offre l'alternativa della dichiarazione a unless
che.
La struttura è esattamente la stessa di un'istruzione if
, eccetto che la condizione è negativa. Inoltre, l'istruzione a unless
che non supporta elsif
, ma supporta else
:
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Caso clinico
Ruby usa la parola chiave case
per le istruzioni switch.
Come da Ruby Docs :
Le affermazioni di caso sono costituite da una condizione facoltativa, che è nella posizione di un argomento a
case
e da zero o più clausolewhen
. La prima clausolawhen
corrisponde alla condizione (o per valutare la verità booleana, se la condizione è nulla) "vince" e la sua stanza di codice viene eseguita. Il valore dell'istruzione case è il valore della clausolawhen
successful, onil
se non esiste tale clausola.Una dichiarazione di un caso può terminare con
else
clausola. Ciascunowhen
un'istruzione può avere più valori candidati, separati da virgole.
Esempio:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Versione più breve:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
Il valore della clausola case
viene confrontato con ciascuna clausola when
utilizzando il metodo ===
(non ==
). Pertanto può essere utilizzato con una varietà di diversi tipi di oggetti.
È possibile utilizzare un'istruzione case
con gli intervalli :
case 17
when 13..19
puts "teenager"
end
Una dichiarazione di un case
può essere utilizzata con un Regexp :
case "google"
when /oo/
puts "word contains oo"
end
Una dichiarazione del case
può essere utilizzata con un Proc o lambda:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
È possibile utilizzare un'istruzione case
con Classi :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
Implementando il metodo ===
puoi creare le tue classi di corrispondenza:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
È possibile utilizzare un'istruzione case
senza un valore con cui confrontare:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Un'istruzione case
ha un valore, quindi puoi usarla come argomento metodo o in un compito:
description = case 16
when 13..19 then "teenager"
else ""
end
Controllo del ciclo con interruzione, successivo e ripetizione
Il flusso di esecuzione di un blocco Ruby può essere controllato con le istruzioni break
, next
e redo
.
break
L'istruzione break
uscirà immediatamente dal blocco. Qualsiasi istruzione rimanente nel blocco verrà saltata e l'iterazione terminerà:
actions = %w(run jump swim exit macarena)
index = 0
while index < actions.length
action = actions[index]
break if action == "exit"
index += 1
puts "Currently doing this action: #{action}"
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
next
La next
istruzione tornerà immediatamente all'inizio del blocco e procederà con la successiva iterazione. Qualsiasi istruzione rimanente nel blocco verrà saltata:
actions = %w(run jump swim rest macarena)
index = 0
while index < actions.length
action = actions[index]
index += 1
next if action == "rest"
puts "Currently doing this action: #{action}"
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
# Currently doing this action: macarena
redo
L'istruzione redo
ritorna immediatamente all'inizio del blocco e riprova la stessa iterazione. Qualsiasi istruzione rimanente nel blocco verrà saltata:
actions = %w(run jump swim sleep macarena)
index = 0
repeat_count = 0
while index < actions.length
action = actions[index]
puts "Currently doing this action: #{action}"
if action == "sleep"
repeat_count += 1
redo if repeat_count < 3
end
index += 1
end
# Currently doing this action: run
# Currently doing this action: jump
# Currently doing this action: swim
# Currently doing this action: sleep
# Currently doing this action: sleep
# Currently doing this action: sleep
# Currently doing this action: macarena
Enumerable
iterativa
Oltre ai cicli, queste istruzioni funzionano con i metodi di iterazione Enumerable, come each
e map
:
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Blocca i valori dei risultati
In entrambe le istruzioni break
e next
, può essere fornito un valore, che verrà utilizzato come valore del risultato del blocco:
even_value = for value in [1, 2, 3]
break value if value.even?
end
puts "The first even value is: #{even_value}"
# The first even value is: 2
buttare, prendere
A differenza di molti altri linguaggi di programmazione, le parole chiave throw
and catch
non sono correlate alla gestione delle eccezioni in Ruby.
In Ruby, throw
e catch
atti un po 'come etichette in altre lingue. Sono usati per cambiare il flusso di controllo, ma non sono correlati a un concetto di "errore" come sono le eccezioni.
catch(:out) do
catch(:nested) do
puts "nested"
end
puts "before"
throw :out
puts "will not be executed"
end
puts "after"
# prints "nested", "before", "after"
Controllo del flusso con istruzioni logiche
Anche se può sembrare controintuitivo, è possibile utilizzare gli operatori logici per determinare se viene eseguita o meno un'istruzione. Per esempio:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Questo controllerà se il file esiste e stamperà il messaggio di errore solo se non lo è. L'istruzione or
è pigro, il che significa che smetterà di essere eseguito una volta che è sicuro se il suo valore sia vero o falso. Non appena viene scoperto che il primo termine è vero, non è necessario verificare il valore dell'altro termine. Ma se il primo termine è falso, deve controllare il secondo termine.
Un uso comune è impostare un valore predefinito:
glass = glass or 'full' # Optimist!
Questo imposta il valore del glass
su "pieno" se non è già impostato. Più concisamente, è possibile utilizzare la versione simbolica di or
:
glass ||= 'empty' # Pessimist.
È anche possibile eseguire la seconda istruzione solo se la prima è falsa:
File.exist?(filename) and puts "#{filename} found!"
Di nuovo, and
è pigro, quindi eseguirà la seconda istruzione solo se necessario per arrivare a un valore.
L'operatore or
ha una precedenza inferiore rispetto a and
. Allo stesso modo, ||
ha precedenza più bassa di &&
. Le forme simbolo hanno una precedenza superiore rispetto alle forme di parole. Questo è utile sapere quando si desidera mescolare questa tecnica con l'assegnazione:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Si noti che la Guida allo stile di Ruby consiglia :
E
and
or
parole chiave sono bannati. La minima leggibilità aggiunta non merita l'alta probabilità di introdurre bug sottili. Per le espressioni booleane, usa sempre&&
e||
anziché. Per il controllo del flusso, utilizzareif
e aunless
;&&
e||
sono anche accettabili ma meno chiari.
inizio, fine
L' begin
blocco è una struttura di controllo che raggruppa più istruzioni.
begin
a = 7
b = 6
a * b
end
A begin
blocco restituirà il valore dell'ultima istruzione nel blocco. Il seguente esempio restituirà 3
.
begin
1
2
3
end
L' begin
blocco è utile per l'assegnazione condizionale usando l' ||=
gestore, nella quale possono essere necessarie più istruzioni per restituire un risultato.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
Può anche essere combinato con altre strutture di blocco come il rescue
, ensure
, while
, if
, unless
, ecc. Per fornire un maggiore controllo del flusso del programma.
Begin
blocchi Begin
non sono blocchi di codice, come { ... }
o do ... end
; non possono essere passati alle funzioni.
return vs. next: ritorno non locale in un blocco
Considera questo frammento spezzato :
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
Ci si potrebbe aspettare che il return
restituisca un valore per la matrice di risultati di blocco della map
. Quindi il valore di ritorno di foo
sarebbe [1, 0, 3, 0]
. Invece, return
restituisce un valore dal metodo foo
. Si noti che baz
non viene stampato, il che significa che l'esecuzione non ha mai raggiunto quella linea.
next
con un valore fa il trucco. Funziona come un return
livello di blocco.
def foo
bar = [1, 2, 3, 4].map do |x|
next 0 if x.even?
x
end
puts 'baz'
bar
end
foo # baz
# => [1, 0, 3, 0]
In assenza di un return
, il valore restituito dal blocco è il valore della sua ultima espressione.
Operatore di assegnazione Or-Ugual / Conditional (|| =)
Ruby ha un operatore or-equal che consente di assegnare un valore a una variabile se e solo se tale variabile valuta o nil
o false
.
||= # this is the operator that achieves this.
questo operatore con i doppi tubi che rappresentano o e il segno di uguale che rappresenta l'assegnazione di un valore. Potresti pensare che rappresenti qualcosa del genere:
x = x || y
questo esempio sopra non è corretto. L'operatore or-equal rappresenta effettivamente questo:
x || x = y
Se x
restituisce un valore nil
o false
x
viene assegnato il valore di y
e lasciato invariato altrimenti.
Ecco un caso d'uso pratico dell'operatore or-equal. Immagina di avere una parte del codice che ci si aspetta che invii un messaggio di posta elettronica a un utente. Cosa fai se per qualsiasi motivo non ci sono e-mail per questo utente. Potresti scrivere qualcosa come questo:
if user_email.nil?
user_email = "[email protected]"
end
Usando l'operatore or-equal possiamo tagliare l'intera porzione di codice, fornendo un controllo e una funzionalità chiari e chiari.
user_email ||= "[email protected]"
Nei casi in cui il false
è un valore valido, è necessario fare attenzione a non ignorarlo accidentalmente:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Operatore ternario
Ruby ha un operatore ternario ( ?:
:), Che restituisce uno dei due valori in base a se una condizione viene valutata come verità:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
è lo stesso che scrivere if a then b else c end
, anche se il ternario è preferito
Esempi:
puts (if 1 then 2 else 3 end) # => 2
puts 1 ? 2 : 3 # => 2
x = if 1 then 2 else 3 end
puts x # => 2
Operatore Flip-Flop
L'operatore flip flop ..
viene utilizzato tra due condizioni in un'istruzione condizionale:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
La condizione diventa false
fino a quando la prima parte diventa true
. Quindi valuta true
finché la seconda parte diventa true
. Dopodiché passa nuovamente a false
.
Questo esempio illustra cosa viene selezionato:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
L'operatore flip-flop funziona solo all'interno di ifs (incluso a unless
) e dell'operatore ternario. Altrimenti viene considerato come l'operatore di intervallo.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Può passare da false
a true
e viceversa più volte:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]