列表数据类型haskell with bounds

时间:2013-11-04 19:14:23

标签: haskell algebraic-data-types

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

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 个答案:

答案 0 :(得分: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

答案 1 :(得分:5)

您可以考虑使用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

答案 2 :(得分: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

答案 3 :(得分:1)

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

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