web-dev-qa-db-fra.com

Comment écrire une instruction switch dans Ruby

Comment écrire une instruction switch en Ruby?

1980
Readonly

Ruby utilise plutôt l'expression case .

case x
when 1..5
  "It's between 1 and 5"
when 6
  "It's 6"
when "foo", "bar"
  "It's either foo or bar"
when String
  "You passed a string"
else
  "You gave me #{x} -- I have no idea what to do with that."
end

Ruby compare l'objet de la clause when à celui de la clause case à l'aide de l'opérateur ===. Par exemple, 1..5 === x et non pas x === 1..5.

Cela permet de créer des clauses when sophistiquées, comme indiqué ci-dessus. Les gammes, les classes et toutes sortes de choses peuvent être testées plutôt que simplement l'égalité.

Contrairement aux instructions switch dans de nombreuses autres langues, case de Ruby n’a pas de chute directe , il n’est donc pas nécessaire de terminer chaque when par un break. Vous pouvez également spécifier plusieurs correspondances dans une seule clause when, telle que when "foo", "bar".

2531
Chuck

case...when se comporte de manière inattendue lors du traitement des classes. Cela est dû au fait qu'il utilise l'opérateur ===.

Cet opérateur fonctionne comme prévu avec les littéraux, mais pas avec les classes:

1 === 1           # => true
Fixnum === Fixnum # => false

Cela signifie que si vous voulez faire un case ... when sur la classe d'un objet, cela ne fonctionnera pas:

obj = 'hello'
case obj.class
when String
  print('It is a string')
when Fixnum
  print('It is a number')
else
  print('It is not a string or number')
end

Imprimera "Ce n'est pas une chaîne ou un nombre".

Heureusement, ceci est facilement résolu. L'opérateur === a été défini pour qu'il renvoie true si vous l'utilisez avec une classe et fournissez une instance de cette classe en tant que second opérande:

Fixnum === 1 # => true

En bref, le code ci-dessus peut être corrigé en supprimant le .class:

obj = 'hello'
case obj  # was case obj.class
when String
  print('It is a string')
when Fixnum
  print('It is a number')
else
  print('It is not a string or number')
end

J'ai trouvé ce problème aujourd'hui alors que je cherchais une réponse, et comme c'était la première page qui apparaissait, je me suis dit que cela pourrait être utile à d'autres dans la même situation.

423
kikito

C'est fait par cas en Ruby. Voir aussi cet article sur Wikipedia .

Cité

case n
when 0
  puts 'You typed zero'
when 1, 9
  puts 'n is a perfect square'
when 2
  puts 'n is a prime number'
  puts 'n is an even number'
when 3, 5, 7
  puts 'n is a prime number'
when 4, 6, 8
  puts 'n is an even number'
else
  puts 'Only single-digit numbers are allowed'
end

Un autre exemple:

score = 70

result = case score
   when 0..40 then "Fail"
   when 41..60 then "Pass"
   when 61..70 then "Pass with Merit"
   when 71..100 then "Pass with Distinction"
   else "Invalid Score"
end

puts result

