Як написати оператор переключення в Ruby


Відповіді:


2669

Рубі замість цього використовує caseвираз .

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 порівнює об’єкт у whenпропозиції з об'єктом у caseпункті за допомогою ===оператора. Наприклад 1..5 === x, і ні x === 1..5.

Це дозволяє скласти складні whenпропозиції, як видно вище. Діапазони, класи та всілякі речі можуть бути перевірені, а не просто рівність.

На відміну від switchвисловлювань на багатьох інших мовах, у Ruby's caseнемає прохідних даних , тому немає необхідності закінчувати кожну whenз а break. Ви також можете вказати кілька збігів у одному whenпункті, як when "foo", "bar".


12
Ви також можете виконати регулярний вираз на переданому аргументі: коли / thisisregex / наступний рядок ставить "Це знайдений збіг № 1 # {$ 1}" кінець
Automatico

8
Також варто відзначити, що ви можете скоротити код, поставивши whenта returnзаяву в той самий рядок:when "foo" then "bar"
Олександр - Відновіть Моніку

9
Важливо: На відміну від switchвисловлювань на багатьох інших мовах, у Ruby's caseНЕ є прохід , тому немає потреби закінчувати кожну whenз а break.
janniks

3
Стільки голосів набрали ще й навіть згадки про ключове слово then. Будь ласка, дивіться також інші відповіді.
Клінт Пахл

442

case...whenповодиться трохи несподівано при обробці занять. Це пов’язано з тим, що ним користується ===оператор.

Цей оператор працює, як очікувалося, з літералами, але не з класами:

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

Це означає, що якщо ви хочете зробити case ... whenнад класом об'єкта, це не вийде:

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

Буде надруковано "Це не рядок або число".

На щастя, це легко вирішити. ===Оператор був визначений так , що він повертається , trueякщо ви використовуєте його з класом і поставити екземпляр цього класу в якості другого операнда:

Fixnum === 1 # => true

Коротше кажучи, код вище можна виправити, видаливши .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

Я сьогодні потрапив на цю проблему, шукаючи відповіді, і це була перша сторінка, що з’явилася, тому я зрозумів, що це буде корисно іншим у моїй самій ситуації.


obj = 'привіт'; case obj; коли "привіт" потім ставить кінець "Це привіт"
Sugumar Venkatesan

Взяти .classучасть у цьому цікаво відзначити, дякую. Звичайно, це цілком відповідна поведінка (хоча я міг бачити, як це може бути звичайною помилкою думати, що це надрукує It is a string) ... ви тестуєте клас якогось довільного об'єкта, а не самого об'єкта. Так, наприклад: case 'hello'.class when String then "String!" when Class then "Class!" else "Something else" endпризводить до: "Class!"Це працює так само 1.class, {}.classі т. Д. Відкидаючи .class, ми отримуємо "String!"або "Something else"для цих різних значень.
лінди

219

Це робиться за допомогою caseв Ruby. Також дивіться " Переключення заяви " у Вікіпедії.

Цитується:

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

Ще один приклад:

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

На сторінці 123 на мові програмування Ruby (1-е видання, O'Reilly) на моєму Kindle написано, що thenключове слово, що слідує за whenпунктами, може бути замінено на новий рядок або крапку з комою (як у if then elseсинтаксисі). (Ruby 1.8 також дозволяє двокрапку замість then, але цей синтаксис більше не дозволений у Ruby 1.9.)


38
when (-1.0/0.0)..-1 then "Epic fail"
Ендрю Грімм

Це відповідь, яку я використав, тому що я визначаю змінну на основі результатів перемикання випадку. Замість того, щоб сказати type = #{score}кожен рядок, я можу просто скопіювати те, що ви зробили. Набагато елегантніше мені також подобаються однолінійки набагато краще (якщо можливо)
onebree

Я знаю, що це не пов'язане з суттю відповіді, але 4 - це також ідеальний квадрат.
Нік Мур

109

випадок ... коли

Щоб додати більше прикладів до відповіді Чака :

З параметром:

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

Без параметра:

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

Будь ласка, пам’ятайте про те, як написати заяву про перемикання в Ruby, про яку попереджає кікіто.


Дякую, це було корисно, коли в одному рядку було кілька варіантів. Я намагався використатиor
шістдесят біт

73

Багато мов програмування, особливо ті, що походять від C, підтримують так званий Switch Fallthrough . Я шукав найкращий спосіб зробити те ж саме в Ruby і думав, що це може бути корисно іншим:

На мовах, подібних С, прорив зазвичай виглядає так:

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

У Ruby цього можна досягти наступним чином:

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

Це не є рівнозначним, тому що неможливо дозволити 'a'виконувати блок коду перед тим, як потрапити на 'b'або 'c', але, здебільшого, я вважаю, що це досить схоже, щоб бути корисним таким же чином.


72

У Ruby 2.0 ви також можете використовувати лямбдати у caseвисловлюваннях:

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

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

Ви також можете легко створити власні компаратори, використовуючи Struct із користувачем ===

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

(Приклад взято з " Чи можна використовувати програми з операторами справи в Ruby 2.0? ".)

Або з повним класом:

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

(Приклад взятий із " Як працює випадок із справою Рубі і що з цим можна зробити ".)


52

Ви можете використовувати регулярні вирази, такі як пошук типу рядка:

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

Рубі caseвикористає ===для цього операнд рівності (спасибі @JimDeville). Додаткову інформацію можна отримати у " Рубінових операторів ". Це також можна зробити, використовуючи приклад @mmdemirbas (без параметра), лише такий підхід є більш чистим для цих типів випадків.


34

Якщо ви прагнете знати, як використовувати умову АБО у випадку переключення Ruby:

Отже, у caseвикладі а ,є еквівалентом ||у ifтвердженні.

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

Див. " Як працює випадок із справою" Рубі "та що з цим можна зробити ".


Форматування коду не працює у зв’язаній статті :-)
froderik

