Как написать оператор switch в Ruby

Ответы:

2671

Ruby вместо этого использует 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 caseнет провала , поэтому нет необходимости заканчивать каждое whenсимволом a break. Вы также можете указать несколько совпадений в одном whenпредложении, как when "foo", "bar".

цыпленок
источник
12
Вы также можете сделать регулярное выражение для переданного аргумента: когда / thisisregex / следующая строка ставит «Это найденное совпадение с номером 1 # {$ 1}» end
Automatico
8
Также стоит отметить, что вы можете сократить свой код, поместив оператор whenand returnв одну строку:when "foo" then "bar"
Александр - Восстановите Монику
9
Важное замечание: В отличие от switchоператоров во многих других языках, Ruby's caseне имеет провала , поэтому нет необходимости заканчивать каждое whenсимволом a break.
Янник
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

Я столкнулся с этой проблемой сегодня, когда искал ответ, и это была первая появившаяся страница, поэтому я подумал, что она будет полезна другим в моей ситуации.

kikito
источник
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и т.д. Dropping.class , мы получаем "String!"или "Something else"для этих различных значений.
Линдс
219

Это делается с помощью caseRuby. Также см. « Смена оператора » в Википедии.

Цитируется:

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.)

nonopolarity
источник
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

Пожалуйста, имейте в виду " Как написать оператор switch в Ruby ", о котором kikito предупреждает.

mmdemirbas
источник
Спасибо, это было полезно для нескольких вариантов на одной строке. Я пытался использоватьor
sixty4bit
73

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

В C-подобных языках падение обычно выглядит так:

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

(Пример взят из « Можно ли использовать процы с операторами case в 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

(Пример взят из статьи « Как работает оператор Ruby и что вы можете с ним сделать ».)

Джеймс Лим
источник
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

Ruby's caseбудет использовать ===для этого операнд равенства (спасибо @JimDeville). Дополнительная информация доступна на " Рубин Операторы ". Это также можно сделать с помощью примера @mmdemirbas (без параметра), только этот подход более чист для таких случаев.

Харис Краина
источник
34

Если вы хотите знать, как использовать условие ИЛИ в случае переключения Ruby:

Таким образом, в caseутверждении a ,является эквивалентом ||в ifутверждении.

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

Смотрите " Как работает Ruby Case case и что вы можете с ним сделать ".

Маниш Шривастава
источник
форматирование кода не работает в связанной статье :-)
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
DigitalRoss
источник
23

Ruby использует caseдля написания операторов switch.

Согласно caseдокументации:

Операторы case состоят из необязательного условия, которое находится в позиции аргумента case, и нуля или более whenпредложений. Первое whenпредложение, которое соответствует условию (или оценивается как логическая истина, если условие имеет значение null) «выигрывает», и выполняется его кодовая часть. Значением оператора case является значение успешного whenпредложения, илиnil если такого предложения нет.

Оператор case может заканчиваться 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!"

Может использоваться с Procs и Lambdas :

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
Lahiru
источник
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
123
источник
2
почему не просто case some_string, when /\d/, (stuff), when /[a-zA-Z]/, (stuff), end(где ,означает
перевод
2
о, и первая часть уже покрыта этим ответом , и многие ответы уже упоминают регулярное выражение. Честно говоря, этот ответ не добавляет ничего нового, и я голосую и голосую, чтобы удалить его.
tckmn
@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
YSK
источник
Хотя ваш код может ответить на этот вопрос, вы должны добавить хотя бы краткое описание того, что делает ваш код и как он решает начальную проблему.
user1438038
Я рассмотрю этот совет в будущем.
YSK
10

Вы можете сделать это более естественным образом,

case expression
when condtion1
   function
when condition2
   function
else
   function
end
Navin
источник
9

Много хороших ответов, но я подумал, что добавлю один фактоид. Если вы пытаетесь сравнивать объекты (классы), убедитесь, что у вас есть метод космического корабля (а не шутка) или вы понимаете, как они сравниваются

" Рубиновое равенство и сравнение объектов " - хорошая дискуссия на эту тему.

jmansurf
источник
7
Для справки, метод «космический корабль» - это метод <=>, который используется для возврата -1, 0, 1 или nil в зависимости от того, возвращает ли сравнение меньше, равно, больше или не сопоставимо соответственно. В документации Ruby Comparable module это объясняется.
Жестянщик
7

Как указано во многих из приведенных выше ответов, ===оператор используется под капотом на case/when операторов.

Вот дополнительная информация об этом операторе:

Оператор равенства случая: ===

Многие из встроенных в Ruby классов, такие как String, Range и Regexp, предоставляют свои собственные реализации ===оператора, также известные как «case-равенства», «тройное равенство» или «тройное равенство». Поскольку он реализован по-разному в каждом классе, он будет вести себя по-разному в зависимости от типа объекта, к которому он был вызван. Как правило, он возвращает true, если объект справа «принадлежит» или «является членом» объекта слева. Например, его можно использовать для проверки того, является ли объект экземпляром класса (или одного из его подклассов).

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

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

Диапазон реализации ===

Когда ===оператор вызывается для объекта диапазона, он возвращает истину, если значение справа попадает в диапазон слева.

(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. Мы скоро вернемся к этому.

BrunoFacca
источник
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"

Это помогает компактный код в некоторых случаях.

deepfritz
источник
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 в выражении метода класса, поскольку ||имеет более высокий приоритет, чем .include?. Если вы рубин-нацист , пожалуйста, представьте, что я использовал это (item.include? 'A') || ...вместо этого. Репл.ит тест.

CPHPython
источник
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 '.

Вы можете подумать, что для выполнения AND , вы можете сделать что-то вроде этого ниже, но это не работает:

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

Это не работает, потому что (3 && x > 2)оценивается как true, а Ruby принимает значение True и сравнивает его xс значением ===, которое не соответствует истине, поскольку xравно 3.

Чтобы сделать &&сравнение, вам нужно рассматривать caseкак if/ elseблок:

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

В книге Ruby Programming Language Матц говорит, что эта последняя форма является простой (и редко используемой) формой, которая является не чем иным, как альтернативным синтаксисом для if/ elsif/ else. Однако, используется ли это редко или нет, я не вижу другого способа присоединить несколько &&выражений для данного whenпредложения.

Донато
источник
Это не похоже на хороший стиль кодирования для меня. Использование редкого альтернативного синтаксиса излишне запутывает. Почему бы не использовать нормальный if...elsif? Похоже, вы пытаетесь смешать заявление случая и условие. Почему? Просто поместите условную внутри группы При блоке, например. when 3; ( x < 2 ) ? 'apple' : 'orange'
sondra.kinsey
0

Мы можем написать оператор switch для нескольких условий.

Например,

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 CASE 'для main: Object) `. Как сказал @ 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Документацию.

Ben96
источник