Cómo escribir una declaración de cambio en Ruby

Respuestas:

2671

Ruby usa la caseexpresión en su lugar.

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 compara el objeto en la whencláusula con el objeto en la casecláusula usando el ===operador. Por ejemplo, 1..5 === xy no x === 1..5.

Esto permite whencláusulas sofisticadas como se ve arriba. Los rangos, las clases y todo tipo de cosas se pueden probar en lugar de solo la igualdad.

A diferencia de las switchdeclaraciones en muchos otros idiomas, Ruby's caseno tiene fallos, por lo que no es necesario terminar cada uno whencon un break. También puede especificar múltiples coincidencias en una sola whencláusula como when "foo", "bar".

Arrojar
fuente
12
También puede hacer expresiones regulares en el argumento pasado: cuando / thisisregex / próxima línea pone "Esta es la coincidencia encontrada n. 1 # {$ 1}" final
Automático
8
También vale la pena señalar, puede acortar su código colocando la declaración wheny returnen la misma línea:when "foo" then "bar"
Alexander - Restablezca Mónica
99
Importante: a diferencia de las switchdeclaraciones en muchos otros idiomas, Ruby's caseNO tiene fallos, por lo que no hay necesidad de terminar cada uno whencon un break.
Janniks
3
Tantos votos positivos, pero ni siquiera una mención de la palabra clave then. Por favor, vea también las otras respuestas.
Clint Pachl
442

case...whense comporta un poco inesperadamente cuando maneja clases. Esto se debe al hecho de que utiliza el ===operador.

Ese operador funciona como se esperaba con literales, pero no con clases:

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

Esto significa que si desea hacer una case ... whenclase sobre un objeto, esto no funcionará:

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

Imprimirá "No es una cadena o número".

Afortunadamente, esto se resuelve fácilmente. El ===operador se ha definido para que regrese truesi lo usa con una clase y proporciona una instancia de esa clase como el segundo operando:

Fixnum === 1 # => true

En resumen, el código anterior se puede solucionar eliminando .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

Encontré este problema hoy mientras buscaba una respuesta, y esta fue la primera página que apareció, así que pensé que sería útil para otros en mi misma situación.

kikito
fuente
obj = 'hola'; caso obj; cuando 'hola' pone el final "Es hola"
Sugumar Venkatesan
Tener la .classparte en Es interesante observar, gracias. Por supuesto, este es un comportamiento completamente apropiado (aunque podría ver cómo podría ser un error común pensar que se imprimiría It is a string) ... está probando la clase de algún objeto arbitrario, no el objeto en sí. Así, por ejemplo: case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" endlos resultados en: "Class!"Esto funciona de la misma para 1.class, {}.class, etc caída .class, obtenemos "String!"o "Something else"para estos diversos valores.
lindes
219

Se hace usando caseen Ruby. Consulte también " Cambiar declaración " en Wikipedia.

Citado:

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

Otro ejemplo:

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

Alrededor de la página 123 de The Ruby Programming Language (1st Edition, O'Reilly) en mi Kindle, dice que la thenpalabra clave que sigue a las whencláusulas se puede reemplazar con una nueva línea o punto y coma (como en la if then elsesintaxis). (Ruby 1.8 también permite dos puntos en lugar de then, pero esta sintaxis ya no está permitida en Ruby 1.9).

nonopolaridad
fuente
38
when (-1.0/0.0)..-1 then "Epic fail"
Andrew Grimm
Esta es la respuesta que utilicé, porque estoy definiendo una variable basada en los resultados de un cambio de caso. En lugar de decir type = #{score}cada línea, simplemente puedo copiar lo que hiciste. Mucho más elegante, también me gustan mucho los one-liners (si es posible)
Onebree
Sé que esto no está relacionado con la esencia de la respuesta, pero 4 también es un cuadrado perfecto.
Nick Moore
109

caso ... cuando

Para agregar más ejemplos a la respuesta de Chuck :

Con parámetro:

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

Sin parámetro:

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

Tenga en cuenta " Cómo escribir una declaración de cambio en Ruby " sobre el que advierte kikito.

mmdemirbas
fuente
Gracias, esto fue útil para tener múltiples opciones en una línea. Había estado tratando de usaror
sixty4bit
73

Muchos lenguajes de programación, especialmente los derivados de C, tienen soporte para el llamado Switch Fallthrough . Estaba buscando la mejor manera de hacer lo mismo en Ruby y pensé que podría ser útil para otros:

En los lenguajes tipo C, la caída generalmente se ve así:

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, se puede lograr lo mismo de la siguiente manera:

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

Esto no es estrictamente equivalente, porque no es posible dejar 'a'ejecutar un bloque de código antes de caer en 'b'o 'c', pero en su mayor parte lo encuentro lo suficientemente similar como para ser útil de la misma manera.

Robert Kajic
fuente
72

En Ruby 2.0, también puede usar lambdas en casedeclaraciones, de la siguiente manera:

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

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

También puede crear sus propios comparadores fácilmente usando un Struct con un personalizado ===

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

(Ejemplo tomado de " ¿Se pueden usar procs con declaraciones de caso en Ruby 2.0? ").

O, con una clase completa:

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

(Ejemplo tomado de " Cómo funciona una declaración de caso Ruby y qué puede hacer con ella ").

James Lim
fuente
52

Puede usar expresiones regulares, como encontrar un tipo de cadena:

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 caseusará el operando de igualdad ===para esto (gracias @JimDeville). Hay información adicional disponible en " Operadores de Ruby ". Esto también se puede hacer usando el ejemplo @mmdemirbas (sin parámetro), solo este enfoque es más limpio para este tipo de casos.

Haris Krajina
fuente
34

Si está ansioso por saber cómo usar una condición OR en una caja de interruptor Ruby:

Entonces, en una casedeclaración, a ,es el equivalente de ||en una ifdeclaración.

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

Consulte " Cómo funciona una declaración de caso Ruby y qué puede hacer con ella ".

Shrivastava Manish
fuente
el formateo del código no funciona en el artículo vinculado :-)
froderik
33

