4

我有以下类型定义来表示卡片:

data Suit = Hearts | Spades | Diamonds | Clubs
data Rank = Numeric Integer | Jack | Queen | King | Ace
data Card = Card Rank Suit
data Deck = None | Cons Card Deck

Numeric Integer 表示从 2 到 10 的等级。

现在我想编写一个函数来获得完整的甲板:

fullDeck :: Deck

我怎样才能以最优雅的方式生成完整的套牌?我知道其中一些定义很丑陋,但我没有选择这个的自由。

谢谢

4

4 回答 4

7

做类似的事情

-- Not sure why you're reinventing a list but
fromList :: [Card] -> Deck
fromList = foldr Cons None

fullDeck = fromList [Card r s | s <- [Hearts, Spades, Diamonds, Clubs]
                              , r <- map Numeric [2..9]++[Jack, Queen, King, Ace]]

看起来不错?我们只是使用列表推导来生成所有可能性的列表,而不是将其粉碎成Deck.

于 2013-11-04T19:19:22.207 回答
6

您可以考虑使用Universe包。它并没有真正使代码变得更容易,但它很有趣。=)

这是它的外观。

import Data.Universe

data Suit = Hearts | Spades | Diamonds | Clubs deriving (Bounded, Enum)
data Rank = Numeric Integer | Jack | Queen | King | Ace

instance Universe Suit
instance Universe Rank where
    universe = map Numeric [2..10] ++ [Jack, Queen, King, Ace]

instance Finite Suit
instance Finite Rank

现在,您可以将universeF :: [(Rank, Suit)]其用作完整的一副牌。如果你真的想要自定义CardDeck类型,你可以添加几行代码:

data Card = Card Rank Suit
instance Universe Card where
    universe = [Card rank suit | (rank, suit) <- universeF]
instance Finite Card

data Deck = None | Cons Card Deck
fullDeck = foldr Cons None universeF
于 2013-11-04T19:45:06.857 回答
4

快速四处寻找,想出了这个:

data Suit = Hearts | Spades | Diamonds | Clubs deriving Show
data Rank = Numeric Integer | Jack | Queen | King | Ace deriving Show
data Card = Card Rank Suit deriving Show
data Deck = None | Cons Card Deck deriving Show

cards :: [Card]
cards = do
  suit <- [Hearts, Spades, Diamonds, Clubs]
  rank <- Ace : (map Numeric [2..10]) ++ [Jack, Queen, King]
  return (Card rank suit)

listToDeck :: [Card] -> Deck
listToDeck [] = None
listToDeck (x:xs) = Cons x $ listToDeck xs

够优雅吗?:D

于 2013-11-04T19:40:02.300 回答
1

@jozefg 的答案可能是最简单的方法,但是可以使其他操作更容易的替代方法是为and定义EnumBounded实例:SuitRank

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
于 2013-11-04T19:47:05.973 回答