Ruby Case 声明:返回哪位玩家获胜 — 石头、纸、剪刀 [英] Ruby Case Statement: Return which player won — Rock, Paper, Scissors

查看:38
本文介绍了Ruby Case 声明:返回哪位玩家获胜 — 石头、纸、剪刀的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

应对 Codewar 挑战:石头、纸、剪刀.目的是返回哪个玩家获胜!抽奖时返回抽奖!

例如:

rps('scissors','paper')//玩家 1 获胜!rps('scissors','rock')//玩家 2 赢了!rps('paper','paper')//画画!

我的代码:

def rps(p1, p2)案例 p1...p2当 (p1 == '摇滚' && p2 == '剪刀') ||(p1 == '剪刀' && p2 == '纸') ||(p1 == '纸' && p2 == '摇滚')返回玩家 1 获胜!"当 (p1 == '剪刀' && p2 == '摇滚') ||(p1 == '纸' && p2 == '剪刀') ||(p1 == '摇滚' && p2 == '纸')否则 (p1 == p2)返回绘制!"结尾结尾

结果:

 玩家 1 获胜预期:玩家 1 获胜!",而是得到:平局!"预期:玩家 1 获胜!",而是得到:平局!"预期:玩家 1 获胜!",而是得到:平局!"玩家 2 获胜预期:玩家 2 赢了!",而是得到:平局!"预期:玩家 2 赢了!",而是得到:平局!"预期:玩家 2 赢了!",而是得到:平局!"画测试通过:值 == 绘制!"测试通过:值 == 绘制!"测试通过:值 == 绘制!"

我需要在代码中更改什么才能返回Player 1 Won!"和玩家 2 赢了!"?

解决方案

Ruby 中的 case 表达式是这样工作的(请参阅第 11.5.2.2.4 节 caseISO/IEC 30170:2012信息技术——编程语言——的代码>表达式Ruby 规范了解详情):

case object_of_interest当类别 1some_expression当类别 2some_other_expression当类别 3另一个表达式别的an_entirely_different_expression结尾

(大致)等价于

temp = object_of_interest如果类别 1 === 温度some_expressionelsif 类别 2 === 温度some_other_expressionelsif category3 === temp另一个表达式别的an_entirely_different_expression结尾

=== 运算符(我称之为 case subsumption 运算符,但这是我编造的,不是官方术语)检查右侧操作数是否可以包含在接收器下.我是什么意思?想象一下,您有一个标有接收器的抽屉,将右侧操作数放入其中是否有意义?

所以,如果你有

foo === bar

这会询问如果我有一个标有 foo 的抽屉,将 bar 放入其中有意义吗?"或如果我解释 >foo 描述一个集合,bar 会是那个集合的成员吗?"

Object#的默认实现=== 或多或少只是简单的等式,它看起来有点像这样(另请参阅第 15.3.1.3.2 节 Kernel#=== ISO/IEC 30170:2012 Ruby 语言规范):

class 对象def ===(其他)等于?(其他)||自己==其他结尾结尾

这并不是很有帮助,因为它没有显示我所说的case subsumption是什么意思.为此,我们必须查看一些覆盖.例如,Module#===(另见 ISO/IEC 30170:2012 Ruby 语言规范的 15.2.2.4.5 Module#=== 部分):<块引用>

mod === objtruefalse

Case Equality——如果 objmod 的一个实例或者 mod 的一个实例,则返回 trueem>mod 的后代.对模块的使用有限,但可用于 case 语句以按类对对象进行分类.

所以等价于

class 模块def ===(其他)other.kind_of?(自我)结尾结尾

因此,它实现了如果我有一个名为 mod 的抽屉,将 other 放入其中是否有意义"的问题?因为Is othermod 的实例",我们可以看到它是有道理的:

Integer === 3 #=>真的整数 === '你好' #=>错误的字符串 === 3 #=>错误的字符串 === '你好' #=>真的