Se llama casey funciona como era de esperar, además de muchas más cosas divertidas, cortesía de las ===cuales implementan las pruebas.

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

Ahora para divertirse:

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

Y resulta que también puede reemplazar una cadena arbitraria if / else (es decir, incluso si las pruebas no involucran una variable común) caseomitiendo el caseparámetro inicial y simplemente escribiendo expresiones donde la primera coincidencia es lo que desea.

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

Ruby usa el case para escribir declaraciones de cambio.

Según la casedocumentación:

Las declaraciones de caso consisten en una condición opcional, que está en la posición de un argumento a case, y cero o más whencláusulas. La primera whencláusula para hacer coincidir la condición (o para evaluar la verdad booleana, si la condición es nula) "gana", y se ejecuta su estrofa de código. El valor de la declaración del caso es el valor de la whencláusula exitosa , o nilsi no existe tal cláusula.

Una declaración de caso puede terminar con una elsecláusula. Cada whenuna declaración puede tener múltiples valores candidatos, separados por comas.

Ejemplo:

case x
when 1,2,3
  puts "1, 2, or 3"
when 10
  puts "10"
else
  puts "Some other number"
end

Versión más corta:

case x
when 1,2,3 then puts "1, 2, or 3"
when 10 then puts "10"
else puts "Some other number"
end

Y como " Declaración del caso de Ruby - técnicas avanzadas " describe a Ruby case;

Se puede usar con rangos :

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

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

Se puede usar con Regex :

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

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

Se puede usar con Procs y Lambdas :

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

## => "lambdas"

Además, puede usarse con sus propias clases de coincidencia:

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
fuente
22

Dependiendo de su caso, podría preferir utilizar un hash de métodos.

Si hay una larga lista de whensy cada uno de ellos tiene un valor concreto para comparar (no un intervalo), será más efectivo declarar un hash de métodos y luego llamar al método relevante del hash de esa manera.

# 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])
Alejandro
fuente
21

Como switch casesiempre devuelve un único objeto, podemos imprimir directamente su resultado:

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

Caso de valor múltiple cuando y sin valor:

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

Y una solución de expresión regular aquí:

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
fuente
2
por qué no solo case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(donde ,significa nueva línea)
tckmn
2
oh, y la primera parte ya está cubierta en esta respuesta , y muchas respuestas ya mencionan expresiones regulares. Francamente, esta respuesta no agrega nada nuevo, y estoy votando y votando para eliminarlo.
tckmn
@DoorknobofSnow Esto es para mostrar que puede usar la solución Regex y los valores separados por comas en el caso de cambio. No estoy seguro de por qué la solución te está causando tanto dolor.
123
así que si obtuvieron una "F", una calificación legítima, ¿es su culpa que a su código le falte un caso?
Mike Graf
Me gusta el humor de esto, y el hecho de que demuestra que puedes unir cadenas a un caso.
esmeril
13

Puedes escribir caseexpresiones de dos maneras diferentes en Ruby:

  1. Similar a una serie de ifdeclaraciones
  2. Especifique un objetivo al lado de casey cada whencláusula se compara con el objetivo.
age = 20
case 
when age >= 21
puts "display something"
when 1 == 0
puts "omg"
else
puts "default condition"
end

o:

case params[:unknown]
when /Something/ then 'Nothing'
when /Something else/ then 'I dont know'
end
ysk
fuente
Aunque su código puede responder la pregunta, debe agregar al menos una breve descripción de lo que hace su código y cómo resuelve el problema inicial.
user1438038
Consideraré este consejo en el futuro.
ysk
10

Puedes hacer esto de una manera más natural,

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

Muchas respuestas geniales, pero pensé que agregaría un factoide. Si estás intentando comparar objetos (clases), asegúrate de tener un método de nave espacial (no una broma) o entender cómo se están comparando.

" Ruby Equality And Object Comparison " es una buena discusión sobre el tema.

jmansurf
fuente
77
Como referencia, el método de "nave espacial" es <=>, que se utiliza para devolver -1, 0, 1 o cero, dependiendo de si la comparación devuelve menor que, igual, mayor que o no comparable, respectivamente. La documentación del módulo comparable de Ruby lo explica.
The Tin Man
7

