Haskell:如何生成两个简单代数数据类型的笛卡尔积

5

我正在学习Haskell,所以我正在编写一些简单的纸牌游戏。我已经定义了一些数据类型:

data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord)

data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show)

data Card = Card Rank Suit 

现在我想创建一副全新的52张牌的牌组。我相信有一种简单的方法可以做到这一点,但我所能想到的只有:

 pristineDeck = [Card Ace Hearts, Card Two Hearts, ...]

我能让Haskell帮我生成这个列表吗?

2
让你的类型派生“Enum”类型类(只需将Enum放在上面的Show旁边即可)。它们中的三个:Rank,Suit和Card。 - Alp Mestanogullari
1
你要找的不是叉积,那是涉及到三维向量的东西。你可能指的是“笛卡尔积”。 - Ben Millwood
@BenMillwood 我的错... "SQL交叉连接" + "笛卡尔积" + 物理学学位 - Tony K.
更保险的做法是像数学家一样,把所有东西都称作“乘积”,然后依靠上下文来消除歧义。如果你感觉慷慨的话,可以称之为“张量积”。@BenMillwood - C. A. McCann
1
@C.A.McCann: 你有一个总和吗?不如叫它余积! - user824425
3个回答

9

列表推导式是一种非常简洁的语法。如果您在RankSuit上派生Enum,则可以非常简单地表达它:

pristineDeck = [ Card rank suit | suit <- [Hearts .. Clubs], rank <- [Ace .. King] ]

如果你想知道为什么我把和按不同顺序排列,第一个是因为构造函数的顺序,而后者是为了获得结果列表的顺序--花色以升序排列。更普遍地说,或者当单个列表理解变得太笨重时,笛卡尔积正好是列表实例所给出的行为。以下代码等同于上面的列表理解式:
pristineDeck = do suit <- [Hearts .. Clubs]
                  rank <- [Ace .. King]
                  return $ Card rank suit

作为另一个小问题,为了避免记住 Suit 值的顺序,同时派生 Bounded 将使您能够编写 [minBound..maxBound] 以枚举具有 EnumBounded 实例的任何类型的所有值。

1
个人而言,我不会为“花色”类型派生Enum,而是在推导式中写出完整的列表。在我看来,“花色”没有自然排序,因此省略号语法很容易让人感到困惑。 - Ben Millwood
2
我还将派生一个 Bounded 实例。[minBound .. maxBound] 明确表示您正在枚举所有变体,即使没有自然排序也是如此。 - Roman Cheplyaka
1
@AlfonsoVillén:谢谢。愚蠢、糟糕、让人头疼的语法边角案例... - C. A. McCann
2
Card <$> [minBound..maxBound] <*> [minBound..maxBound] - user824425
1
@Tinctorius:那样做会起作用!但在这种情况下,我实际上认为列表推导更易读。更有用的是,在我调整过的“Prelude”中定义的enumerate = [minBound .. maxBound]。它非常方便,可以用于正是这种目的。 - C. A. McCann
显示剩余5条评论

7
有多种方法可以实现这个目标,其所需的技术水平也有所不同。
首先,由于你的类型的构造函数没有参数,因此你可以为它们派生一个枚举(Enum)。这将允许你使用 [Ace..King] 这样的语法来获取所有扑克牌的列表。
其次,列表推导(List comprehensions)是从多个其他列表中提取项形成列表的好方法。尝试一下这个:
[x + y | x <- [100,200,300], y <- [1,2,3]]

那应该能为您提供所需工具,以便应用到您的示例中。

3
阿尔普正确地告诉你要派生枚举。
>data Rank = Ace|Two|Three|Four|Five|Six|Seven|Eight|Nine|Ten|Jack|Queen|King deriving (Eq,Show,Ord,Enum)
>data Suit = Hearts|Spades|Diamonds|Clubs deriving (Show,Enum)

现在:

>enumFrom Ace
[Ace,Two,Three,Four,Five,Six,Seven,Eight,Nine,Ten,Jack,Queen,King]

为了获得两个列表的排列组合,您可以使用列表推导式:
>[[x,y]|x<-[1..2],y<-[2..5]]
[[1,2],[1,3],[1,4],[1,5],[2,2],[2,3],[2,4],[2,5]]

或者获取加法的排列组合:

>[x + y|x<-[1..2],y<-[2..5]]
[3,4,5,6,4,5,6,7]

现在你只需要进行一些替换,就可以得到按照等级和花色排列的汽车排列组合。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接