如果我有一个标有 Integer 的抽屉,将 3 放入其中是否有意义?是的,它确实.'Hello' 怎么样?不,它没有.

另一个例子是Range#===(另请参阅 ISO/IEC 30170:2012 Ruby 语言规范的第 15.2.14.4.2 Range#=== 部分):

<块引用>

rng === objtruefalse

返回 true 如果 obj 在范围的开始和结束之间,false 否则(与 cover? 相同)).

因此,它实现了如果我有一个名为 rng 的抽屉,将 other 放入其中是否有意义"的问题;如范围 rng 是否覆盖 other",我们可以看出这是有道理的:

0..2 === 1 #=>真的0..2 === 3 #=>错误的

如果我有一个标有 0..2 的抽屉,将 1 放入其中是否有意义?是的,它确实.3 怎么样?不,它没有.

最后一个例子是Regexp#===(另请参阅 ISO/IEC 30170:2012 Ruby 语言规范的 15.2.15.7.4 Regexp#=== 部分).想象一个描述与该正则表达式匹配的无限语言集的正则表达式:

/el+/=== 'Hello' #=>真的/el+/=== '世界' #=>错误的

如果我有一个标有 /el+/ 的抽屉,把 'Hello' 放在里面有意义吗?是的,它确实.'World' 怎么样?不,它没有.

所以,你的 case 表达式:

case p1...p2当 p1 == 'rock' &&p2 == '剪刀' ||p1 == '剪刀' &&p2 == '纸' ||p1 == '纸' &&p2 == '摇滚'返回玩家 1 获胜!"当 p1 == 'scissors' &&p2 == '摇滚' ||p1 == '纸' &&p2 == '剪刀' ||p1 == '摇滚' &&p2 == '纸'别的p1 == p2返回平局!"结尾

注意 else 没有条件.它的字面意思是在所有其他情况下",因此不需要条件.所以,p1 == p2 实际上是 else 块中的第一个表达式.但是,由于它没有副作用,并且完全忽略了它的值,因此它实际上根本没有任何作用.

让我们将其简化为:

case p1...p2当 something_that_is_either_true_or_false返回玩家 1 获胜!"当 something_that_is_either_true_or_false别的返回平局!"结尾

这相当于

temp = p1...p2if something_that_is_either_true_or_false === temp返回玩家 1 获胜!"elsif something_that_is_either_true_or_false === temp别的返回平局!"结尾

现在,根据您调用 rps 方法的准确程度,这些条件表达式可能是 truefalse,但我们不会不知道具体是哪个.然而,我们知道的是,TrueClass#===FalseClass#=== 覆盖 Object#===,因此它们仍然具有相同的语义:它们只是测试是否相等.所以,这实际上相当于

if something_that_is_either_true_or_false == p1...p2返回玩家 1 获胜!"elsif something_that_is_either_true_or_false == p1...p2别的返回平局!"结尾

事情是这样的:一个布尔值永远不会等于一个范围.他们甚至不是同一种类型!您在 case 表达式中有效地询问的是

如果我有一个标有 true 的抽屉,将 p1...p2 放入其中是否有意义?";或如果我有一个标有 false 的抽屉,将 p1...p2 放入其中是否有意义?"您遇到两种情况中的哪一种都没有关系,因为答案是不".在这两种情况下.

所以,事实上,不可能任何when子句永远> 是 true.因此,您的 case 表达式将始终评估 else 子句,或者换句话说,您的整个方法完全等同于

def rps(_, _) # 忽略所有参数'画!'结尾

case 表达式还有第二种形式:当你省略 object_of_interest 时,它就相当于一系列条件,就像这样:

case当条件 1some_expression当条件 2some_other_expression当有条件3另一个表达式别的an_entirely_different_expression结尾

(大致)等价于

if conditional1some_expressionelif 条件 2some_other_expressionelif 条件 3另一个表达式别的an_entirely_different_expression结尾

