Como escrever uma instrução switch em Ruby

Respostas:

2670

Ruby usa a caseexpressão .

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 objeto na whencláusula com o objeto na casecláusula usando o ===operador. Por exemplo,, 1..5 === xe não x === 1..5.

Isso permite whencláusulas sofisticadas, como visto acima. Intervalos, classes e todo tipo de coisa podem ser testados em vez de apenas igualdade.

Diferentemente das switchdeclarações em muitos outros idiomas, o Ruby casenão tem falhas , portanto, não há necessidade de terminar cada whenum com a break. Você também pode especificar várias correspondências em uma única whencláusula, como when "foo", "bar".

Mandril
fonte
12
Você também pode fazer regex no argumento passado: quando / thisisregex / next line colocar "Esta é a correspondência encontrada nº 1 # {$ 1}" end
Automatico
8
Também é importante notar que você pode encurtar o código colocando a instrução whene returnna mesma linha:when "foo" then "bar"
Alexander - Reinstate Monica
9
Importante: Diferentemente das switchdeclarações em muitos outros idiomas, o Ruby's caseNÃO possui falhas , portanto, não há necessidade de terminar cada whenum com a break.
Janniks 03/09/19
3
Tantos votos positivos, mas nem uma menção à palavra-chave then. Por favor, veja também as outras respostas.
Clint Pachl
442

case...whense comporta um pouco inesperadamente ao lidar com classes. Isso se deve ao fato de ele usar o ===operador.

Esse operador trabalha como esperado com literais, mas não com classes:

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

Isso significa que se você quiser fazer uma case ... whenclasse sobre um objeto, 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 or number')
end

Irá imprimir "Não é uma string ou número".

Felizmente, isso é facilmente resolvido. O ===operador foi definido para que retorne truese você o usar com uma classe e fornecer uma instância dessa classe como o segundo operando:

Fixnum === 1 # => true

Em resumo, 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 or number')
end

Encontrei esse problema hoje ao procurar uma resposta, e essa foi a primeira página que apareceu, então achei que seria útil para outras pessoas na mesma situação.

kikito
fonte
obj = 'olá'; case obj; quando 'olá' então coloca "It's olá" final
Sugumar Venkatesan
Ter a .classparte É interessante notar, obrigado. Claro, esse é um comportamento inteiramente apropriado (embora eu pudesse ver como seria um erro comum pensar que isso seria impresso It is a string) ... você está testando a classe de algum objeto arbitrário, não o próprio objeto. Assim, por exemplo: case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" endresulta em: "Class!"Isso funciona da mesma para 1.class, {}.class, etc. deixando cair .class, temos "String!"ou "Something else"para estes vários valores.
Lindes
219

Isso é feito usando caseRuby. Veja também " Alternar declaração " 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 da Linguagem de programação Ruby (1ª edição, O'Reilly) no meu Kindle, ele diz que a thenpalavra - chave após as whencláusulas pode ser substituída por uma nova linha ou ponto e vírgula (como na if then elsesintaxe). (O Ruby 1.8 também permite dois pontos no lugar de then, mas essa sintaxe não é mais permitida no Ruby 1.9.)

falta de polaridade
fonte
38
when (-1.0/0.0)..-1 then "Epic fail"
Andrew Grimm
Esta é a resposta que eu usei, porque estou definindo uma variável com base nos resultados de uma alternância de caso. Em vez de dizer type = #{score}cada linha, posso simplesmente copiar o que você fez. Muito mais elegante que eu também gosto do one-liners muito melhor (se possível)
onebree
Eu sei que isso não tem relação com a essência da resposta, mas 4 também é um quadrado perfeito.
Nick Moore
109

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 de " Como escrever uma instrução switch em Ruby " sobre a qual o kikito alerta.

mmdemirbas
fonte
Obrigado, isso foi útil por ter várias opções em uma linha. Eu tinha tentado usaror
sixty4bit
73

Muitas linguagens de programação, especialmente as derivadas de C, têm suporte para o chamado Switch Fallthrough . Eu estava procurando a melhor maneira de fazer o mesmo no Ruby e achei que poderia ser útil para outras pessoas:

Em idiomas do tipo C, o avanço geralmente se parece com isso:

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

No 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

Isso não é estritamente equivalente, porque não é possível 'a'executar um bloco de código antes de passar para 'b'ou 'c', mas, na maioria das vezes, acho semelhante o suficiente para ser útil da mesma maneira.

Robert Kajic
fonte
72

No Ruby 2.0, você também pode usar lambdas em caseinstruções, da seguinte maneira:

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 extraído de "Os procs podem ser usados ​​com instruções de caso 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 uma declaração de caso Ruby funciona e o que você pode fazer com ela ".)