À la page 123 environ (j'utilise Kindle) de , le Ruby Programming Lanugage (1re édition, O'Reilly), dit le mot-clé then suivant les clauses when peut être remplacé par un retour à la ligne ou un point-virgule (comme dans la syntaxe if then else]. (Ruby 1.8 autorise également deux points au lieu de then... Mais cette syntaxe n'est plus autorisée dans Ruby 1.9.)

202

cas ... quand

Pour ajouter plus d'exemples à réponse de Chuck :

Avec paramètre:

case a
when 1
  puts "Single value"
when 2, 3
  puts "One of comma-separated values"
when 4..6
  puts "One of 4, 5, 6"
when 7...9
  puts "One of 7, 8, but not 9"
else
  puts "Any other thing"
end

Sans paramètre:

case
when b < 3
  puts "Little than 3"
when b == 3
  puts "Equal to 3"
when (1..10) === b
  puts "Something in closed range of [1..10]"
end

S'il vous plaît, soyez conscient de le problème que kikito met en garde.

94
mmdemirbas

De nombreux langages de programmation, en particulier ceux dérivés du C, prennent en charge le soi-disant Switch Fallthrough . Je cherchais le meilleur moyen de faire de même dans Ruby et je pensais que cela pourrait être utile aux autres:

Dans les langages de type C, l’incroyable chute ressemble à ceci:

switch (expression) {
    case 'a':
    case 'b':
    case 'c':
        // Do something for a, b or c
        break;
    case 'd':
    case 'e':
        // Do something else for d or e
        break;
}

En Ruby, on peut atteindre le même objectif de la manière suivante:

case expression
when 'a', 'b', 'c'
  # Do something for a, b or c
when 'd', 'e'
  # Do something else for d or e
end

Ce n'est pas strictement équivalent, car il n'est pas possible de laisser 'a' exécuter un bloc de code avant de tomber dans 'b' ou 'c', mais je le trouve assez similaire pour l'être utile de la même manière.

70
Robert Kajic

Dans Ruby 2.0, vous pouvez également utiliser lambdas dans les instructions case, comme suit:

is_even = ->(x) { x % 2 == 0 }

case number
when 0 then puts 'zero'
when is_even then puts 'even'
else puts 'odd'
end

Vous pouvez également créer facilement vos propres comparateurs en utilisant une structure avec un === personnalisé.

Moddable = Struct.new(:n) do
  def ===(numeric)
    numeric % n == 0
  end
end

mod4 = Moddable.new(4)
mod3 = Moddable.new(3)

case number
when mod4 then puts 'multiple of 4'
when mod3 then puts 'multiple of 3'
end

(Exemple tiré de " Les procédures peuvent-elles être utilisées avec des instructions case dans Ruby 2.0? ".)

Ou, avec une classe complète:

class Vehicle
  def ===(another_vehicle)
    self.number_of_wheels == another_vehicle.number_of_wheels
  end
end

four_wheeler = Vehicle.new 4
two_wheeler = Vehicle.new 2

case vehicle
when two_wheeler
  puts 'two wheeler'
when four_wheeler
  puts 'four wheeler'
end

(Exemple tiré de " Comment une instruction de cas Ruby fonctionne et ce que vous pouvez en faire) ".)

66
James Lim

Vous pouvez utiliser des expressions régulières, telles que la recherche d'un type de chaîne:

case foo
when /^(true|false)$/
   puts "Given string is boolean"
when /^[0-9]+$/ 
   puts "Given string is integer"
when /^[0-9\.]+$/
   puts "Given string is float"
else
   puts "Given string is probably string"
end

case de Ruby utilisera l'opérande d'égalité === pour cela (merci @JimDeville). Des informations complémentaires sont disponibles sur " Opérateurs Ruby ". Cela peut aussi être fait en utilisant l'exemple @mmdemirbas (sans paramètre), seule cette approche est plus propre pour ces types de cas.

50
Haris Krajina

Si vous souhaitez savoir comment utiliser une condition OR dans un boîtier de commutateur Ruby:

Ainsi, dans une instruction case, un , est l'équivalent de || dans une instruction if.

case car
   when 'Maruti', 'Hyundai'
      # Code here
end

Beaucoup d'autres choses que vous pouvez faire avec une instruction de cas Ruby

31
Manish Shrivastava

Elle s'appelle case et fonctionne comme prévu, avec bien plus de choses amusantes grâce à === qui implémente les tests.

case 5
  when 5
    puts 'yes'
  else
    puts 'else'
end

Maintenant pour s'amuser:

case 5 # every selector below would fire (if first)
  when 3..7    # OK, this is Nice
  when 3,4,5,6 # also Nice
  when Fixnum  # or
  when Integer # or
  when Numeric # or
  when Comparable # (?!) or
  when Object  # (duhh) or
  when Kernel  # (?!) or
  when BasicObject # (enough already)
    ...
end

Et il s'avère que vous pouvez également remplacer une chaîne if/else (c'est-à-dire même si les tests ne comportent pas de variable commune) avec case en omettant le paramètre initial case et en écrivant simplement expressions où la première correspondance est ce que vous voulez.

case
  when x.nil?
    ...
  when (x.match /'^fn'/)
    ...
  when (x.include? 'substring')
    ...
  when x.gsub('o', 'z') == 'fnzrq'
    ...
  when Time.now.tuesday?
    ...
end
30
DigitalRoss

Selon votre cas, vous préférerez peut-être utiliser un hash de méthodes.

S'il existe une longue liste de moments et que chacun d'entre eux a une valeur concrète à comparer (et non un intervalle), il sera plus efficace de déclarer un hachage de méthodes, puis d'appeler la méthode correspondante à partir du hachage.

# Define the hash
menu = {a: :menu1, b: :menu2, c: :menu2, d: :menu3}

# Define the methods
def menu1
  puts 'menu 1'
end

def menu2
  puts 'menu 2'
end

def menu3
  puts 'menu3'
end

# Let's say we case by selected_menu = :a
selected_menu = :a

# Then just call the relevant method from the hash
send(menu[selected_menu])
20
Alexander

Ruby utilise le case pour écrire des instructions de commutateur.

Selon le Ruby Docs :