因此,修复您的方法的一种方法是简单地删除 p1...p2:

case # 这就是我们需要更改以使其工作的全部内容当 p1 == 'rock' &&p2 == '剪刀' ||p1 == '剪刀' &&p2 == '纸' ||p1 == '纸' &&p2 == '摇滚'返回玩家 1 获胜!"当 p1 == 'scissors' &&p2 == '摇滚' ||p1 == '纸' &&p2 == '剪刀' ||p1 == '摇滚' &&p2 == '纸'别的p1 == p2返回平局!"结尾

这就是我们需要改变的全部内容.只需删除该单个表达式即可.我们可以进一步简化它:首先,如上所述,我们可以删除 p1 == p2,因为它什么也不做.其次,case 表达式是一个表达式.这不是一个声明.(实际上,Ruby 中没有语句.一切都是表达式.)

因此,case 表达式求值为一个值,特别是,它求值为所采用的分支的值.所以,每当你看到类似

case foo当酒吧返回巴兹当 qux返回结尾

相当于

return case foo当酒吧巴兹当 qux弗罗布兹结尾

此外,在方法体(或块体、lambda 体、类定义体或模块定义体)中计算的最后一个表达式是整个方法(块、lambda、类定义、模块定义)的值,所以在您的情况下,return 在那里也是多余的.

最后,你的第二个 when 没有主体,所以它什么都不做,这意味着我们可以删除它.

这意味着整个方法变成了

case当 p1 == 'rock' &&p2 == '剪刀' ||p1 == '剪刀' &&p2 == '纸' ||p1 == '纸' &&p2 == '摇滚'玩家 1 赢了!"别的'画!'结尾

因为只有一个条件,所以它是一个case表达式根本没有意义,所以我们把它变成一个条件表达式:

if p1 == 'rock' &&p2 == '剪刀' ||p1 == '剪刀' &&p2 == '纸' ||p1 == '纸' &&p2 == '摇滚'玩家 1 赢了!"别的'画!'结尾

case 表达式解决这个问题的另一种方法是实际使用这样一个事实,即当没有被特别覆盖时,=== 只是意味着平等:

case [p1, p2]当 ['rock', 'scissors'], ['scissors', 'paper'], ['paper', 'rock']玩家 1 赢了!"当 ['scissors', 'rock'], ['paper', 'scissors'], ['rock', 'paper']玩家 2 赢了!"别的'画!'结尾

Working on a Codewar challenge: Rock, Paper,Scissors. The aim is to return which player won! In case of a draw return Draw!.

For example:

rps('scissors','paper') // Player 1 won!
rps('scissors','rock') // Player 2 won!
rps('paper','paper') // Draw!

my code:

def rps(p1, p2)

case p1...p2

when  (p1 == 'rock' && p2 == 'scissors') ||
      (p1 == 'scissors' && p2 == 'paper') ||
      (p1 == 'paper' && p2 == 'rock')
  return "Player 1 won!"
  
when  (p1 == 'scissors' && p2 == 'rock') ||
      (p1 == 'paper' && p2 == 'scissors') ||
      (p1 == 'rock' && p2 == 'paper')
else (p1 == p2)
  return "Draw!"
end
end

outcome:

 player 1 win
Expected: "Player 1 won!", instead got: "Draw!"
Expected: "Player 1 won!", instead got: "Draw!"
Expected: "Player 1 won!", instead got: "Draw!"
 player 2 win
Expected: "Player 2 won!", instead got: "Draw!"
Expected: "Player 2 won!", instead got: "Draw!"
Expected: "Player 2 won!", instead got: "Draw!"
 draw
Test Passed: Value == "Draw!"
Test Passed: Value == "Draw!"
Test Passed: Value == "Draw!"

What do I need to change in my code for it to return "Player 1 Won!" and "Player 2 Won!"?

解决方案

The case expression in Ruby works like this (see section 11.5.2.2.4 The case expression of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification for details):

case object_of_interest
when category1
  some_expression
