Ruby Language
Flux de contrôle
Recherche…
si, elsif, sinon et fin
Ruby propose les expressions if
et else
attendues pour la logique de branchement, terminées par le mot clé 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
Dans Ruby, les instructions if
sont des expressions dont l'évaluation donne une valeur et le résultat peut être affecté à une variable:
status = if age < 18
:minor
else
:adult
end
Ruby propose également des opérateurs ternaires de style C ( voir ici pour plus de détails ) pouvant être exprimés comme suit:
some_statement ? if_true : if_false
Cela signifie que l'exemple ci-dessus utilisant if-else peut aussi être écrit comme
status = age < 18 ? :minor : :adult
En outre, Ruby propose le mot-clé elsif
qui accepte une expression pour activer une logique de branchement supplémentaire:
label = if shirt_size == :s
'small'
elsif shirt_size == :m
'medium'
elsif shirt_size == :l
'large'
else
'unknown size'
end
Si aucune des conditions d'une chaîne if
/ elsif
n'est vraie et qu'il n'y a pas de clause else
, l'expression est nulle. Cela peut être utile dans l'interpolation de chaînes, car nil.to_s
est la chaîne vide:
"user#{'s' if @users.size != 1}"
Valeurs véridiques et fausses
Dans Ruby, il y a exactement deux valeurs qui sont considérées comme "falsy", et qui retourneront false si elles sont testées comme condition pour une expression if
. Elles sont:
-
nil
- booléen
false
Toutes les autres valeurs sont considérées comme "véridiques", notamment:
-
0
- zéro numérique (entier ou autre) -
""
- Chaînes vides -
"\n"
- Chaînes ne contenant que des espaces -
[]
- Tableaux vides -
{}
- Les hashes vides
Prenez, par exemple, le code suivant:
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", {})
Va sortir:
false is falsy
nil is falsy
0 is truthy
empty string is truthy
\n is truthy
empty array is truthy
empty hash is truthy
tandis que, jusqu'à
A while
la boucle exécute le bloc tandis que la condition donnée est satisfaite:
i = 0
while i < 5
puts "Iteration ##{i}"
i +=1
end
Un until
la boucle exécute le bloc alors que le conditionnel est faux:
i = 0
until i == 5
puts "Iteration ##{i}"
i +=1
end
Inline si / sauf
Un schéma courant consiste à utiliser un inline ou à la fin if
ou à unless
:
puts "x is less than 5" if x < 5
Ceci est connu comme un modificateur conditionnel, et est un moyen pratique d'ajouter un code de garde simple et des retours anticipés:
def save_to_file(data, filename)
raise "no filename given" if filename.empty?
return false unless data.valid?
File.write(filename, data)
end
Il n'est pas possible d'ajouter une clause else
à ces modificateurs. En outre , il est généralement recommandé de ne pas utiliser les modificateurs conditionnels dans la logique principale - Pour le code complexe on devrait utiliser la normale if
, elsif
, d' else
à la place.
sauf si
Une déclaration commune est if !(some condition)
. Ruby offre l'alternative de la déclaration unless
.
La structure est exactement la même qu'une instruction if
, sauf que la condition est négative. En outre, la déclaration unless
ne prend pas en charge elsif
, mais prend en charge else
:
# Prints not inclusive
unless 'hellow'.include?('all')
puts 'not inclusive'
end
Déclaration de cas
Ruby utilise le mot-clé case
pour les instructions switch.
Selon les Ruby Docs :
Les instructions de cas sont constituées d'une condition facultative, qui se trouve dans la position d'un argument par rapport à la
case
, et de zéro ou plusieurswhen
clauses. La première clausewhen
qui correspond à la condition (ou à l'évaluation de la vérité booléenne, si la condition est nulle) «gagne» et sa strophe de code est exécutée. La valeur de l'instruction case est la valeur de la clausewhen
, ounil
s'il n'y en a pas.Une instruction de cas peut se terminer par une clause
else
. Chaquewhen
qu'une instruction peut avoir plusieurs valeurs candidates, séparées par des virgules.
Exemple:
case x
when 1,2,3
puts "1, 2, or 3"
when 10
puts "10"
else
puts "Some other number"
end
Version plus courte:
case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end
La valeur de la clause case
correspond à chaque clause when
à l'aide de la méthode ===
(non ==
). Par conséquent, il peut être utilisé avec différents types d'objets.
Une déclaration de case
peut être utilisée avec des plages :
case 17
when 13..19
puts "teenager"
end
Une déclaration de case
peut être utilisée avec une expression rationnelle :
case "google"
when /oo/
puts "word contains oo"
end
Une instruction de case
peut être utilisée avec un Proc ou un lambda:
case 44
when -> (n) { n.even? or n < 0 }
puts "even or less than zero"
end
Une déclaration de case
peut être utilisée avec les classes :
case x
when Integer
puts "It's an integer"
when String
puts "It's a string"
end
En implémentant la méthode ===
, vous pouvez créer vos propres classes de correspondance:
class Empty
def self.===(object)
!object or "" == object
end
end
case ""
when Empty
puts "name was empty"
else
puts "name is not empty"
end
Une instruction de case
peut être utilisée sans valeur à comparer:
case
when ENV['A'] == 'Y'
puts 'A'
when ENV['B'] == 'Y'
puts 'B'
else
puts 'Neither A nor B'
end
Une instruction de case
a une valeur, vous pouvez donc l'utiliser comme argument de méthode ou dans une affectation:
description = case 16
when 13..19 then "teenager"
else ""
end
Contrôle de boucle avec break, next et redo
Le flux d'exécution d'un bloc Ruby peut être contrôlé avec les instructions break
, next
et redo
.
break
La déclaration de break
sortira immédiatement du bloc. Toutes les instructions restantes dans le bloc seront ignorées et l'itération se terminera comme suit:
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
L'instruction next
retournera immédiatement en haut du bloc et poursuivra l'itération suivante. Toutes les instructions restantes dans le bloc seront ignorées:
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'instruction redo
retourne immédiatement en haut du bloc et réessaie la même itération. Toutes les instructions restantes dans le bloc seront ignorées:
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
Outre les boucles, ces instructions fonctionnent avec les méthodes d'itération Enumerable, telles que each
et la map
:
[1, 2, 3].each do |item|
next if item.even?
puts "Item: #{item}"
end
# Item: 1
# Item: 3
Bloquer les valeurs de résultat
Dans les instructions break
et next
, une valeur peut être fournie et sera utilisée comme valeur de résultat de bloc:
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
jeter, attraper
Contrairement à de nombreux autres langages de programmation, les mots clés throw
et catch
ne sont pas liés à la gestion des exceptions dans Ruby.
Dans Ruby, throw
et catch
agissent un peu comme des étiquettes dans d'autres langues. Ils sont utilisés pour modifier le flux de contrôle, mais ne sont pas liés à un concept "d'erreur" comme les exceptions.
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"
Contrôle du flux avec des instructions logiques
Bien que cela puisse sembler contre-intuitif, vous pouvez utiliser des opérateurs logiques pour déterminer si une instruction est exécutée ou non. Par exemple:
File.exist?(filename) or STDERR.puts "#{filename} does not exist!"
Cela vérifie si le fichier existe et n'imprime le message d'erreur que si ce n'est pas le cas. L'instruction or
est paresseuse, ce qui signifie qu'elle cessera de s'exécuter une fois qu'il sera certain que sa valeur est vraie ou fausse. Dès que le premier terme est trouvé vrai, il n'est pas nécessaire de vérifier la valeur de l'autre terme. Mais si le premier terme est faux, il doit vérifier le second terme.
Une utilisation courante consiste à définir une valeur par défaut:
glass = glass or 'full' # Optimist!
Cela définit la valeur du glass
à «plein» si ce n'est pas déjà fait. Plus précisément, vous pouvez utiliser la version symbolique de or
:
glass ||= 'empty' # Pessimist.
Il est également possible d'exécuter la deuxième instruction uniquement si la première est fausse:
File.exist?(filename) and puts "#{filename} found!"
Encore une fois, and
est paresseux donc il n'exécutera la deuxième instruction que si nécessaire pour arriver à une valeur.
L'opérateur or
a une priorité inférieure à and
. De même, ||
a une préséance inférieure à &&
. Les formes de symbole ont une priorité plus élevée que les formes de mot. Ceci est pratique pour savoir quand vous voulez mélanger cette technique avec l'affectation:
a = 1 and b = 2
#=> a==1
#=> b==2
a = 1 && b = 2; puts a, b
#=> a==2
#=> b==2
Notez que le Guide de style Ruby recommande :
Les mots clés
and
etor
sont interdits. La lisibilité minimale ajoutée ne vaut tout simplement pas la forte probabilité d'introduire des bogues subtils. Pour les expressions booléennes, utilisez toujours&&
et||
au lieu. Pour le contrôle de flux, utilisezif
etunless
;&&
et||
sont également acceptables mais moins clairs.
commence, fin
Le bloc de begin
est une structure de contrôle regroupant plusieurs instructions.
begin
a = 7
b = 6
a * b
end
Un bloc de begin
renvoie la valeur de la dernière instruction du bloc. L'exemple suivant renverra 3
.
begin
1
2
3
end
Le bloc begin
est utile pour l'affectation conditionnelle à l'aide de l'opérateur ||=
où plusieurs instructions peuvent être nécessaires pour renvoyer un résultat.
circumference ||=
begin
radius = 7
tau = Math::PI * 2
tau * radius
end
Il peut également être combiné avec d'autres structures de bloc telles que le rescue
, ensure
, tant while
, if
, unless
, etc. pour fournir un meilleur contrôle du déroulement du programme.
Begin
blocs de do ... end
ne sont pas des blocs de code, comme { ... }
ou do ... end
; ils ne peuvent pas être transmis aux fonctions.
retour vs suivant: retour non local dans un bloc
Considérez cet extrait cassé :
def foo
bar = [1, 2, 3, 4].map do |x|
return 0 if x.even?
x
end
puts 'baz'
bar
end
foo # => 0
On pourrait s'attendre à ce que le return
donne une valeur pour le tableau de résultats de bloc de map
. Donc, la valeur de retour de foo
serait [1, 0, 3, 0]
. Au lieu de cela, return
renvoie une valeur de la méthode foo
. Notez que baz
n'est pas imprimé, ce qui signifie que l'exécution n'a jamais atteint cette ligne.
next
avec une valeur fait le tour. Il agit comme un return
niveau du bloc.
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]
En l'absence de return
, la valeur renvoyée par le bloc est la valeur de sa dernière expression.
Opérateur d'attribution Or-Equals / Conditional (|| =)
Ruby possède un opérateur or-equals qui permet d'attribuer une valeur à une variable si et seulement si cette variable est nil
ou false
.
||= # this is the operator that achieves this.
cet opérateur avec les doubles tuyaux représentant ou et le signe égal représentant l'attribution d'une valeur. Vous pensez peut-être que cela représente quelque chose comme ceci:
x = x || y
Cet exemple ci-dessus n'est pas correct. L'opérateur or-equals représente en fait ceci:
x || x = y
Si x
évalué à nil
ou à false
x
se voit attribuer la valeur de y
et reste inchangé dans le cas contraire.
Voici un cas d'utilisation pratique de l'opérateur ou-equals. Imaginez que vous ayez une partie de votre code qui devrait envoyer un courrier électronique à un utilisateur. Que faites-vous si pour quelle raison il n'y a pas de courrier électronique pour cet utilisateur? Vous pourriez écrire quelque chose comme ceci:
if user_email.nil?
user_email = "[email protected]"
end
En utilisant l'opérateur ou-equals, nous pouvons couper tout ce morceau de code, fournissant un contrôle et des fonctionnalités propres et clairs.
user_email ||= "[email protected]"
Dans les cas où false
est une valeur valide, il faut veiller à ne pas la remplacer accidentellement:
has_been_run = false
has_been_run ||= true
#=> true
has_been_run = false
has_been_run = true if has_been_run.nil?
#=> false
Opérateur ternaire
Ruby a un opérateur ternaire ( ?:
:), Qui renvoie une valeur sur deux si une condition est évaluée comme vraie:
conditional ? value_if_truthy : value_if_falsy
value = true
value ? "true" : "false"
#=> "true"
value = false
value ? "true" : "false"
#=> "false"
c'est la même chose qu'écrire if a then b else c end
, bien que le ternaire soit préféré
Exemples:
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
Opérateur Flip-Flop
L'opérateur flip flop ..
est utilisé entre deux conditions dans une instruction conditionnelle:
(1..5).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4]
La condition est false
jusqu'à ce que la première partie devienne true
. Ensuite , il évalue à true
jusqu'à ce que la seconde partie est true
. Après cela, il redevient false
.
Cet exemple illustre ce qui est sélectionné:
[1, 2, 2, 3, 4, 4, 5].select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 2, 3, 4]
L'opérateur flip-flop ne fonctionne que dans l'opérateur ifs (y compris unless
) et l'opérateur ternaire. Sinon, il est considéré comme l'opérateur de la gamme.
(1..5).select do |e|
(e == 2) .. (e == 4)
end
# => ArgumentError: bad value for range
Il peut passer de false
à true
et inversement plusieurs fois:
((1..5).to_a * 2).select do |e|
e if (e == 2) .. (e == 4)
end
# => [2, 3, 4, 2, 3, 4]