James Lim
fonte
52

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

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 caseusará 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), apenas essa abordagem é mais limpa para esses tipos de casos.

Haris Krajina
fonte
34

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

Portanto, em uma casedeclaração, a ,é equivalente a ||em uma ifdeclaração.

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

Consulte " Como uma declaração de caso Ruby funciona e o que você pode fazer com ela ".

Manish Shrivastava
fonte
formatação de código não está funcionando no artigo ligado :-)
froderik
33

É chamado casee funciona como você esperaria, além de muito mais coisas divertidas, cortesia da ===qual implementa os testes.

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

Agora, para se divertir:

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 substituir uma cadeia if / else arbitrária (ou seja, mesmo que os testes não envolvam uma variável comum) casedeixando de fora o caseparâmetro inicial e escrevendo expressões onde a primeira correspondência é o que você deseja.

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

Ruby usa o casepara escrever instruções de opção.

Conforme a casedocumentação:

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

Uma declaração de caso pode terminar com uma elsecláusula. Cada whendeclaraçã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 " declaração de caso de Ruby - técnicas avançadas " descreve 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 classes 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
Lahiru
fonte
22

Dependendo do seu caso, você pode preferir usar um hash de métodos.

Se houver uma longa lista de se whencada um deles tiver um valor concreto para comparar (não um intervalo), será mais eficaz declarar um hash de métodos e depois chamar o método relevante a partir desse hash.

# 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])
Alexander
fonte
21

Como switch casesempre retorna um único objeto, podemos imprimir diretamente seu resultado:

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

Caso com vários valores 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
123
fonte
2
porque não basta case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(onde ,os meios de nova linha)
tckmn
2
ah, e a primeira parte já está coberta nesta resposta , e muitas respostas já mencionam regex. Sinceramente, essa resposta não acrescenta nada de novo, e eu estou votando e votando para excluí-la.
tckmn
@DoorknobofSnow Isso mostra que você pode usar a solução Regex e os valores separados por vírgula no caso de switch. Não sei por que a solução está lhe dando tanta dor.
123
então, se eles obtiveram um "F", uma nota legítima, é culpa deles que seu código está faltando um caso?
Mike Graf
Eu gosto do humor disso e do fato de demonstrar que você pode combinar cadeias de caracteres com um caso.
esmeralda
13

Você pode escrever caseexpressões de duas maneiras diferentes no Ruby:

  1. Semelhante a uma série de if declarações
  2. Especifique um destino próximo ae casecada whencláusula é comparada ao destino.
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
fonte
Embora seu código possa responder à pergunta, você deve adicionar pelo menos uma breve descrição sobre o que seu código faz e como ele resolve o problema inicial.
user1438038
Vou considerar esta dica no futuro.
ysk
10

Você pode fazer isso de maneira mais natural,

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

Muitas ótimas respostas, mas pensei em adicionar um factóide. Se você estiver tentando comparar objetos (Classes), verifique se você tem um método de nave espacial (não uma piada) ou entenda como eles estão sendo comparados.

" Igualdade de Ruby e comparação de objetos " é uma boa discussão sobre o assunto.

jmansurf
fonte
7
Para referência, o método "nave espacial" <=>é usado para retornar -1, 0, 1 ou zero, dependendo se a comparação retorna menos que, igual, maior que ou não comparável, respectivamente. A documentação do módulo Comparable do Ruby explica isso.
the Tin Man
7

Conforme declarado em muitas das respostas acima, o ===operador é usado sob o capô case/ whendeclarações.

Aqui estão informações adicionais sobre esse operador:

Operador de igualdade de caso: ===

