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

时间:2013-01-04 23:40:27

标签: haskell

我正在学习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为我生成这个列表吗?

3 个答案:

答案 0 :(得分:10)

列表推导是一种非常整洁的语法。如果您在EnumRank上获得Suit,则可以将其简单地表达为:

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

如果您想知道为什么我在不同的订单中有suitrank,第一个是因为Card构造函数使用的顺序,而后者是为了获取订单结果列表 - 按升序排列。

更一般地说,或者当单个列表理解变得过于庞大时,笛卡尔积就是列表的Monad实例给出的行为。以下等同于上面的列表推导:

pristineDeck = do suit <- [Hearts .. Clubs]
                  rank <- [Ace .. King]
                  return $ Card rank suit

作为另一个小问题,为了省去记住Suit值所在的顺序的麻烦,导出Bounded也可以编写[minBound .. maxBound]来枚举所有值包含EnumBounded实例的任何类型。

答案 1 :(得分:7)

有几种方法可以做到这一点,不同程度的巫术。

首先,由于您的类型的构造函数都没有参数,因此您可以为它们派生Enum。这将允许你写例如[Ace..King]获取所有卡片的清单。

其次,列表推导是形成从多个其他列表中绘制的项目列表的好方法。试试这个:

[x + y | x <- [100,200,300], y <- [1,2,3]]

这应该为您提供应用于您的示例所需的工具。

答案 2 :(得分:3)

Alp是正确的告诉你派生Enum

>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]

现在你只需要进行一些替换来获得排名和套装的Car的排列。