Como se indica en muchas de las respuestas anteriores, el ===operador se utiliza bajo el capó en case/ whendeclaraciones.

Aquí hay información adicional sobre ese operador:

Operador de igualdad de casos: ===

Muchas de las clases integradas de Ruby, como String, Range y Regexp, proporcionan sus propias implementaciones del ===operador, también conocido como "igualdad de casos", "triple igual" o "tres iguales". Debido a que se implementa de manera diferente en cada clase, se comportará de manera diferente según el tipo de objeto al que se llamó. En general, devuelve verdadero si el objeto de la derecha "pertenece" o "es miembro del" objeto de la izquierda. Por ejemplo, se puede usar para probar si un objeto es una instancia de una clase (o una de sus subclases).

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

Se puede lograr el mismo resultado con otros métodos que probablemente sean los más adecuados para el trabajo, como is_a?y instance_of?.

Implementación de Rango de ===

Cuando ===se llama al operador en un objeto de rango, devuelve verdadero si el valor de la derecha cae dentro del rango de la izquierda.

(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

Recuerde que el ===operador invoca el ===método del objeto de la izquierda. Entonces (1..4) === 3es equivalente a (1..4).=== 3. En otras palabras, la clase del operando de la izquierda definirá a qué implementación del ===método se llamará, por lo que las posiciones de los operandos no son intercambiables.

Implementación de Regexp de ===

Devuelve verdadero si la cadena de la derecha coincide con la expresión regular de la izquierda.

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

La única diferencia relevante entre los dos ejemplos anteriores es que, cuando hay una coincidencia, ===devuelve verdadero y =~devuelve un entero, que es un valor verdadero en Ruby. Volveremos a esto pronto.

BrunoFacca
fuente
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
fuente
1
Ayuda más si proporciona una explicación de por qué esta es la solución preferida y explica cómo funciona. Queremos educar, no solo proporcionar código.
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

Para obtener más información, consulte " Ruby: si ... si no, caso, a menos que ".

Navneet
fuente
1

Empecé a usar:

a = "secondcase"

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

puts var_name
>> "bar"

Ayuda a compactar código en algunos casos.

Deepfritz
fuente
1
Un código como este generalmente debe hacerse usando una Hash, en lugar de una casedeclaración.
Tom Lord
Usar un hash sería más rápido cuando ese interruptor se hiciera grande.
El hombre de hojalata
1

¿No hay soporte para expresiones regulares en su entorno? Por ejemplo, Shopify Script Editor (abril de 2018):

[Error]: constante RegExp no inicializada

Una solución alternativa que sigue una combinación de métodos ya cubiertos anteriormente aquí y aquí :

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

Usé ors en la declaración del método de clase ya que ||tiene mayor prioridad que .include?. Si eres un rubí-nazi , imagina que usé esto (item.include? 'A') || ...en su lugar. repl.it test.

CPHPython
fuente
1

Es crítico enfatizar la coma ( ,) en una whencláusula. Actúa como ||de una ifdeclaración, es decir, se hace un OR de comparación y no un Y la comparación entre las expresiones delimitadas de la whencláusula. Vea la siguiente declaración de caso:

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

xno es menor que 2, pero el valor de retorno es "apple". ¿Por qué? Porque xera 3 y desde ',`` acts as an|| , it did not bother to evaluate the expressionx <2 '.

Puede pensar que para realizar un AND , puede hacer algo como esto a continuación, pero no funciona:

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

No funciona porque se (3 && x > 2)evalúa como verdadero, y Ruby toma el valor Verdadero y lo compara xcon ===, lo cual no es cierto, ya quex es 3.

Para hacer una &&comparación, tendrá que tratar casecomo un if/ elsebloque:

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

En el libro Ruby Programming Language, Matz dice que esta última forma es la forma simple (y que se usa con poca frecuencia), que no es más que una sintaxis alternativa para if/ elsif/ else. Sin embargo, si se usa con poca frecuencia o no, no veo ninguna otra forma de adjuntar múltiples &&expresiones para una whencláusula dada .

Donato
fuente
Esto no me parece un buen estilo de codificación. El uso de una sintaxis alternativa rara se ofusca innecesariamente. ¿Por qué no usar normal if...elsif? Parece que estás tratando de mezclar una declaración de caso y una condición. ¿Por qué? Simplemente ponga el condicional dentro del bloque when, por ejemplo. when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey
0

Podemos escribir una declaración de cambio para múltiples condiciones.

Por ejemplo,

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
fuente
1
Esto no funcionará; Palabras clave Ruby (por ejemplo. case, when, end) Entre mayúsculas y minúsculas y no pueden estar en mayúsculas como este.
sondra.kinsey
NoMethodError (undefined method CASO 'para main: Object) `. Como dijo @ sondra.kinsey, no puedes usar mayúsculas. Ruby pensará que es CONSTANTE.
El hombre de hojalata
0

El caseoperador de declaración es comoswitch en los otros idiomas.

Esta es la sintaxis de switch...caseen C:

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

Esta es la sintaxis de case...whenen Ruby:

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

Por ejemplo:

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

Para más información ver el case documentación.

Ben96
fuente