Come scrivere un'istruzione switch in Ruby


Risposte:


2671

Ruby usa invece l' caseespressione .

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 confronta l'oggetto nella whenclausola con l'oggetto nella caseclausola usando l' ===operatore. Ad esempio 1..5 === x, e non x === 1..5.

Questo consente sofisticati when clausole come visto sopra. Intervalli, classi e ogni genere di cose possono essere testati piuttosto che solo uguaglianza.

A differenza delle switchaffermazioni in molte altre lingue, Ruby casenon ha errori , quindi non è necessario terminare ognuna whencon un break. Puoi anche specificare più corrispondenze in una singola whenclausola come when "foo", "bar".


12
Puoi anche fare regex sull'argomento passato: when / thisisregex / next line mette "Questa è la corrispondenza trovata n. 1 # {$ 1}" end
Automatico

8
Vale anche la pena notare che è possibile abbreviare il codice inserendo la dichiarazione whene returnsulla stessa riga:when "foo" then "bar"
Alexander - Ripristina Monica

9
Importante: a differenza delle switchaffermazioni in molte altre lingue, Ruby caseNON ha errori , quindi non è necessario terminare ognuna whencon un break.
Janniks,

3
Così tanti voti positivi e nemmeno una menzione della parola chiave then. Vedi anche le altre risposte.
Clint Pachl,

442

case...whensi comporta un po 'inaspettatamente quando si gestiscono le classi. Ciò è dovuto al fatto che utilizza l' ===operatore.

Quell'operatore funziona come previsto con i valori letterali, ma non con le classi:

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

Ciò significa che se si desidera eseguire una case ... whenclasse over di un oggetto, questo non funzionerà:

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

Stampa "Non è una stringa o un numero".

Fortunatamente, questo è facilmente risolvibile. L' ===operatore è stato definito in modo che ritorni truese lo usi con una classe e fornisci un'istanza di quella classe come secondo operando:

Fixnum === 1 # => true

In breve, il codice sopra può essere risolto rimuovendo .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

Oggi ho riscontrato questo problema mentre cercavo una risposta, e questa è stata la prima pagina che appare, quindi ho pensato che sarebbe stato utile per gli altri nella mia stessa situazione.


obj = 'ciao'; case obj; quando "ciao" mette fine "È ciao"
Sugumar Venkatesan,

Avere la .classparte in è interessante da notare, grazie. Certo, questo è un comportamento del tutto appropriato (anche se ho potuto vedere come potrebbe essere un errore comune pensare che verrebbe stampato It is a string) ... stai testando la classe di qualche oggetto arbitrario, non l'oggetto stesso. Così, per esempio: case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" endi risultati a: "Class!"Questo funziona lo stesso per 1.class, {}.classecc Dropping .class, si ottiene "String!"o "Something else"per questi diversi valori.
scade l'

219

Viene fatto usando casein Ruby. Vedi anche " Cambia istruzione " su Wikipedia.

citato:

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 altro esempio:

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