Les instructions case comprennent une condition facultative, qui se trouve dans la position d'un argument de case et de zéro ou plusieurs clauses when. La première clause when pour faire correspondre la condition (ou pour évaluer 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 clause when réussie, ou nil s'il n'existe pas de telle clause.

Une instruction case peut se terminer par une clause else. Chaque when 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

Et comme ce blog de Honeybadger décrit Ruby Case;

Peut être utilisé avec Ranges :

case 5
when (1..10)
  puts "case statements match inclusion in a range"
end

## => "case statements match inclusion in a range"

Peut être utilisé avec Regex :

case "FOOBAR"
when /BAR$/
  puts "they can match regular expressions!"
end

## => "they can match regular expressions!"

Peut être utilisé avec Procs et Lambdas :

case 40
when -> (n) { n.to_s == "40" }
  puts "lambdas!"
end

## => "lambdas"

Vous pouvez également utiliser vos propres classes de correspondance:

class Success
  def self.===(item)
    item.status >= 200 && item.status < 300
  end
end

class Empty
  def self.===(item)
    item.response_size == 0
  end
end

case http_response
when Empty
  puts "response was empty"
when Success
  puts "response was a success"
end
20
Lahiru

Puisque switch case renvoie toujours un seul objet, nous pouvons directement imprimer son résultat:

puts case a
     when 0
        "It's zero"
     when 1
        "It's one"
     end
19
Sonu Oommen

Cas multi-valeur quand et sans valeur:

print "Enter your grade: "
grade = gets.chomp
case grade
when "A", "B"
  puts 'You pretty smart!'
when "C", "D"
  puts 'You pretty dumb!!'
else
  puts "You can't even use a computer!"
end

Et une solution expression régulière ici:

print "Enter a string: "
some_string = gets.chomp
case
when some_string.match(/\d/)
  puts 'String has numbers'
when some_string.match(/[a-zA-Z]/)
  puts 'String has letters'
else
  puts 'String has no numbers or letters'
end
18
123

Vous pouvez écrire des expressions de casse de deux manières différentes dans Ruby.

  1. Semblable à une série de déclarations "if"
  2. Spécifiez une cible à côté du cas et chaque clause "quand" est comparée à la cible.

1er chemin

age = 20
case 
when age >= 21
puts "display something"
when 1 == 0
puts "omg"
else
puts "default condition"
end

2ème voie

 case params[:unknown]
 when /Something/ then 'Nothing'
 when /Something else/ then 'I dont know'
 end
11
ysk

Vous pouvez faire comme ça de manière plus naturelle,

case expression
when condtion1
   function
when condition2
   function
else
   function
end
9
Navin

Si vous essayez de comparer des objets (classes), assurez-vous de disposer d'une méthode de vaisseau spatial (pas d'une blague) ou de comprendre comment ils sont comparés.

Voici une bonne discussion sur le sujet http://www.skorks.com/2009/09/Ruby-equality-and-object-comparison/

8
jmansurf
puts "Recommend me a language to learn?"
input = gets.chomp.downcase.to_s

case input
when 'Ruby'
    puts "Learn Ruby"
when 'python'
    puts "Learn Python"
when 'Java'
    puts "Learn Java"
when 'php'
    puts "Learn PHP"
else
    "Go to Sleep!"
end
5
Prabhakar

Comme indiqué dans de nombreuses réponses ci-dessus, l'opérateur === est utilisé sous le capot des déclarations case/when.

Voici quelques informations supplémentaires sur cet opérateur.

Opérateur d'égalité de cas: ===

