Jak napisać instrukcję switch w Ruby


Odpowiedzi:


2669

Zamiast tego Ruby używa casewyrażenia .

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 porównuje obiekt w whenklauzuli z obiektem w caseklauzuli za pomocą ===operatora. Na przykład 1..5 === xi nie x === 1..5.

Pozwala to na stosowanie wyrafinowanych whenklauzul, jak pokazano powyżej. Zakresy, klasy i wszelkiego rodzaju rzeczy mogą być testowane pod kątem nie tylko równości.

W przeciwieństwie do switchstwierdzeń w wielu innych językach, Ruby's casenie ma przewrotności , więc nie ma potrzeby kończenia każdego whenna break. Możesz także określić wiele dopasowań w jednej whenklauzuli, takiej jak when "foo", "bar".


12
Możesz także wykonać wyrażenie regularne na przekazanym argumencie: kiedy / thisisregex / next wiersz wstawia „To jest znalezione dopasowanie nr. 1 # {$ 1}” koniec
Automatico

8
Warto również zauważyć, że możesz skrócić swój kod, umieszczając instrukcję wheni returnw tym samym wierszu:when "foo" then "bar"
Alexander - Przywróć Monikę

9
Ważne: W przeciwieństwie do switchstwierdzeń w wielu innych językach, Ruby caseNIE ma przewrotności , więc nie ma potrzeby kończenia każdego whenznakiem break.
janniks

3
Tyle głosów, nawet jeszcze nie wspominając o tym słowie kluczowym then. Zobacz także inne odpowiedzi.
Clint Pachl

442

case...whenzachowuje się nieco nieoczekiwanie podczas obsługi klas. Wynika to z faktu, że korzysta on z ===operatora.

Ten operator działa zgodnie z oczekiwaniami z literałami, ale nie z klasami:

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

Oznacza to, że jeśli chcesz wykonać case ... whenponad klasę obiektu, to nie zadziała:

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

Wypisuje „To nie jest ciąg ani liczba”.

Na szczęście można to łatwo rozwiązać. ===Operator został zdefiniowany tak, że wraca true, jeśli używasz go z klasy i dostarczamy instancji tej klasy jako drugiego argumentu:

Fixnum === 1 # => true

Krótko mówiąc, powyższy kod można naprawić, usuwając .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

Znalazłem ten problem dzisiaj, szukając odpowiedzi, i to była pierwsza strona, więc pomyślałem, że przyda się innym w tej samej sytuacji.


obj = 'hello'; case obj; kiedy „hello” oznacza koniec „It's hello”
Sugumar Venkatesan

Mając .classudział w Interesujący jest fakt, dzięki. Oczywiście, jest to całkowicie właściwe zachowanie (chociaż mogłem zobaczyć, jak często może się zdarzyć, że to się wydrukuje It is a string) ... testujesz klasę dowolnego obiektu, a nie samego obiektu. Tak więc, na przykład: case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" endwyniki w: "Class!"To działa tak samo na 1.class, {}.classitp spada .class, mamy "String!"lub "Something else"dla tych różnych wartościach.
Lindes

219

Odbywa się to za pomocą caseRuby. Zobacz także „ Oświadczenie o zmianie” na Wikipedii.

Zacytowany:

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