33

Це називається, caseі він працює так, як ви очікували, плюс ще багато веселощів, люб'язність ===яких реалізує тести.

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

Тепер для веселощів:

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

І виявляється, ви також можете замінити довільний ланцюг if / else (тобто навіть якщо тести не включають загальну змінну) caseна, виключивши початковий caseпараметр і просто записавши вирази, де перший збіг - це те, що ви хочете.

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 використовує caseдля написання операторів переключення.

Відповідно до caseдокументації:

Заяви справи складаються з необов'язкової умови, яка перебуває в позиції аргументу caseта нульових або більше whenпропозицій. Перший whenпункт, який відповідає умові (або для оцінки булевої істини, якщо умова є нульовою), "виграє", і його строфа коду виконується. Значення твердження справи - це значення успішної whenпропозиції, або nilякщо такого немає.

Заява справи може закінчуватися elseпунктом. Кожен whenвислів може мати декілька значень кандидата, розділених комами.

Приклад:

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

Коротша версія:

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

І як " твердження справи Рубі - передові методики " описує Рубі case;

Можна використовувати з діапазонами :

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

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

Можна використовувати з Regex :

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

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

Можна використовувати з програмами та лямбдами :

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

## => "lambdas"

Також можна використовувати з власними класами матчів:

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

Залежно від вашого випадку, ви можете скористатися хеш-методами.

Якщо існує довгий список whens і кожен з них має конкретне значення для порівняння (не інтервал), то ефективніше буде оголосити хеш методів, а потім викликати відповідний метод із такого хеша.

# 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

Оскільки switch caseзавжди повертається один об'єкт, ми можемо безпосередньо надрукувати його результат:

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

20

Багатозначне значення, коли і без значення:

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

І рішення регулярного вираження тут:

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
чому б не просто case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(де ,означає новий рядок)
Дорноб

2
о, і перша частина вже висвітлена в цій відповіді , і багато відповідей вже згадують регулярний вираз. Відверто кажучи, ця відповідь не додає нічого нового, і я схиляюсь і голосую, щоб її видалити.
Дверноруб

@DoorknobofSnow Це означає, що ви можете використовувати рішення Regex і значення, відокремлені комами, у випадку вимикача. Не впевнений, чому рішення так сильно болить.
123

Так що, якщо вони отримали "F", законний клас, їхня вина, у вашому коді відсутня справа?
Майк Граф

Мені подобається гумор цього, і той факт, що він демонструє, що ви можете зіставити рядки до справи.
наждак

13

Ви можете писати caseвирази двома різними способами в Ruby:

  1. Подібно до серії ifтверджень
  2. Вкажіть ціль поруч із, caseа кожен whenпункт порівнюється з цільовим.
age = 20
case 
when age >= 21
puts "display something"
when 1 == 0
puts "omg"
else
puts "default condition"
end

або:

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

Хоча ваш код може відповісти на питання, вам слід додати хоча б короткий опис того, що робить ваш код і як він вирішує початкову проблему.
користувач1438038

Я буду розглядати цю пораду в майбутньому.
іск

10

Ви можете зробити це як більш природний спосіб,

case expression
when condtion1
   function
when condition2
   function
else
   function
end

9

Дуже багато чудових відповідей, але я подумав, що я додам один фактоїд. Якщо ви намагаєтеся порівняти об'єкти (Класи), переконайтеся, що у вас є метод космічного корабля (не на жарт) або зрозумійте, як їх порівнюють

" Рубійна рівність та порівняння об'єктів " - це хороша дискусія з цієї теми.


7
Для довідки, "космічний корабель" метод <=>, який використовується для повернення -1, 0, 1 або нуля, залежно від того, повернення порівняно менше, рівне, більше, або не порівнянне. Порівнянна документація модуля Ruby пояснює це.
Олов'яний чоловік