De nombreuses classes intégrées de Ruby, telles que String, Range et Regexp, fournissent leurs propres implémentations de l'opérateur ===, également connu sous le nom d'égalité de cas, triple égal ou triple. Comme il est implémenté différemment dans chaque classe, il se comportera différemment selon le type d'objet sur lequel il a été appelé. Généralement, il retourne vrai si l'objet de droite "appartient à" ou "est un membre de" l'objet de gauche. Par exemple, il peut être utilisé pour tester si un objet est une instance d'une classe (ou de l'une de ses sous-classes).

String === "zen"  # Output: => true
Range === (1..2)   # Output: => true
Array === [1,2,3]   # Output: => true
Integer === 2   # Output: => true

Le même résultat peut être obtenu avec d'autres méthodes qui conviennent probablement le mieux, telles que is_a? et instance_of ?.

Mise en oeuvre de la gamme de ===

Lorsque l'opérateur === est appelé sur un objet d'intervalle, il renvoie true si la valeur de droite se situe dans l'intervalle de gauche.

(1..4) === 3  # Output: => true
(1..4) === 2.345 # Output: => true
(1..4) === 6  # Output: => false

("a".."d") === "c" # Output: => true
("a".."d") === "e" # Output: => false

Rappelez-vous que l'opérateur === appelle la méthode === de l'objet de gauche. Donc (1..4) === 3 est équivalent à (1..4). === 3. En d’autres termes, la classe de l’opérande de gauche déterminera quelle implémentation de la méthode === sera appelé, donc les positions d'opérande ne sont pas interchangeables.

Regexp Implémentation de ===

Renvoie true si la chaîne à droite correspond à l'expression régulière à gauche./zen/=== "pratiquez zazen aujourd'hui" # Résultat: => vrai # est similaire à "pratiquez zazen aujourd'hui" = ~/zen /

La seule différence pertinente entre les deux exemples ci-dessus est que, lorsqu'il y a correspondance, === renvoie true et = ~ renvoie un entier, qui est une valeur de vérité en Ruby. Nous y reviendrons bientôt.

5
BrunoFacca
$age =  5
case $age
when 0 .. 2
   puts "baby"
when 3 .. 6
   puts "little child"
when 7 .. 12
   puts "child"
when 13 .. 18
   puts "youth"
else
   puts "adult"
end

reference => https://www.tutorialspoint.com/Ruby/ruby_if_else.htm

3
Navneet

J'ai commencé à utiliser:

a = "secondcase"

var_name = case a
  when "firstcase" then "foo"
  when "secondcase" then "bar"
end

puts var_name
>> "bar"

Cela aide le code compact dans certains cas.

1
deepfritz

Il est essentiel de mettre la virgule ',' dans une clause when comme un || d’une instruction if, c’est-à-dire qu’il effectue une comparaison OR et non une comparaison AND entre les expressions délimitées de la clause when. Vérifiez donc la déclaration de cas ci-dessous. Clairement, x n’est pas inférieur à 2, mais la valeur de retour est "Apple". Pourquoi? Puisque x était 3 et que ',' agissait comme un ||, l'évaluation de l'expression 'x <2' n'a pas été une peine.

x = 3
case x
  when 3, x < 2 then 'Apple'
  when 3, x > 2 then 'orange'
end
 => "Apple"

Vous pourriez penser que pour effectuer un AND, vous pouvez faire quelque chose comme ceci ci-dessous. Mais ça ne marche pas. C'est parce que (3 && x> 2) est évalué à true et que Ruby prend la valeur True et la compare à x avec ===, ce qui n'est évidemment pas vrai, car x est 3.

case x
  when (3 && x < 2) then 'Apple'
  when (3 && x > 2) then 'orange'
end
 => nil 

Pour faire une comparaison &&, vous devrez traiter les cas comme et si sinon bloquer:

case
  when x == 3 && x < 2 then 'Apple'
  when x == 3 && x > 2 then 'orange'
end

Dans le livre Langage de programmation Ruby, Matz indique que cette dernière forme est la forme simple (et rarement utilisée), qui n'est rien d'autre qu'une syntaxe alternative pour if/elsif/else. Cependant, que ce soit rarement utilisé ou non, je ne vois pas d'autre moyen d'attacher plusieurs expressions && pour une clause 'when' donnée.

1
Donato

Pas de support pour les expressions régulières dans votre environnement? Par exemple. Shopify Editeur de script (avril 2018):

[Erreur]: constante non initialisée RegExp

Une solution de contournement après une combinaison de méthodes déjà couvertes dans ici et ici :

code = '!ADD-SUPER-BONUS!'

class StrContains
  def self.===(item)
    item.include? 'SUPER' or item.include? 'MEGA' or\
    item.include? 'MINI' or item.include? 'UBER'
  end
end

case code.upcase
when '12345PROMO', 'CODE-007', StrContains
  puts "Code #{code} is a discount code!"
when '!ADD-BONUS!'
  puts 'This is a bonus code!'
else
  puts 'Sorry, we can\'t do anything with the code you added...'
end

J'ai utilisé ors dans l'instruction de méthode de classe car || a une priorité supérieure à .include?. Si vous êtes un Ruby-Nazi , imaginez que j'ai utilisé ce (item.include? 'A') || ... à la place. repl.it test.

1
CPHPython

Nous pouvons écrire une instruction switch pour plusieurs conditions.

Par exemple,

x = 22

CASE x
  WHEN 0..14 THEN puts "#{x} is less than 15"    
  WHEN 15 THEN puts "#{x} equals 15" 
  WHEN 15 THEN puts "#{x} equals 15" 
  WHEN 15..20 THEN puts "#{x} is greater than 15" 
  ELSE puts "Not in the range, value #{x} " 
END
1
Foram Thakral