Inny przykład:

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 stronie 123 The Ruby Programming Language (1st Edition, O'Reilly) na moim Kindle, napisano, że thensłowo kluczowe po whenklauzulach można zastąpić znakiem nowej linii lub średnikiem (tak jak w if then elseskładni). (Ruby 1.8 zezwala również na dwukropek zamiast then, ale ta składnia nie jest już dozwolona w Ruby 1.9.)


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

To jest odpowiedź, której użyłem, ponieważ definiuję zmienną na podstawie wyników przełączania wielkości liter. Zamiast wypowiadać type = #{score}każdą linię, mogę po prostu skopiować to, co zrobiłeś. O wiele bardziej elegancki Lubię też
jednowarstwowe

Wiem, że nie ma to związku z istotą odpowiedzi, ale 4 to także idealny kwadrat.
Nick Moore

109

skrzynia ... kiedy

Aby dodać więcej przykładów do odpowiedzi Chucka :

Z parametrem:

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

Bez parametru:

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

Należy pamiętać o „ Jak napisać instrukcję zmiany w Ruby ”, o której ostrzega kikito.


Dzięki, pomogło to w wielu opcjach w jednym wierszu. Próbowałem użyćor
sixty4bit

73

Wiele języków programowania, zwłaszcza tych pochodzących z C, obsługuje tak zwany Switch Fallthrough . Szukałem najlepszego sposobu, aby zrobić to samo w Ruby i pomyślałem, że może być przydatny dla innych:

W językach podobnych do C przegląd zwykle wygląda następująco:

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

W Ruby to samo można osiągnąć w następujący sposób:

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

Nie jest to ściśle równoważne, ponieważ nie jest możliwe 'a'wykonanie bloku kodu przed wpadnięciem do 'b'lub 'c', ale w przeważającej części uważam, że jest wystarczająco podobny, aby był użyteczny w ten sam sposób.


72

W Ruby 2.0 możesz również używać lambd w caseinstrukcjach, w następujący sposób:

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

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

Możesz także łatwo tworzyć własne komparatory za pomocą Struct z niestandardowym ===

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

(Przykład wzięty z „ Czy można używać procs z instrukcjami case w Ruby 2.0? ”.)

Lub z pełną klasą:

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

(Przykład zaczerpnięty z „ Jak działa instrukcja Ruby Case i co możesz z tym zrobić ”).


52

Możesz użyć wyrażeń regularnych, takich jak znalezienie typu ciągu:

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 caseużyje ===do tego operandu równości (dzięki @JimDeville). Dodatkowe informacje są dostępne w „ Ruby Operators ”. Można to również zrobić na przykładzie @mmdemirbas (bez parametru), tylko takie podejście jest czystsze dla tego typu przypadków.



33

Nazywa się casei działa tak, jak można się spodziewać, a także o wiele więcej zabawy dzięki uprzejmości, ===która implementuje testy.

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

Teraz trochę zabawy:

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

Okazuje się, że możesz również zastąpić dowolny łańcuch if / else (to znaczy nawet jeśli testy nie obejmują wspólnej zmiennej) case, pomijając caseparametr początkowy i zapisując wyrażenia, w których pierwsze dopasowanie jest tym, czego chcesz.

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 używa casedo pisania instrukcji switch.

Zgodnie z casedokumentacją:

Instrukcje case składają się z warunku opcjonalnego, który jest w pozycji argumentu case, i zero lub więcej whenklauzul. Pierwsza whenklauzula, która pasuje do warunku (lub do oceny logicznej prawdy, jeśli warunek jest pusty) „wygrywa”, a sekcja kodu jest wykonywana. Wartość instrukcji case jest wartością pomyślnej whenklauzuli lub niljeśli takiej klauzuli nie ma.

Instrukcja case może kończyć się elseklauzulą. Każda wheninstrukcja może mieć wiele wartości kandydujących, oddzielonych przecinkami.

Przykład:

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

Krótsza wersja:

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

A jako „ Deklaracja przypadku Ruby - zaawansowane techniki ” opisuje Ruby case;

Może być używany z zakresami :

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

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

Może być stosowany z Regex :

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

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

Może być stosowany z Procs i Lambdas :

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

## => "lambdas"

Ponadto można go używać z własnymi klasami meczów:

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

W zależności od przypadku możesz użyć skrótu metod.

Jeśli istnieje długa lista whens, a każda z nich ma konkretną wartość do porównania (nie interwał), skuteczniej będzie zadeklarować skrót metod, a następnie wywołać odpowiednią metodę z takiego skrótu.

# 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

Ponieważ switch casezawsze zwraca pojedynczy obiekt, możemy bezpośrednio wydrukować jego wynik:

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

20

Przypadek wielowartościowy, gdy i bez wartości:

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

I tutaj rozwiązanie wyrażeń regularnych :

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
dlaczego nie tylko case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(gdzie ,oznacza nowy wiersz)
Klamka

2
och, a pierwsza część jest już uwzględniona w tej odpowiedzi , a wiele odpowiedzi już wspomina o wyrażeniu regularnym. Szczerze mówiąc, ta odpowiedź nie dodaje nic nowego, a ja głosuję i głosuję za jej usunięciem.
Klamka

@DoorknobofSnow To pokazuje, że możesz użyć rozwiązania Regex i wartości oddzielonych przecinkami w przypadku przełączników. Nie jestem pewien, dlaczego to rozwiązanie daje ci tyle bólu.
123

więc jeśli dostali „F”, wiarygodną ocenę, to z ich winy w kodzie brakuje przypadku?
Mike Graf

Podoba mi się humor i fakt, że pokazuje, że możesz dopasować ciągi znaków do skrzynki.
szmergiel

13

W caseRuby możesz pisać wyrażenia na dwa różne sposoby:

  1. Podobne do serii ifstwierdzeń
  2. Określ cel obok, casea każda whenklauzula jest porównywana z celem.
age = 20
case 
when age >= 21
puts "display something"
when 1 == 0
puts "omg"
else
puts "default condition"
end

lub:

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

Chociaż Twój kod może odpowiedzieć na pytanie, powinieneś dodać przynajmniej krótki opis tego, co robi Twój kod i jak rozwiązuje początkowy problem.
user1438038

Rozważę tę wskazówkę w przyszłości.
ysk

10

Możesz to zrobić w bardziej naturalny sposób,

case expression
when condtion1
   function
when condition2
   function
else
   function
end

9

Wiele świetnych odpowiedzi, ale pomyślałem, że dodam jeden faktoid. Jeśli próbujesz porównać obiekty (klasy), upewnij się, że masz metodę statku kosmicznego (nie żart) lub zrozum, jak są porównywane

Ruby Equality And Object Porównanie ” to dobra dyskusja na ten temat.


7
Dla porównania, metoda „statek kosmiczny” <=>jest używana do zwracania wartości -1, 0, 1 lub zero w zależności od tego, czy porównanie zwraca odpowiednio mniej niż, równość, więcej niż lub nieporównywalnie. Wyjaśnia to dokumentacja modułu Ruby Porównywalna .
Tin Man

7

Jak stwierdzono w wielu powyższych odpowiedziach, ===operator jest używany pod maską case/ wheninstrukcji.

Oto dodatkowe informacje o tym operatorze:

Operator równości przypadków: ===

Wiele wbudowanych klas Ruby, takich jak String, Range i Regexp, zapewnia własne implementacje ===operatora, znanego również jako „równość wielkości liter”, „trzykrotnie równe” lub „trzykwale”. Ponieważ jest zaimplementowany inaczej w każdej klasie, będzie zachowywać się inaczej w zależności od typu obiektu, do którego został wywołany. Zasadniczo zwraca wartość true, jeśli obiekt po prawej „należy do” lub „jest członkiem” obiektu po lewej stronie. Na przykład można go użyć do przetestowania, czy obiekt jest instancją klasy (lub jednej z jego podklas).

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

Ten sam wynik można osiągnąć za pomocą innych metod, które prawdopodobnie najlepiej nadają się do pracy, takich jak is_a?i instance_of?.

Zakres realizacji ===

Gdy ===operator jest wywoływany na obiekcie zakresu, zwraca wartość true, jeśli wartość po prawej stronie mieści się w zakresie po lewej stronie.

(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

Pamiętaj, że ===operator wywołuje ===metodę obiektu po lewej stronie. Więc (1..4) === 3jest równoważna (1..4).=== 3. Innymi słowy, klasa operandu po lewej stronie określi, która implementacja ===metody zostanie wywołana, więc pozycje operandu nie są wymienne.

Wdrożenie Regexp ===

Zwraca true, jeśli ciąg po prawej stronie pasuje do wyrażenia regularnego po lewej.

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

Jedyną istotną różnicą między dwoma powyższymi przykładami jest to, że gdy występuje dopasowanie, ===zwraca true i =~zwraca liczbę całkowitą, która jest prawdą w Rubim. Wrócimy do tego wkrótce.


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
Przydaje się więcej, jeśli wyjaśnisz, dlaczego jest to preferowane rozwiązanie i wyjaśnisz, jak to działa. Chcemy edukować, a nie tylko dostarczać kod.
Tin Man


1

Zacząłem używać:

a = "secondcase"

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

puts var_name
>> "bar"

W niektórych przypadkach pomaga to w kompaktowaniu kodu.


1
Tego rodzaju kod powinien być zwykle wykonywany za Hashpomocą caseinstrukcji , a nie instrukcji.
Tom Lord

Używanie skrótu byłoby szybsze, gdy ten przełącznik stanie się duży.
Tin Man

1

Brak obsługi wyrażeń regularnych w twoim środowisku? Np. Shopify Script Editor (kwiecień, 2018):

[Błąd]: niezainicjowana stała RegExp

Obejście po kombinacji metod już wcześniej omawianych tutaj i tutaj :

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

Użyłem ors w instrukcji metody class, ponieważ ||ma wyższy priorytet niż .include?. Jeśli jesteś rubinowym nazistą , wyobraź sobie, że użyłem tego (item.include? 'A') || ...zamiast tego . test repl.it.


1

Bardzo ważne jest podkreślenie przecinka ( ,) w whenzdaniu. Działa jako ||o ifoświadczeniu, to znaczy, że robi LUB porównania, a nie I Porównanie ustalonych wyrażeń whenklauzuli. Zobacz następującą instrukcję przypadku:

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

xjest nie mniejsza niż 2, ale zwracana jest wartość "apple". Dlaczego? Ponieważ xbyło 3, a od ',`` acts as an|| , it did not bother to evaluate the expressionx <2 '.

Możesz pomyśleć, że aby wykonać operację AND , możesz zrobić coś takiego poniżej, ale to nie działa:

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

Nie działa, ponieważ (3 && x > 2)zwraca wartość true, a Ruby przyjmuje wartość True i porównuje ją xz ===, co nie jest prawdą, ponieważ xwynosi 3.

Aby dokonać &&porównania, będziesz musiał traktować casejak if/ elseblock:

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

W książce Ruby Programming Language Matz mówi, że ta ostatnia forma jest prostą (i rzadko używaną) formą, która jest niczym innym jak alternatywną składnią dla if/ elsif/ else. Jednak niezależnie od tego, czy jest to rzadko używane, czy nie, nie widzę innego sposobu na dołączenie wielu &&wyrażeń dla danej whenklauzuli.


Nie wydaje mi się to dobrym stylem kodowania. Używanie rzadkiej alternatywnej składni niepotrzebnie zaciemnia. Dlaczego nie użyć normalnego if...elsif? Wygląda na to, że próbujesz połączyć instrukcję przypadku i warunek. Dlaczego? Wystarczy umieścić warunek wewnątrz bloku when, np. when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey

0

Możemy napisać instrukcję switch dla wielu warunków.

Na przykład,

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
To nie zadziała; Ruby słowa kluczowe np. ( case, when, end) Są wrażliwe na wielkość liter i nie może być tak wielka.
sondra.kinsey

NoMethodError (undefined method CASE 'dla main: Object) `. Jak powiedział @ sondra.kinsey, nie można używać wielkich liter. Ruby pomyśli, że to STAŁA.
Tin Man

0

caseOperator stwierdzenie jest jak switchw innych językach.

Oto składnia switch...casew C:

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

Oto składnia case...whenw 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

Na przykład:

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

Aby uzyskać więcej informacji, zobacz casedokumentację.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.