Como escrever uma instrução switch em Ruby?

Como faço para escrever uma instrução switch em Ruby?

Ruby usa a expressão case 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 o object na cláusula when com o object na cláusula case usando o operador === . Por exemplo, 1..5 === x , e não x === 1..5 .

Isto permite cláusulas sofisticadas when visto acima. Intervalos, classs e todos os tipos de coisas podem ser testados em vez de apenas igualdade.

Ao contrário das instruções switch em muitos outros idiomas, o case do Ruby não possui fall-through , então não há necessidade de finalizar cada um com um break . Você também pode especificar várias correspondências em uma única cláusula when , como when "foo", "bar" .

case...when se comporta um pouco inesperadamente ao lidar com classs. Isso se deve ao fato de que ele usa o operador === .

Esse operador funciona como esperado com literais, mas não com classs:

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

Isso significa que, se você quiser fazer um case ... when sobre a class de um object, isso não 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') end 

Vai imprimir “Não é uma string”.

Felizmente, isso é facilmente resolvido. O operador === foi definido para que ele retorne true se você usá-lo com uma class e fornecer uma instância dessa class como o segundo operando:

 Fixnum === 1 # => true 

Em suma, o código acima pode ser corrigido, removendo o .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') end 

Eu acertei esse problema hoje enquanto procurava por uma resposta, e essa foi a primeira página que apareceu, então eu achei que seria útil para outros na mesma situação.

Isso é feito por caso em Ruby. Veja também este artigo na 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 

Outro exemplo:

 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 

Na página 123 (Estou usando o Kindle) de The Ruby Programming Lanugage (1ª Edição, O’Reilly), ele diz que a palavra then chave then após as cláusulas when pode ser substituída por uma nova linha ou ponto-e-vírgula (como na syntax if then else ). (O Ruby 1.8 também permite dois pontos no lugar de … Mas essa syntax não é mais permitida no Ruby 1.9.)

caso … quando

Para adicionar mais exemplos à resposta de Chuck :

Com 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 

Sem 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 

Por favor, esteja ciente do problema que o kikito avisa.

Muitas linguagens de programação, especialmente aquelas derivadas de C, têm suporte para o chamado Falling Through . Eu estava procurando a melhor maneira de fazer o mesmo em Ruby e achei que poderia ser útil para os outros:

Em linguagens semelhantes a C, o processo de fallout through geralmente se parece com isto:

 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; } 

Em Ruby, o mesmo pode ser alcançado da seguinte maneira:

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

Isto não é estritamente equivalente, porque não é possível deixar 'a' executar um bloco de código antes de passar para 'b' ou 'c' , mas na maior parte eu acho que é similar o suficiente para ser útil da mesma maneira.

No Ruby 2.0, você também pode usar lambdas em instruções case , como segue:

 is_even = ->(x) { x % 2 == 0 } case number when 0 then puts 'zero' when is_even then puts 'even' else puts 'odd' end 

Você também pode criar seus próprios comparadores facilmente usando um Struct com um 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 

(Exemplo retirado de ” Pode o procs ser usado com instruções case no Ruby 2.0? “.)

Ou, com uma aula 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 

(Exemplo retirado de ” Como funciona uma instrução de caso Ruby e o que você pode fazer com ela “.)

Você pode usar expressões regulares, como encontrar um tipo de string:

 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 

O case de Ruby usará o operando de igualdade === para isso (obrigado @JimDeville). Informações adicionais estão disponíveis em ” Ruby Operators “. Isso também pode ser feito usando o exemplo @mmdemirbas (sem parâmetro), somente essa abordagem é mais limpa para esses tipos de casos.

Se você está ansioso para saber como usar uma condição OR em um caso de switch Ruby:

Então, em uma declaração de case , a , é o equivalente de || em uma declaração if .

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

Muitas outras coisas que você pode fazer com uma instrução de caso Ruby

É chamado case e funciona como você esperaria, além de muito mais coisas divertidas cortesia de === que implementa os testes.

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

Agora para um pouco de diversão:

 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 

E acontece que você também pode replace uma cadeia arbitrária if / else (isto é, mesmo se os testes não envolverem uma variável comum) com case e case deixando de fora o parâmetro inicial do case e apenas escrevendo expressões onde a primeira correspondência é o que você quer.

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

Como o switch case sempre retorna um único object, podemos imprimir diretamente seu resultado:

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

