Comment écrire une instruction switch dans Ruby

Réponses:

2671

Ruby utilise plutôt l' caseexpression .

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 dans la whenclause avec l'objet dans la caseclause à l'aide de l' ===opérateur. Par exemple, 1..5 === xet non x === 1..5.

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

Contrairement aux switchinstructions dans de nombreuses autres langues, Ruby casen'a pas d' interruption , il n'est donc pas nécessaire de terminer chacune whenpar un break. Vous pouvez également spécifier plusieurs correspondances dans une seule whenclause comme when "foo", "bar".

Mandrin
la source
12
Vous pouvez également effectuer une regex sur l'argument passé: lorsque / thisisregex / la ligne suivante met "Ceci est la correspondance trouvée n ° 1 # {$ 1}" end
Automatico
8
A noter également, vous pouvez raccourcir votre code en mettant la déclaration whenet returnsur la même ligne:when "foo" then "bar"
Alexander - Reinstate Monica
9
Important: contrairement aux switchinstructions dans de nombreuses autres langues, Ruby casen'a PAS de raccourci , il n'est donc pas nécessaire de terminer chacune whenpar un break.
janniks
3
Tant de votes positifs mais pas même une mention du mot-clé then. Veuillez également consulter les autres réponses.
Clint Pachl
442

case...whense comporte un peu de manière inattendue lors de la gestion 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 souhaitez effectuer une opération case ... whensur une classe d'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

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

Heureusement, cela est facilement résolu. L' ===opérateur a été défini de sorte qu'il renvoie truesi vous l'utilisez avec une classe et fournissez une instance de cette classe comme deuxième opérande:

Fixnum === 1 # => true

En bref, le code ci-dessus peut être corrigé en supprimant .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 rencontré ce problème aujourd'hui en cherchant une réponse, et c'était la première page qui apparaissait, alors j'ai pensé que cela serait utile à d'autres dans ma même situation.

kikito
la source
obj = 'bonjour'; case obj; quand 'bonjour' met alors "c'est bonjour" fin
Sugumar Venkatesan
Il .classest intéressant de noter que le rôle est joué, merci. Bien sûr, c'est un comportement tout à fait approprié (bien que je puisse voir comment ce pourrait être une erreur courante de penser que cela s'imprimerait It is a string) ... vous testez la classe d'un objet arbitraire, pas l'objet lui-même. Ainsi, par exemple: les case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" endrésultats dans: "Class!"Cela fonctionne même pour 1.class, {}.classetc. Dropping .class, nous obtenons "String!"ou "Something else"pour ces différentes valeurs.
lindes
219

Cela se fait en utilisant caseRuby. Voir également la « déclaration Switch » sur Wikipédia.

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

Sur la page 123 de The Ruby Programming Language (1st Edition, O'Reilly) sur mon Kindle, il est indiqué que le thenmot - clé suivant les whenclauses peut être remplacé par une nouvelle ligne ou un point-virgule (comme dans la if then elsesyntaxe). (Ruby 1.8 autorise également les deux points à la place de then, mais cette syntaxe n'est plus autorisée dans Ruby 1.9.)

non-polarité
la source
38
when (-1.0/0.0)..-1 then "Epic fail"
Andrew Grimm
C'est la réponse que j'ai utilisée, car je définis une variable basée sur les résultats d'un changement de cas. Plutôt que de dire type = #{score}chaque ligne, je peux simplement copier ce que vous avez fait. Beaucoup plus élégant j'aime aussi beaucoup mieux les one-liners (si possible)
onebree
Je sais que cela n'est pas lié à l'essence de la réponse, mais 4 est également un carré parfait.
Nick Moore
109

cas ... quand

Pour ajouter plus d'exemples à la 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 " Comment écrire une instruction switch en Ruby " que kikito met en garde.

mmdemirbas
la source
Merci, cela a été utile d'avoir plusieurs options sur une même ligne. J'avais essayé d'utiliseror
sixty4bit
73

De nombreux langages de programmation, en particulier ceux dérivés de C, prennent en charge le soi-disant Switch Fallthrough . Je cherchais la meilleure façon de faire de même dans Ruby et j'ai pensé que cela pourrait être utile aux autres:

Dans les langages de type C, la chute ressemble généralement à 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;
}

Dans Ruby, la même chose peut être obtenue 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 passer à 'b'ou 'c', mais pour la plupart je le trouve assez similaire pour être utile de la même manière.

Robert Kajic
la source
72

Dans Ruby 2.0, vous pouvez également utiliser des lambdas dans les caseinstructions, 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 à l'aide d'un Struct avec une personnalisation ===

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 procs peuvent-ils être utilisés 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 fonctionne une déclaration Ruby Case et ce que vous pouvez en faire ".)

James Lim
la source
52

Vous pouvez utiliser des expressions régulières, telles que rechercher 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

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

