Ruby Case 声明:返回哪位玩家获胜 — 石头、纸、剪刀 [英] Ruby Case Statement: Return which player won — Rock, Paper, Scissors
问题描述
应对 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 节 case
ISO/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 === obj
→ true
或 false
Case Equality——如果 obj 是 mod 的一个实例或者 mod 的一个实例,则返回 true
em>mod 的后代.对模块的使用有限,但可用于 case 语句以按类对对象进行分类.
所以等价于
class 模块def ===(其他)other.kind_of?(自我)结尾结尾
因此,它实现了如果我有一个名为 mod
的抽屉,将 other
放入其中是否有意义"的问题?因为Is other
是 mod
的实例",我们可以看到它是有道理的:
Integer === 3 #=>真的整数 === '你好' #=>错误的字符串 === 3 #=>错误的字符串 === '你好' #=>真的
如果我有一个标有 Integer
的抽屉,将 3
放入其中是否有意义?是的,它确实.'Hello'
怎么样?不,它没有.
另一个例子是Range#===
(另请参阅 ISO/IEC 30170:2012 Ruby 语言规范的第 15.2.14.4.2 Range#===
部分):
rng === obj
→ true
或 false
返回 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
方法的准确程度,这些条件表达式可能是 true
或 false
,但我们不会不知道具体是哪个.然而,我们做知道的是,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 === obj
→true
orfalse
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 === obj
→true
orfalse
Returns
true
if obj is between begin and end of range,false
otherwise (same ascover?
).
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屋!