Muitas das classes internas do Ruby, como String, Range e Regexp, fornecem suas próprias implementações do ===operador, também conhecidas como "igualdade de caso", "igual a triplo" ou "três a igual". Como é implementado de maneira diferente em cada classe, ele se comportará de maneira diferente, dependendo do tipo de objeto em que foi chamado. Geralmente, ele retornará true se o objeto à direita "pertencer a" ou "for um membro" do objeto à esquerda. Por exemplo, ele pode ser usado para testar se um objeto é uma instância de uma classe (ou uma de suas subclasses).

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

O mesmo resultado pode ser alcançado com outros métodos que provavelmente são mais adequados para o trabalho, como is_a?einstance_of? .

Implementação de Gama de ===

Quando o ===operador é chamado em um objeto 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 chama o ===método do objeto esquerdo. Então (1..4) === 3é equivalente a (1..4).=== 3. Em outras palavras, a classe do operando esquerdo definirá qual implementação do ===método será chamada, de modo que as posições do operando não serão intercambiáveis.

Implementação Regexp de ===

Retorna true se a sequência à direita corresponder à expressão regular à esquerda.

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

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

BrunoFacca
fonte
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
fonte
1
É mais útil se você fornecer uma explicação sobre por que essa é a solução preferida e explicar como ela funciona. Queremos educar, não apenas fornecer código.
o homem de lata
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

Consulte " Ruby - se ... mais, caso, a menos " para obter mais informações.

Navneet
fonte
1

Comecei a usar:

a = "secondcase"

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

puts var_name
>> "bar"

Ajuda o código compacto em alguns casos.

deepfritz
fonte
1
Um código como esse geralmente deve ser feito usando Hashuma caseinstrução , e não uma instrução.
21416 Tom Lord
Usar um hash seria mais rápido quando esse comutador aumentasse.
o homem de lata
1

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

[Erro]: RegExp constante não inicializada

Uma solução alternativa após uma combinação de métodos 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 ors na instrução de método de classe, pois ||tem maior precedência que .include?. Se você é um rubi-nazista , imagine que eu usei isso (item.include? 'A') || .... teste repl.it.

CPHPython
fonte
1

É essencial enfatizar a vírgula ( ,) em uma whencláusula. Ele atua como um ||de uma ifdeclaração, ou seja, ele faz um OR comparação e não um E comparação entre as expressões delimitados da whencláusula. Veja a seguinte declaração de caso:

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

xnão é menor que 2, mas o valor de retorno é "apple". Por quê? Porque xtinha 3 anos e desde ',`` acts as an|| , it did not bother to evaluate the expressionx <2 '.

Você pode pensar que, para executar um AND , pode fazer algo como isto abaixo, mas não funciona:

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

Não funciona porque é (3 && x > 2)avaliado como verdadeiro, e Ruby pega o valor True e o compara xcom o ===que não é verdadeiro, pois xé 3.

Para fazer uma &&comparação, você terá que tratar casecomo um bloco if/ else:

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

No livro Ruby Programming Language, Matz diz que esse último formulário é simples (e raramente usado), que nada mais é do que uma sintaxe alternativa para if/ elsif/ else. No entanto, se é pouco usado ou não, não vejo outra maneira de anexar várias &&expressões para uma determinada whencláusula.

Donato
fonte
Isso não parece um bom estilo de codificação para mim. O uso de uma sintaxe alternativa rara ofusca desnecessariamente. Por que não usar normal if...elsif? Parece que você está tentando misturar uma declaração de caso e uma condição. Por quê? Basta colocar a condicional dentro do bloco when, por exemplo. when 3; ( x < 2 ) ? 'apple' : 'orange'
Sondra.kinsey 20/05/19
0

Podemos escrever a instrução switch para várias condições.

Por exemplo,

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
fonte
1
Isso não vai funcionar; Palavras-chave Ruby (eg. case, when, end) São maiúsculas de minúsculas e não pode ser maiúscula como esta.
Sondra.kinsey 20/05/19
NoMethodError (undefined method CASE 'para main: Object) `. Como @ sondra.kinsey disse, você não pode usar maiúsculas. Ruby achará que é um CONSTANTE.
o homem de lata
0

O caseoperador de instrução é comoswitch nos outros idiomas.

Esta é a sintaxe de switch...caseem C:

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

Esta é a sintaxe do 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

Por exemplo:

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 mais informações, consulte a casedocumentação.

Ben96
fonte