Haris Krajina
la source
34

Si vous êtes impatient de savoir comment utiliser une condition OU dans un boîtier de commutateur Ruby:

Ainsi, dans une casedéclaration, a ,est l'équivalent de ||dans une ifdéclaration.

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

Voir " Fonctionnement d'une déclaration Ruby Case et ce que vous pouvez en faire ".

Manish Shrivastava
la source
la mise en forme du code ne fonctionne pas dans l'article lié :-)
froderik
33

Il s'appelle caseet il fonctionne comme vous vous en doutez, en plus de beaucoup de choses amusantes grâce ===auxquelles les tests sont mis en œuvre.

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

Maintenant, pour le plaisir:

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 arbitraire (c'est-à-dire, même si les tests n'impliquent pas une variable commune) caseen omettant le caseparamètre initial et en écrivant simplement des expressions où la première correspondance correspond à 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
DigitalRoss
la source
23

Ruby utilise le casepour écrire des instructions switch.

Selon la casedocumentation:

Les instructions case se composent d'une condition facultative, qui est à la position d'un argument caseet de zéro ou plusieurs whenclauses. La première whenclause 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 whenclause réussie , ou nils'il n'y en a pas.

Une déclaration de cas peut se terminer par une elseclause. Chaque wheninstruction 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 " l'énoncé de cas de Ruby - techniques avancées " décrit Ruby case;

Peut être utilisé avec des gammes :

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"

Peut également être utilisé avec 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
Lahiru
la source
22

Selon votre cas, vous pourriez préférer utiliser un hachage de méthodes.

S'il y a une longue liste de whens et que chacun d'eux a une valeur concrète à comparer (pas un intervalle), il sera plus efficace de déclarer un hachage de méthodes puis d'appeler la méthode appropriée à partir du hachage comme ça.

# 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])
Alexandre
la source
21

Puisque switch caserenvoie 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
Sonu Oommen
la source
20