A pagina 123 di The Ruby Programming Language (1st Edition, O'Reilly) sul mio Kindle, si dice che la thenparola chiave che segue le whenclausole può essere sostituita con una nuova riga o punto e virgola (proprio come nella if then elsesintassi). (Ruby 1.8 consente anche i due punti al posto di then, ma questa sintassi non è più consentita in Ruby 1.9.)


38
when (-1.0/0.0)..-1 then "Epic fail"
Andrew Grimm,

Questa è la risposta che ho usato, perché sto definendo una variabile in base ai risultati di un cambio di case. Invece di dire type = #{score}ogni riga, posso semplicemente copiare quello che hai fatto. Molto più elegante, mi piacciono molto meglio anche i one-liner (se possibile)
onebree

So che questo non è correlato all'essenza della risposta, ma 4 è anche un quadrato perfetto.
Nick Moore,

109

caso ... quando

Per aggiungere altri esempi alla risposta di Chuck :

Con parametro:

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

Senza parametro:

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

Si prega di essere consapevoli di " Come scrivere un'istruzione switch in Ruby " di cui kikito avverte.


Grazie, questo è stato utile per avere più opzioni su una riga. Avevo cercato di usareor
sixty4bit

73

Molti linguaggi di programmazione, in particolare quelli derivati ​​da C, supportano il cosiddetto Switch Fallthrough . Stavo cercando il modo migliore per fare lo stesso in Ruby e ho pensato che potesse essere utile ad altri:

Nei linguaggi simili a C, generalmente il fallthrough appare così:

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

In Ruby, lo stesso può essere ottenuto nel modo seguente:

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

Questo non è strettamente equivalente, perché non è possibile consentire l' 'a'esecuzione di un blocco di codice prima di passare a 'b'o 'c', ma per la maggior parte lo trovo abbastanza simile da essere utile allo stesso modo.


72

In Ruby 2.0, puoi anche usare lambdas nelle caseistruzioni, come segue:

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

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

Puoi anche creare facilmente i tuoi comparatori usando un Struct con un'abitudine ===

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

(Esempio tratto da "I proc possono essere usati con le istruzioni case in Ruby 2.0? ".)

Oppure, con una classe 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

(Esempio tratto da " Come funziona un'istruzione di un caso Ruby e cosa si può fare con esso ".)


52

Puoi usare espressioni regolari, come trovare un tipo di stringa:

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

Per questo Ruby caseutilizzerà l'operando di uguaglianza ===(grazie a @JimDeville). Ulteriori informazioni sono disponibili su " Operatori Ruby ". Questo può essere fatto anche usando l'esempio @mmdemirbas (senza parametro), solo questo approccio è più pulito per questi tipi di casi.



33

Si chiama casee funziona come ci si aspetterebbe, oltre a molte altre cose divertenti per gentile concessione di ===quali implementano i test.

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

Ora per divertirti:

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 si scopre che puoi anche sostituire una catena if / else arbitraria (cioè, anche se i test non coinvolgono una variabile comune) con casetralasciando il caseparametro iniziale e scrivendo semplicemente espressioni in cui la prima corrispondenza è quella che desideri.

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

23

Ruby utilizza le caseistruzioni switch per la scrittura.

Secondo la casedocumentazione:

Le dichiarazioni di casi sono costituite da una condizione facoltativa, che è nella posizione di un argomento casee da zero o più whenclausole. La prima whenclausola che corrisponde alla condizione (o per valutare la verità booleana, se la condizione è nulla) "vince" e la sua stanza del codice viene eseguita. Il valore dell'istruzione case è il valore della whenclausola corretta , oppurenil se non esiste tale clausola.

Un'istruzione case può terminare con una elseclausola. Ciascuna whenistruzione può avere più valori candidati, separati da virgole.

Esempio:

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

Versione più corta:

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

E come " Dichiarazione del caso di Ruby - tecniche avanzate " descrive Ruby case;

Può essere utilizzato con gli intervalli :

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

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

Può essere utilizzato con Regex :

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

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

Può essere utilizzato con Procs e Lambdas :

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

## => "lambdas"

Inoltre, può essere utilizzato con le tue classi di match:

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

22

A seconda del caso, potresti preferire utilizzare un hash di metodi.

Se esiste un lungo elenco di se whenciascuno di essi ha un valore concreto da confrontare (non un intervallo), sarà più efficace dichiarare un hash di metodi e quindi chiamare il metodo rilevante dall'hash in quel modo.

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

21

Poiché switch caserestituisce sempre un singolo oggetto, possiamo stampare direttamente il suo risultato:

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

20

Valore multiplo quando e nessun valore:

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 una soluzione di espressione regolare qui:

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

2
perché non solo case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(dove ,significa newline)
tckmn

2
oh, e la prima parte è già trattata in questa risposta , e molte risposte menzionano già regex. Francamente, questa risposta non aggiunge nulla di nuovo e sto effettuando il downvoting e votando per eliminarla.
martedì

@DoorknobofSnow Questo dimostra che è possibile utilizzare la soluzione Regex e i valori separati da virgola in caso di cambio. Non sono sicuro del motivo per cui la soluzione ti dà così tanto dolore.
123

quindi se hanno una "F", un grado legittimo, è colpa loro se al tuo codice manca un caso?
Mike Graf,

Mi piace l'umorismo di questo e il fatto che dimostri che è possibile abbinare le stringhe a un caso.
smeriglio

13

Puoi scrivere caseespressioni in due modi diversi in Ruby:

  1. Simile a una serie di ifaffermazioni
  2. Specificare un target accanto alla casee ogni whenclausola viene confrontata con il target.
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

Sebbene il codice possa rispondere alla domanda, è necessario aggiungere almeno una breve descrizione di ciò che fa il codice e di come risolve il problema iniziale.
user1438038,

Prenderò in considerazione questo suggerimento in futuro.
ysk,

10

Puoi fare così in modo più naturale,

case expression
when condtion1
   function
when condition2
   function
else
   function
end

9

Molte risposte fantastiche, ma ho pensato di aggiungere un factoide. Se stai tentando di confrontare oggetti (Classi) assicurati di avere un metodo di nave spaziale (non uno scherzo) o di capire come vengono confrontati

" Ruby Equality And Object Comparison " è una buona discussione sull'argomento.


7
Per riferimento, il metodo "nave spaziale" è <=>, che viene usato per restituire -1, 0, 1 o zero a seconda che il confronto ritorni rispettivamente minore di, uguale, maggiore di o non comparabile. La documentazione del modulo comparabile di Ruby lo spiega.
Tin Man,

7

Come indicato in molte delle risposte di cui sopra, l' ===operatore viene utilizzato sotto le istruzioni on case/ hood when.

Ecco ulteriori informazioni su quell'operatore:

Operatore di uguaglianza di caso: ===

Molte delle classi integrate di Ruby, come String, Range e Regexp, forniscono le proprie implementazioni ===dell'operatore, note anche come "case-uguaglianza", "triple uguali" o "trequals". Poiché è implementato in modo diverso in ogni classe, si comporterà in modo diverso a seconda del tipo di oggetto su cui è stato chiamato. In genere, restituisce true se l'oggetto a destra "appartiene a" o "è un membro di" l'oggetto a sinistra. Ad esempio, può essere utilizzato per verificare se un oggetto è un'istanza di una classe (o una delle sue sottoclassi).

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

Lo stesso risultato può essere ottenuto con altri metodi che sono probabilmente più adatti per il lavoro, come is_a?einstance_of? .

Gamma Implementazione di ===

Quando l' ===operatore viene chiamato su un oggetto intervallo, restituisce vero se il valore a destra rientra nell'intervallo a sinistra.

(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

Ricordare che l' ===operatore invoca il ===metodo dell'oggetto per la mano sinistra. Quindi (1..4) === 3è equivalente a (1..4).=== 3. In altre parole, la classe dell'operando di sinistra definirà quale implementazione del ===metodo verrà chiamata, quindi le posizioni dell'operando non sono intercambiabili.

Regexp Implementazione di ===

Restituisce vero se la stringa a destra corrisponde all'espressione regolare a sinistra.

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

L'unica differenza rilevante tra i due esempi sopra è che, quando c'è una corrispondenza, ===restituisce true e =~restituisce un numero intero, che è un valore di verità in Ruby. Torneremo presto su questo.


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

1
Aiuta di più se fornisci una spiegazione del perché questa è la soluzione preferita e spiega come funziona. Vogliamo educare, non solo fornire codice.
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

Vedi " Ruby - if ... else, case, salvo " per maggiori informazioni.


1

Ho iniziato a usare:

a = "secondcase"

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

puts var_name
>> "bar"

Aiuta in alcuni casi a compattare il codice.


1
Il codice come questo dovrebbe di solito essere fatto usando una Hash, piuttosto che caseun'istruzione.
Tom Lord,

L'uso di un hash sarebbe più veloce quando l'interruttore diventa grande.
l'Uomo di latta il

1

Nessun supporto per le espressioni regolari nel tuo ambiente? Ad esempio, Shopify Script Editor (aprile 2018):

[Errore]: RegExp costante non inizializzata

Una soluzione alternativa che segue una combinazione di metodi già precedentemente trattati qui e qui :

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

Ho usato ors nell'istruzione del metodo di classe poiché ||ha una precedenza più alta di .include?. Se sei un rubino-nazista , immagina di averlo usato (item.include? 'A') || ...invece. test di repl.it.


1

È fondamentale enfatizzare la virgola ( ,) in una whenclausola. Esso agisce come un ||di ifdichiarazione, che è, lo fa un OR di confronto e non un E il confronto tra le espressioni delimitati della whenclausola. Vedi la seguente dichiarazione del caso:

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

xnon è inferiore a 2, ma il valore restituito è "apple". Perché? Perché xera 3 e da allora ',`` acts as an|| , it did not bother to evaluate the expressionx <2 '.

Potresti pensare che per eseguire un AND , puoi fare qualcosa di simile qui sotto, ma non funziona:

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

Non funziona perché (3 && x > 2)restituisce true e Ruby prende il valore True e lo confronta xcon ===, il che non è vero, poiché xè 3.

Per fare un &&confronto, dovrai trattare casecome un blocco if/ else:

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

Nel libro Ruby Programming Language, Matz afferma che quest'ultima forma è la forma semplice (e usata raramente), che non è altro che una sintassi alternativa per if/ elsif/ else. Tuttavia, sia che venga usato di rado o meno, non vedo nessun altro modo per allegare &&espressioni multiple per una determinata whenclausola.


Questo non mi sembra un buon stile di programmazione. L'uso di una rara sintassi alternativa offusca inutilmente. Perché non usare normale if...elsif? Sembra che tu stia provando a mescolare un'istruzione case e una condizione. Perché? Basta inserire il condizionale all'interno del blocco when, ad es. when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey,

0

Possiamo scrivere un'istruzione switch per più condizioni.

Per esempio,

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
Questo non funzionerà; Rubino parole chiave (ad es. case, when, end) Sono case-sensitive e non possono essere maiuscolo come questo.
sondra.kinsey,

NoMethodError (undefined method CASE 'per main: Object) `. Come diceva @ sondra.kinsey, non è possibile utilizzare le maiuscole. Ruby penserà che sia una COSTANTE.
l'Uomo di latta il

0

L' caseoperatore dell'istruzione è simileswitch nelle altre lingue.

Questa è la sintassi di switch...casein C:

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

Questa è la sintassi di case...whenin 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

Per esempio:

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

Per ulteriori informazioni consultare la casedocumentazione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.