6

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

4

3 回答 3

9

列表推导是一个非常整洁的语法。如果你推导出来EnumRankSuit可以非常简单地表达为:

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

如果你想知道为什么我有suit不同rank的顺序,第一个是因为Card构造函数使用的顺序,而后者是为了得到结果列表的顺序——以升序排列。

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

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

作为另一个小问题,为了省去记住Suit值的顺序的麻烦,派生Bounded也将能够编写枚举具有和[minBound .. maxBound]的实例的任何类型的所有值。EnumBounded

于 2013-01-04T23:48:42.217 回答
7

有几种方法可以做到这一点,不同数量的魔法。

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

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

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

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

于 2013-01-04T23:49:13.897 回答
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 with Rank and Suit 的排列。

于 2013-01-04T23:58:08.473 回答