Cas à valeurs multiples 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 d' 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
123
la source
2
pourquoi pas seulement case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(où ,signifie
newline
2
oh, et la première partie est déjà couverte dans cette réponse , et de nombreuses réponses mentionnent déjà l'expression régulière. Franchement, cette réponse n'ajoute rien de nouveau, et je vote en aval et je vote pour la supprimer.
tckmn
@DoorknobofSnow C'est pour montrer que vous pouvez utiliser la solution Regex et les valeurs séparées par des virgules dans le cas du commutateur. Je ne sais pas pourquoi la solution vous fait tant mal.
123
donc s'ils ont obtenu un "F", une note légitime, c'est leur faute si votre code manque un cas?
Mike Graf
J'aime l'humour de cela et le fait que cela démontre que vous pouvez faire correspondre des cordes à un boîtier.
emery
13

Vous pouvez écrire des caseexpressions de deux manières différentes dans Ruby:

  1. Semblable à une série de ifdéclarations
  2. Spécifiez une cible à côté de caseet chaque whenclause est comparée à la cible.
age = 20
case 
when age >= 21
puts "display something"
when 1 == 0
puts "omg"
else
puts "default condition"
end

ou:

case params[:unknown]
when /Something/ then 'Nothing'
when /Something else/ then 'I dont know'
end
ysk
la source
Bien que votre code puisse répondre à la question, vous devez ajouter au moins une brève description de ce que fait votre code et comment il résout le problème initial.
user1438038
J'examinerai cette astuce à l'avenir.
2017 à 11h59
10

Vous pouvez faire ça de façon plus naturelle,

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

Beaucoup de bonnes réponses mais je pensais que j'ajouterais un factoïde .. Si vous essayez de comparer des objets (classes) assurez-vous que vous avez une méthode de vaisseau spatial (pas une blague) ou comprenez comment ils sont comparés

" Ruby Equality And Object Comparison " est une bonne discussion sur le sujet.

jmansurf
la source
7
Pour référence, la méthode du «vaisseau spatial» <=>est utilisée pour renvoyer -1, 0, 1 ou zéro selon que la comparaison renvoie respectivement moins que, égal, supérieur ou non comparable. La documentation du module Comparable de Ruby l' explique.
The Tin Man
7

Comme indiqué dans la plupart des réponses ci-dessus, l' ===opérateur est utilisé sous le capot sur case/ wheninstructions.

Voici des 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 appelées «égalité de casse», «triple égal» ou «trois quals». Parce qu'il est implémenté différemment dans chaque classe, il se comportera différemment selon le type d'objet auquel il a été appelé. En général, elle renvoie vrai si l'objet de droite "appartient à" ou "est membre de" l'objet de gauche. Par exemple, il peut être utilisé pour tester si un objet est une instance d'une classe (ou 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 sont probablement les mieux adaptées au travail, telles que is_a?et instance_of?.

Mise en œuvre de la gamme ===

Lorsque l' ===opérateur est appelé sur un objet de plage, il renvoie vrai si la valeur de droite se situe dans la plage 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

N'oubliez pas que l' ===opérateur appelle la ===méthode de l'objet de gauche. (1..4) === 3Est donc équivalent à (1..4).=== 3. En d'autres termes, la classe de l'opérande de gauche définira quelle implémentation de la ===méthode sera appelée, de sorte que les positions des opérandes ne sont pas interchangeables.

Regexp Implementation of ===

Renvoie true si la chaîne de droite correspond à l'expression régulière de gauche.

/zen/ === "practice zazen today"  # Output: => true
# is similar to
"practice zazen today"=~ /zen/

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

BrunoFacca
la source
5
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
Prabhakar Undurthi
la source
1
Cela aide davantage si vous expliquez pourquoi c'est la solution préférée et expliquez comment cela fonctionne. Nous voulons éduquer, pas seulement fournir du code.
The Tin Man
3
$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

Voir " Ruby - si ... sinon, cas, sauf " pour plus d'informations.

Navneet
la source
1

J'ai commencé à utiliser:

a = "secondcase"

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

puts var_name
>> "bar"

Il aide à compacter du code dans certains cas.

deepfritz
la source
1
Un code comme celui-ci doit généralement être effectué à l'aide d'un Hash, plutôt que d'une caseinstruction.
Tom Lord
L'utilisation d'un hachage serait plus rapide lorsque ce commutateur devient gros.
The Tin Man
1

Pas de prise en charge des expressions régulières dans votre environnement? Par exemple, Shopify Script Editor (avril 2018):

[Erreur]: RegExp constante non initialisée

Une solution de contournement suivant une combinaison de méthodes déjà décrites précédemment 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 depuis ||a une priorité plus élevée que .include?. Si vous êtes un rubis-nazi , imaginez s'il vous plaît que j'ai utilisé cela à la (item.include? 'A') || ...place. test repl.it.

CPHPython
la source
1

Il est essentiel de souligner la virgule ( ,) dans une whenclause. Il agit comme ||d'une ifdéclaration, qui est, il fait un OU comparaison et non une ET comparaison entre les expressions délimitées de la whenclause. Voir l'énoncé de cas suivant:

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

xn'est pas inférieur à 2, mais la valeur de retour est "apple". Pourquoi? Parce que xc'était 3 et depuis ',`` acts as an|| , it did not bother to evaluate the expressionx <2 '.

Vous pourriez penser que pour effectuer un ET , vous pouvez faire quelque chose comme ceci ci-dessous, mais cela ne fonctionne pas:

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

Cela ne fonctionne pas, car (3 && x > 2)il a la valeur true, et Ruby prend la valeur True et la compare à xavec ===, ce qui n'est pas vrai, car elle xvaut 3.

Pour faire une &&comparaison, vous devrez traiter casecomme un bloc if/ else:

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

Dans le livre Ruby Programming Language, Matz dit 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, qu'il soit rarement utilisé ou non, je ne vois pas d'autre moyen d'attacher plusieurs &&expressions pour une donnéewhen clause .

Donato
la source
Cela ne me semble pas être un bon style de codage. L'utilisation d'une syntaxe alternative rare obscurcit inutilement. Pourquoi ne pas utiliser normal if...elsif? Il semble que vous essayez de mélanger une déclaration de cas et une condition. Pourquoi? Mettez simplement le conditionnel dans le bloc when, par exemple. when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey
0

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
Foram Thakral
la source
1
Cela ne fonctionnera pas; Mots - clés Ruby (par exemple. case, when, end) Sont sensibles à la casse et ne peuvent pas être en majuscules comme celui - ci.
sondra.kinsey
NoMethodError (undefined method CASE 'pour main: Object) `. Comme l'a dit @ sondra.kinsey, vous ne pouvez pas utiliser de majuscules. Ruby pensera que c'est une constante.
The Tin Man
0

L' caseopérateur de déclaration est comme switchdans les autres langues.

Voici la syntaxe de switch...caseen C:

switch (expression)
​{
    case constant1:
      // statements
      break;
    case constant2:
      // statements
      break;
    .
    .
    .
    default:
      // default statements
}

Voici la syntaxe de case...whenRuby:

case expression
  when constant1, constant2 #Each when statement can have multiple candidate values, separated by commas.
     # statements 
     next # is like continue in other languages
  when constant3
     # statements 
     exit # exit is like break in other languages
  .
  .
  .
  else
     # statements
end

Par exemple:

x = 10
case x
when 1,2,3
  puts "1, 2, or 3"
  exit
when 10
  puts "10" # it will stop here and execute that line
  exit # then it'll exit
else
  puts "Some other number"
end

Pour plus d'informations, consultez la casedocumentation.

Ben96
la source