@jozefg 的答案可能是最简单的方法,但是可以使其他操作更容易的替代方法是为and定义Enum
和Bounded
实例:Suit
Rank
data Suit = Hearts | Spades | Diamonds | Clubs deriving (Eq, Show, Enum, Bounded)
data Rank = Numeric Integer | Jack | Queen | King | Ace deriving (Eq, Show)
data Card = Card Rank Suit deriving (Eq, Show)
data Deck = None | Cons Card Deck deriving (Eq, Show)
instance Bounded Rank where
minBound = Numeric 2
maxBound = Ace
instance Enum Rank where
toEnum n
| n <= 1 = error "Invalid card value"
| n <= 10 = Numeric $ toInteger n
| n == 11 = Jack
| n == 12 = Queen
| n == 13 = King
| n == 14 = Ace
| otherwise = error "Invalid card value"
fromEnum (Numeric i) = fromEnum i
fromEnum Jack = 11
fromEnum Queen = 12
fromEnum King = 13
fromEnum Ace = 14
range :: (Bounded a, Enum a) => [a]
range = [minBound .. maxBound]
fromList :: [Card] -> Deck
fromList = foldr Cons None
fullDeck :: Deck
fullDeck = fromList [Card r s | r <- range, s <- range]
让编译器只派生 的实例相当容易Suit
,但对于Rank
. 严格来说,你不需要range
,但我认为它有点酷。另一个优点是您可以做类似的事情[Numeric 2, Numeric 5, .. Ace]
并返回[Numeric 2, Numeric 5, Numeric 8, Jack, Ace]
。
编辑
如果您无法更改数据类型的定义以添加派生语句,则可以这样做:
{-# LANGUAGE StandaloneDeriving #-}
data Suit = Hearts | Spades | Diamonds | Clubs
-- etc
deriving instance Eq Suit
deriving instance Show Suit
deriving instance Enum Suit
deriving instance Bounded Suit