when category2
  some_other_expression
when category3
  another_expression
else
  an_entirely_different_expression
end

is (roughly) equivalent to

temp = object_of_interest

if category1 === temp
  some_expression
elsif category2 === temp
  some_other_expression
elsif category3 === temp
  another_expression
else
  an_entirely_different_expression
end

The === operator (I call it the case subsumption operator, but that is something I made up, not an official term) checks whether the right-hand operand can be subsumed under the receiver. What do I mean by that? Imagine, you had a drawer labelled with the receiver, would it make sense to put the right-hand operand into it?

So, if you have

foo === bar

this asks "if I have a drawer labelled foo, would it make sense to put bar into it?", or "if I interpret foo as describing a set, would bar be a member of that set?"

The default implementation of Object#=== is more or less just simple equality, it looks a bit like this (see also section 15.3.1.3.2 Kernel#=== of the ISO/IEC 30170:2012 Ruby Language Specification):

class Object
  def ===(other)
    equals?(other) || self == other
  end
end

This is not exactly helpful, because it does not show what I mean by case subsumption. For that, we have to look at some of the overrides. For example, Module#=== (see also section 15.2.2.4.5 Module#=== of the ISO/IEC 30170:2012 Ruby Language Specification):

mod === objtrue or false

Case Equality—Returns true if obj is an instance of mod or an instance of one of mod's descendants. Of limited use for modules, but can be used in case statements to classify objects by class.

So, it is equivalent to

class Module
  def ===(other)
    other.kind_of?(self)
  end
end

So, it implements the question "If I had a drawer named mod, would it make sense to put other in it" as "Is other an instance of mod", and we can see that it makes sense:

Integer === 3       #=> true
Integer === 'Hello' #=> false

String  === 3       #=> false
String  === 'Hello' #=> true

If I have a drawer labelled Integer, does it make sense to put 3 in it? Yes, it does. What about 'Hello'? No, it does not.

Another example is Range#=== (see also section 15.2.14.4.2 Range#=== of the ISO/IEC 30170:2012 Ruby Language Specification):

rng === objtrue or false

Returns true if obj is between begin and end of range, false otherwise (same as cover?).

So, it implements the question "If I had a drawer named rng, would it make sense to put other in it" as "Does the range rng cover other", and we can see that it makes sense:

0..2 === 1 #=> true
0..2 === 3 #=> false

If I have a drawer labelled 0..2, does it make sense to put 1 in it? Yes, it does. What about 3? No, it does not.

The last example is Regexp#=== (see also section 15.2.15.7.4 Regexp#=== of the ISO/IEC 30170:2012 Ruby Language Specification). Imagine a Regexp describing an infinite set of languages that match that Regexp:

/el+/ === 'Hello' #=> true
/el+/ === 'World' #=> false

If I have a drawer labelled /el+/, does it make sense to put 'Hello' in it? Yes, it does. What about 'World'? No, it does not.

So, your case expression:

case p1...p2
when p1 == 'rock' && p2 == 'scissors' || p1 == 'scissors' && p2 == 'paper' || p1 == 'paper' && p2 == 'rock'
  return 'Player 1 won!'
when p1 == 'scissors' && p2 == 'rock' || p1 == 'paper' && p2 == 'scissors' || p1 == 'rock' && p2 == 'paper'
else
  p1 == p2
  return 'Draw!'
end

Note that else doesn't have a conditional. It literally means "in every other case", so there is no need for a conditional. So, the p1 == p2 is actually the first expression inside the else block. However, since it has no side-effects, and its value is completely ignored, it doesn't actually do anything at all.

Let's simplify that to:

case p1...p2
when something_that_is_either_true_or_false
  return 'Player 1 won!'
when something_that_is_either_true_or_false
else
  return 'Draw!'
end

This is equivalent to

temp = p1...p2
if    something_that_is_either_true_or_false === temp
  return 'Player 1 won!'