7

Як зазначено у багатьох вищезазначених відповідях, ===оператор використовується під капотом on case/ whenинструкції.

Ось додаткова інформація про цього оператора:

Оператор рівності випадків: ===

Багато вбудованих класів Ruby, такі як String, Range та Regexp, забезпечують власні реалізації ===оператора, також відомі як "case-рівність", "потрійні рівні" або "threequals". Оскільки він реалізується по-різному в кожному класі, він буде поводитися по-різному в залежності від типу об'єкта, на який він був викликаний. Як правило, він повертає істину, якщо об’єкт праворуч "належить" або "є членом" об'єкта зліва. Наприклад, з його допомогою можна перевірити, чи об’єкт є екземпляром класу (або одним із його підкласів).

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

Такого ж результату можна досягти і з іншими методами, які, ймовірно, найкраще підходять для роботи, наприклад, is_a?та instance_of?.

Діапазон Впровадження ===

Коли ===оператор викликається об'єктом діапазону, він повертає значення true, якщо значення праворуч падає в діапазон зліва.

(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

Пам'ятайте, що ===оператор викликає ===метод лівого об’єкта. Так що (1..4) === 3еквівалентно (1..4).=== 3. Іншими словами, клас лівого операнда визначатиме, яка реалізація ===методу буде називатися, тому позиції операндів не є взаємозамінними.

Впровадження ===

Повертає значення true, якщо рядок праворуч відповідає регулярному виразу зліва.

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

Єдина відповідна відмінність між двома наведеними вище прикладами полягає в тому, що, коли є збіг, ===повертається true та =~повертається ціле число, яке є тривале значення в Ruby. Ми повернемося до цього незабаром.


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
Це допомагає більше, якщо ви надасте пояснення, чому це найкраще рішення та поясніть, як воно працює. Ми хочемо вчити, а не просто надавати код.
Олов'яний чоловік


1

Я почав використовувати:

a = "secondcase"

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

puts var_name
>> "bar"

Це допомагає компактному коду в деяких випадках.


1
Код, подібний до цього, зазвичай слід робити, використовуючи Hash, а не caseвиписку.
Том Лорд

Використання хешу було б швидше, коли цей перемикач стає більшим.
Олов'яний чоловік

1

Немає підтримки регулярних виразів у вашому оточенні? Наприклад, редактор сценаріїв Shopify (квітень 2018 р.):

[Помилка]: неініціалізована константа RegExp

Вирішення за комбінацією методів, уже розглянутих тут і тут :

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

Я використовував ors в операторі методу class, оскільки ||має більшу перевагу ніж .include?. Якщо ви рубін-нацист , будь ласка, уявіть, що я використовував це (item.include? 'A') || ...замість цього. тест repl.it


1

Надзвичайно важливо підкреслити коми ( ,) у whenпункті. Він діє як ||з ifзаяви, тобто, він робить або порівняння і не І порівняння між роздільниками виразів whenпункту. Дивіться таку заяву справи:

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

xне менше 2, але повертається значення "apple". Чому? Тому що xбуло 3, а з ',`` acts as an|| , it did not bother to evaluate the expressionх <2 '.

Ви можете подумати, що для виконання І , ви можете зробити щось подібне нижче, але це не працює:

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

Це не працює, оскільки (3 && x > 2)оцінюється як істинне, і Рубі приймає значення True і порівнює його xз ===, що не відповідає дійсності, оскільки xце 3.

Для &&порівняння вам доведеться ставитися caseяк до if/ elseblock:

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

В Рубі Мова програмування книги, маци кажуть , що ця остання форма є простою (і рідко використовується) форма, яка є не більше ніж альтернативний синтаксис if/ elsif/ else. Однак, нечасто він використовується чи ні, я не бачу іншого способу приєднання декількох &&виразів до даного whenпункту.


Мені це не здається гарним стилем кодування. Використання рідкісного альтернативного синтаксису непотрібно обтяжує. Чому б не використовувати нормальний if...elsif? Здається, ви намагаєтесь змішати випадок справи та умову. Чому? Просто поставте умовне всередину блок коли, наприклад. when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey

0

Ми можемо написати оператор переключення для декількох умов.

Наприклад,

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
Це не спрацює; Рубінові ключові слова (наприклад. case, when, end) Чутливі до регістру і не може бути в верхньому регістрі , як це.
sondra.kinsey

NoMethodError (undefined method СЛУЧАЙ "для основного: Об'єкт)`. Як сказав @ sondra.kinsey, ви не можете використовувати великі регістри. Рубі подумає, що це КОНСТАНТ.
Олов'яний чоловік

0

caseОператор оператор, як і switchв інших мовах.

Це синтаксис switch...caseв C:

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

Це синтаксис case...whenу 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

Наприклад:

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

Для отримання додаткової інформації див caseдокументацію.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.