Caso de valor múltiplo quando e sem 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 

E uma solução de expressão regular aqui:

 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 

Dependendo do seu caso, você pode preferir usar um hash de methods.

Se houver uma longa lista de quando e cada um deles tiver um valor concreto para comparar com (não um intervalo), será mais eficaz declarar um hash de methods e, em seguida, chamar o método relevante do hash dessa maneira.

 # 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]) 

Ruby usa o case para escrever instruções switch.

De acordo com o Ruby Docs :

As instruções case consistem em uma condição opcional, que está na posição de um argumento para case e zero ou mais when cláusulas. A primeira cláusula when para corresponder à condição (ou para avaliar a verdade booleana, se a condição for nula) “wins”, e sua estrofe de código é executada. O valor da declaração de caso é o valor da cláusula when bem-sucedida ou nil se não houver tal cláusula.

Uma declaração de caso pode terminar com uma cláusula else . Cada when uma declaração pode ter vários valores candidatos, separados por vírgulas.

Exemplo:

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

Versão mais curta:

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

E como este blog de Honeybadger descreve o Ruby Case;

Pode ser usado com intervalos :

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

Pode ser usado com o Regex :

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

Pode ser usado com Procs e Lambdas :

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

Além disso, pode ser usado com suas próprias classs de correspondência:

 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 

Você pode escrever expressões de caso de duas maneiras diferentes em ruby.

  1. Semelhante a uma série de declarações “if”
  2. Especifique um destino ao lado do caso e cada cláusula “when” é comparada ao destino.

1a via

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

Ò caminho

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

Muitas respostas ótimas, mas eu pensei em adicionar um factoid. Se você está tentando comparar objects (Classes), certifique-se de ter um método de naves espaciais (não uma piada) ou entenda como eles estão sendo comparados

Aqui está uma boa discussão sobre o tópico http://www.skorks.com/2009/09/ruby-equality-and-object-comparison/

Você pode fazer assim de maneira mais natural,

 case expression when condtion1 function when condition2 function else function end 
 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 

Como indicado em muitas das respostas acima, o operador === é usado sob o capô no caso / quando declarações.

Aqui estão algumas informações extras sobre esse operador.

Operador de igualdade de casos: ===

Muitas das classs internas do Ruby, como String, Range e Regexp, fornecem suas próprias implementações do operador ===, também conhecidas como case-equality, triple equals ou threequals. Como ele é implementado de maneira diferente em cada class, ele se comportará de maneira diferente dependendo do tipo de object em que foi chamado. Geralmente, retorna true se o object à direita “pertence a” ou “é um membro de” o object à esquerda. Por exemplo, ele pode ser usado para testar se um object é uma instância de uma class (ou uma de suas subclasss).

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

O mesmo resultado pode ser obtido com outros methods que provavelmente são mais adequados para o trabalho, como is_a? e instance_of ?.

Implementação de faixa de ===

Quando o operador === é chamado em um object de intervalo, ele retorna true se o valor à direita estiver dentro do intervalo à esquerda.

 (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 

Lembre-se de que o operador === invoca o método === do object da esquerda. Então (1..4) === 3 é equivalente a (1..4). === 3. Em outras palavras, a class do operando à esquerda definirá qual implementação do método === será chamadas, então as posições dos operandos não são intercambiáveis.

Regexp Implementação de ===

Retorna true se a string à direita corresponder à expressão regular à esquerda. / zen / === “practice zazen today” # Saída: => true # é semelhante a “practica zazen hoje” = ~ / zen /

A única diferença relevante entre os dois exemplos acima é que, quando há uma correspondência, === retorna true e = ~ retorna um inteiro, que é um valor geral em Ruby. Voltaremos a isso em breve.

 $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 

referência => https://www.tutorialspoint.com/ruby/ruby_if_else.htm

Eu comecei a usar:

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

Isso ajuda o código compacto em alguns casos.

Não há suporte para expressões regulares em seu ambiente? Por exemplo, Editor de Roteiros do Shopify (abril de 2018):

[Erro]: constante não inicializada RegExp

Uma solução alternativa seguindo uma combinação de methods já abordados anteriormente aqui e aqui :

 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 

Eu usei or s na instrução de método de class desde || tem precedência mais alta que .include? . Se você é um ruby-nazi , por favor, imagine que eu usei isso (item.include? 'A') || ... (item.include? 'A') || ... vez disso. teste de repl.it.