Scala:使用模式匹配检测 5 张牌扑克牌中的顺子
对于那些不知道什么是 5 张牌扑克顺子的人:http://en.wikipedia .org/wiki/List_of_poker_hands#Straight
我正在用 Scala 编写一个小型扑克模拟器来帮助我学习该语言,并且我创建了一个手牌类中有 5 张有序的卡片。每张卡牌都有一个等级和花色,两者都定义为枚举。 Hand 类具有评估手牌等级的方法,其中之一检查手牌是否包含顺子(我们暂时可以忽略顺子)。我知道有一些很好的算法可以用来确定顺子,但我想看看是否可以使用 Scala 的模式匹配来设计一些东西,所以我想出了以下方法:
def isStraight() = {
def matchesStraight(ranks: List[Rank.Value]): Boolean = ranks match {
case head :: Nil => true
case head :: tail if (Rank(head.id + 1) == tail.head) => matchesStraight(tail)
case _ => false
}
matchesStraight(cards.map(_.rank).toList)
}
效果很好并且具有相当的可读性,但我想知道是否有有什么方法可以摆脱这个if。我会想象类似以下的东西,尽管我无法让它发挥作用:
private def isStraight() = {
def matchesStraight(ranks: List[Rank.Value]): Boolean = ranks match {
case head :: Nil => true
case head :: next(head.id + 1) :: tail => matchesStraight(next :: tail)
case _ => false
}
matchesStraight(cards.map(_.rank).toList)
}
有什么想法吗?另外,作为一个附带问题,对内部 matchesStraight 定义的普遍看法是什么?这应该是私人的还是以不同的方式完成?
For those who don't know what a 5-card Poker Straight is: http://en.wikipedia.org/wiki/List_of_poker_hands#Straight
I'm writing a small Poker simulator in Scala to help me learn the language, and I've created a Hand class with 5 ordered Cards in it. Each Card has a Rank and Suit, both defined as Enumerations. The Hand class has methods to evaluate the hand rank, and one of them checks whether the hand contains a Straight (we can ignore Straight Flushes for the moment). I know there are a few nice algorithms for determining a Straight, but I wanted to see whether I could design something with Scala's pattern matching, so I came up with the following:
def isStraight() = {
def matchesStraight(ranks: List[Rank.Value]): Boolean = ranks match {
case head :: Nil => true
case head :: tail if (Rank(head.id + 1) == tail.head) => matchesStraight(tail)
case _ => false
}
matchesStraight(cards.map(_.rank).toList)
}
That works fine and is fairly readable, but I was wondering if there is any way to get rid of that if. I'd imagine something like the following, though I can't get it to work:
private def isStraight() = {
def matchesStraight(ranks: List[Rank.Value]): Boolean = ranks match {
case head :: Nil => true
case head :: next(head.id + 1) :: tail => matchesStraight(next :: tail)
case _ => false
}
matchesStraight(cards.map(_.rank).toList)
}
Any ideas? Also, as a side question, what is the general opinion on the inner matchesStraight definition? Should this rather be private or perhaps done in a different way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您不能将信息传递给提取器,也不能使用一个值在另一个值中返回的信息,除了
if
语句 - 该语句涵盖了所有这些情况。您可以做的是创建自己的提取器来测试这些东西,但如果没有任何重用,它不会给您带来太多好处。
例如:
那么您可以这样使用它:
但是,当然,您真正想要的只是
SeqExtractor
中的if
语句。因此,不要太迷恋解决方案,因为您可能会错过更简单的做事方法。You can't pass information to an extractor, and you can't use information from one value returned in another, except on the
if
statement -- which is there to cover all these cases.What you can do is create your own extractors to test these things, but it won't gain you much if there isn't any reuse.
For example:
Then you can use it like this:
But, of course, all that you really want is that
if
statement inSeqExtractor
. So, don't get too much in love with a solution, as you may miss simpler ways of doing stuff.你可以这样做:
不过,正确处理 A 需要做一些工作。
更新:这是一个更好的解决方案:
比较中的
% 13
处理 ace 的等级 1 和等级 14。You could do something like:
Handling aces correctly requires a bit of work, though.
Update: Here's a much better solution:
The
% 13
in the comparison handles aces being both rank 1 and rank 14.怎么样:
def isStraight(cards:List[Card]) = (cards zip cards.tail) forall { case (c1,c2) =>; c1.rank+1 == c2.rank}
val cards = List(Card(1),Card(2),Card(3),Card(4))
How about something like:
def isStraight(cards:List[Card]) = (cards zip cards.tail) forall { case (c1,c2) => c1.rank+1 == c2.rank}
val cards = List(Card(1),Card(2),Card(3),Card(4))
这是一种完全不同的方法,但它确实使用模式匹配。它在 match 子句中产生警告,这似乎表明它不应该工作。但它实际上产生了正确的结果:
我暂时忽略了套件,也忽略了 2 下的 A 的可能性。
如果您对像这样的更多内容感兴趣,请谷歌“教堂数字 scala 类型系统”
This is a completely different approache, but it does use pattern matching. It produces warnings in the match clause which seem to indicate that it shouldn't work. But it actually produces the correct results:
I ignored the Suites for now and I also ignored the possibility of an ace under a 2.
If you are interested in more stuff like this google 'church numerals scala type system'
像这样的事情怎么样?
您仍然坚持使用
if
(实际上更大),但没有递归或自定义提取器(我相信您错误地使用next
,所以是为什么你的第二次尝试不起作用)。How about something like this?
You're still stuck with the
if
(which is in fact larger) but there's no recursion or custom extractors (which I believe you're using incorrectly withnext
and so is why your second attempt doesn't work).如果您正在编写扑克程序,那么您已经在检查 n-of-a-kind 了。当一手牌没有 n 种(n > 1)并且最小面值和最大面值之间的差恰好是 4 时,它是顺子。
If you're writing a poker program, you are already check for n-of-a-kind. A hand is a straight when it has no n-of-a-kinds (n > 1) and the different between the minimum denomination and the maximum is exactly four.
几天前,我在为 Project Euler 问题 54 做类似的事情。像你一样,我有 Rank 和 Suit 作为枚举。
我的 Card 类如下所示:
注意,我给了它
Ordered
特征,以便我们稍后可以轻松比较卡片。另外,在解析手牌时,我使用sorted
从高到低对它们进行排序,这使得评估值变得更加容易。这是我的
straight
测试,它根据是否是顺子返回一个Option
值。实际返回值(整数列表)用于确定手牌的强度,第一个代表从 0(无对子)到 9(同花)的手牌类型,其他是牌中任何其他牌的等级。计入其价值的手牌。对于顺子,我们只担心排名最高的牌。另请注意,您可以用 A 为低位、“轮盘”或 A2345 形成顺子。
I was doing something like this a few days ago, for Project Euler problem 54. Like you, I had Rank and Suit as enumerations.
My Card class looks like this:
Note I gave it the
Ordered
trait so that we can easily compare cards later. Also, when parsing the hands, I sorted them from high to low usingsorted
, which makes assessing values much easier.Here is my
straight
test which returns anOption
value depending on whether it's a straight or not. The actual return value (a list of Ints) is used to determine the strength of the hand, the first representing the hand type from 0 (no pair) to 9 (straight flush), and the others being the ranks of any other cards in the hand that count towards its value. For straights, we're only worried about the highest ranking card.Also, note that you can make a straight with Ace as low, the "wheel", or A2345.
这是一个完整的惯用 Scala 手牌分类器,适用于所有手牌(处理 5 高顺子):
Here is a complete idiomatic Scala hand classifier for all hands (handles 5-high straights):