elsif something_that_is_either_true_or_false === temp
else
  return 'Draw!'
end

Now, depending on how exactly you call the rps method, those conditional expressions may be either true or false, but we don't know exactly which. What we do know, however, is that neither TrueClass#=== nor FalseClass#=== override Object#===, so they still have the same semantics: they simply test for equality. So, this is actually equivalent to

if    something_that_is_either_true_or_false == p1...p2
  return 'Player 1 won!'
elsif something_that_is_either_true_or_false == p1...p2
else
  return 'Draw!'
end

Here's the thing: a boolean will never be equal to a range. They aren't even the same type! What you are effectively asking in your case expression is

"If I have a drawer labelled true, does it make sense to put p1...p2 into it?" or "If I have a drawer labelled false, does it make sense to put p1...p2 into it?" It doesn't matter which of the two situations you have, because the answer is "No" in both cases.

So, there is, in fact, no possible way in which any of the when clauses can ever be true. Therefore, your case expression will always evaluate the else clause, or put another way, your entire method is completely equivalent to

def rps(_, _) # ignore all arguments
  'Draw!'
end

There is a second form of the case expression: when you leave out the object_of_interest, then it becomes equivalent to a series of conditionals, like this:

case
when conditional1
  some_expression
when conditional2
  some_other_expression
when conditional3
  another_expression
else
  an_entirely_different_expression
end

is (roughly) equivalent to

if conditional1
  some_expression
elsif conditional2
  some_other_expression
elsif conditional3
  another_expression
else
  an_entirely_different_expression
end

So, one way to fix your method, is to simply delete the p1...p2:

case # that is all we need to change to make it work
when p1 == 'rock' && p2 == 'scissors' || p1 == 'scissors' && p2 == 'paper' || p1 == 'paper' && p2 == 'rock'
  return 'Player 1 won!'
when p1 == 'scissors' && p2 == 'rock' || p1 == 'paper' && p2 == 'scissors' || p1 == 'rock' && p2 == 'paper'
else
  p1 == p2
  return 'Draw!'
end

That is literally all we need to change to make it work. Just delete that single expression. We can simplify it a bit further: first, as discussed above, we can remove the p1 == p2, because it doesn't do anything. Secondly, the case expression is an expression. It is not a statement. (In fact, there are no statements in Ruby. Everything is an expression.)

Ergo, the case expression evaluates to a value, in particular, it evaluates to the value of the branch that was taken. So, whenever you see something like

case foo
when bar
  return baz
when qux
  return frobz
end

that is equivalent to

return case foo
when bar
  baz
when qux
  frobz
end

Also, the last expression evaluated in a method body (or block body, lambda body, class definition body, or module definition body) is the value of the whole method (block, lambda, class definition, module definition), so in your case, the return is redundant there, too.

Lastly, your second when has no body, so it isn't doing anything, which means we can just remove it.

Which means the entire method becomes

case
when p1 == 'rock' && p2 == 'scissors' || p1 == 'scissors' && p2 == 'paper' || p1 == 'paper' && p2 == 'rock'
  'Player 1 won!'
else
  'Draw!'
end

Since there is only one condition, it doesn't really make sense for it to be a case expression at all, so we make it a conditional expression instead:

if p1 == 'rock' && p2 == 'scissors' || p1 == 'scissors' && p2 == 'paper' || p1 == 'paper' && p2 == 'rock'
  'Player 1 won!'
else
  'Draw!'
end

An alternative way of solving this with a case expression would be to actually use the fact that, when not specifically overridden, === just means equality:

case [p1, p2]
when ['rock', 'scissors'], ['scissors', 'paper'], ['paper', 'rock']
  'Player 1 won!'
when ['scissors', 'rock'], ['paper', 'scissors'], ['rock', 'paper']
'Player 2 won!'
else
  'Draw!'
end

这篇关于Ruby Case 声明:返回哪位玩家获胜 — 石头、纸、剪刀的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