Могу ли я использовать сравнение между двумя переменными внутри оператора Ruby case? - PullRequest
0 голосов
/ 17 апреля 2020

Я пробую этот код, но каждый раз получаю ноль (это просто игра в Блэк Джек): это потому, что я сравниваю две переменные в выражении case?

def end_game_message(player_score, bank_score)
  message = ""
  case player_score
  when player_score == 21
    message = "Black Jack!"
  when player_score > bank_score
    message = "You win!"
  when player_score > 21
    message = "You lose!"
  when player_score < bank_score
    message = "You lose"
  when player_score == bank_score
    message = "Push"
  end
  message
end

puts end_game_message(21, 15)

Спасибо заранее за любую помощь!

Ответы [ 3 ]

4 голосов
/ 17 апреля 2020

IMO, вы должны использовать if вместо case в этом случае. Однако если вы хотите использовать case, код может выглядеть следующим образом:

def end_game_message(player_score, bank_score)
  case player_score
  when 21
    "Black Jack!"
  when -> s { s > bank_score }
    "You win!"
  when -> s { s > 21 }
    "You lose!"
  when -> s { s < bank_score }
    "You lose"
  when -> s { s == bank_score }
    "Push"
  end
end

puts end_game_message(21, 15)

Ключевым моментом является присвоение Proc объекта предложению when. В этом случае s - это player_score (значение, данное case clause`).

(И незначительное улучшение: оператор case возвращает значение, поэтому вам не нужно присваивать сообщение локальная переменная)

2 голосов
/ 17 апреля 2020
def end_game_message(player_score, bank_score)
  case player_score
  when 21
    bank_score == 21 ? "Push" : "Black Jack!"
  when (22..)
    "You lose!"
  when (..bank_score-1)
    bank_score <= 21 ? "You lose!" : "You win"
  when (bank_score+1..) 
    "You win!"
  else
    "Push"
  end
end

end_game_message(21,18)  #=> "Black Jack!" 
end_game_message(21,21)  #=> "Push" 
end_game_message(22,22)  #=> "You lose!" 
end_game_message(12,22)  #=> "You win!" 
end_game_message(18,19)  #=> "You lose!" 
end_game_message(19,18)  #=> "You win!" 
end_game_message(18,18)  #=> "Push" 

(..bank_score-1) и (bank_score+1..) являются бесконечными и бесконечными диапазонами , введенными в Ruby v2. 7 . Конечно, они не являются необходимыми, поскольку их можно заменить на (0..bank_score-1) и (bank_score+1..30). (...bank_score) можно использовать вместо (..bank_score-1), но мое собственное руководство по стилю гласит, что следует избегать трехточечных диапазонов, за исключением случаев, когда конец бесконечного диапазона должен быть исключен.

1 голос
/ 17 апреля 2020

На самом деле в вашем вопросе есть несколько разных вопросов.

Первый в заголовке:

Могу ли я использовать сравнение между двумя переменными внутри оператора Ruby case?

И ответ на этот вопрос "Да, конечно, почему бы вам не?" Вы можете использовать любое Ruby выражение .

Хорошо, хорошо, если мы хотим быть по-настоящему педантичными c, ответ будет "Нет" по двум причинам:

  • Вы не можете сравнивать переменные, вы можете только сравнивать объекты, а переменные не являются объектами.
  • Ruby не имеет оператора case, только case выражение . (На самом деле, Ruby вообще не имеет операторов .)

(Это может звучать слишком педантично c, но понимание различия между переменными и объектами фундаментальный не только в Ruby, но и в программировании в целом , как и разница между выражениями и операторами.)

Ваш следующий вопрос: почему вы получаете nil. Ответ заключается в том, что последнее выражение в вашем коде -

puts end_game_message(21, 15)

Итак, ваш код оценивается как возвращаемое значение Kernel#puts, а Kernel#puts определено как всегда return nil.

Вопрос, который, я думаю, вы действительно задаете, но на самом деле не в вашем вопросе: «Почему выражение case не работает так, как я думаю? делает? "

Вам просто нужно запомнить, как вычисляется выражение case. Существуют две разные формы выражения case. (См. раздел 11.5.2.2.4 case выражение ) спецификации языка ISO Ruby , например.

Более простая форма - это то, что Спецификация языка ISO Ruby вызывает case выражение без выражения , которое выглядит следующим образом:

case               # Look ma, no expression
when bar then :bar
when baz then :baz
else          :qux
end

И оценивается следующим образом:

if    bar then :bar
elsif baz then :baz
else           :qux
end

Другой формой является case -выражение с выражением

case foo           # In this case, there is an expression here
when bar then :bar
when baz then :baz
else          :qux
end

, которое оценивается так:

if    bar === foo then :bar
elsif baz === foo then :baz
else                   :qux
end

В вашем случае , вы используете case -expression-with-expression , поэтому ваше выражение case оценивается так:

if    (player_score == 21)         === player_score
  message = "Black Jack!"
elsif (player_score > bank_score)  === player_score
  message = "You win!"
elsif (player_score > 21)          === player_score
  message = "You lose!"
elsif (player_score < bank_score)  === player_score
  message = "You lose"
elsif (player_score == bank_score) === player_score
  message = "Push"
end

С player_score == 21, player_score > bank_score, player_score > 21, player_score < bank_score и player_score == bank_score оцениваются как true или false, а player_score - это число, все ветви в вашем выражении case фактически проверяют что-то вроде этого:

some_boolean === some_number

Что никогда true!

Итак, как мы можем это исправить?

Простейшим решением было бы использование case -expression-withou Вместо этого t-выражение , и для этого единственное, что нам нужно сделать, - это удалить выражение после case:

case
when player_score == 21
  message = "Black Jack!"
when player_score > bank_score
  message = "You win!"
when player_score > 21
  message = "You lose!"
when player_score < bank_score
  message = "You lose"
when player_score == bank_score
  message = "Push"
end

. Это будет оценено следующим образом:

if    player_score == 21
  message = "Black Jack!"
elsif player_score > bank_score
  message = "You win!"
elsif player_score > 21
  message = "You lose!"
elsif player_score < bank_score
  message = "You lose"
elsif player_score == bank_score
  message = "Push"
end

Что будет делать то, что вы хотите.

Если вы хотите оставить выражение case с выражением , вам нужно убедиться, что when -аргумент каждого when -класса - это то, что отвечает на === таким образом, что имеет смысл для вашего сравнения.

Мы могли бы, для Например, используйте Range с и Integer с. Метод Range#=== проверяет, находится ли аргумент внутри Range, а метод Integer#=== проверяет, является ли аргумент численно равным, поэтому мы можем переписать case выражение, подобное этому:

case player_score
when 21
  message = "Black Jack!"
when ((bank_score + 1)..)
  message = "You win!"
when (22..)
  message = "You lose!"
when ...bank_score
  message = "You lose"
when bank_score
  message = "Push"
end

Поскольку дела оцениваются сверху вниз, и вы можете оценить несколько условий в одном и том же случае, мы можем немного переставить их, чтобы сделать их приятнее для чтения:

case player_score
when 21
  message = "Black Jack!"
when bank_score
  message = "Push"
when 21.., ...bank_score
  message = "You lose!"
when (bank_score..)
  message = "You win!"
end

Также помните, что выражение case является выражением , что означает, что оно оценивается как значение, а именно, по значению ветви, которая была оценена. Поэтому мы можем «вытянуть» присвоение message:

message = case player_score
when 21
  "Black Jack!"
when bank_score
  "Push"
when 21.., ...bank_score
  "You lose!"
when (bank_score..)
  "You win!"
end

Обратите внимание, что это имеет немного семантику, отличную от той, что была у нас ранее: Раньше присвоение оценивалось только тогда, когда оценивалась одна из ветвей, в противном случае значение message оставалось прежним , Принимая во внимание, что в этой форме присваивание всегда оценивается, и если ни одна из ветвей не оценивается, выражение case оценивается как nil.

Однако способ case выражение записано, все случаи покрыты, поэтому всегда будет присваиваться не nil значение.

Как только мы выполнили это преобразование, мы заметим, что в начале метода присваивается сообщение пустой строке "", а затем немедленно присваивается другое значение. Таким образом, мы можем избавиться от первого присваивания, так как его эффекты в любом случае немедленно перезаписываются.

И как только мы это сделаем, мы увидим, что мы присваиваем переменной message и затем немедленно возвращаем ее. Таким образом, мы могли бы с таким же успехом вернуть значение выражения case, например:

def end_game_message(player_score, bank_score)
  case player_score
  when 21
    "Black Jack!"
  when bank_score
    "Push"
  when 21.., ...bank_score
    "You lose!"
  when (bank_score..)
    "You win!"
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...