我有以下类型定义来表示卡片:
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
我怎样才能以最优雅的方式制作完整的牌组?我知道其中一些定义很难看,但我没有选择这个的自由。
由于
答案 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)]
作为您的全套牌。如果您真的想要自定义Card
和Deck
类型,则只需添加几行代码:
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的答案可能是最简单的方法,但是可以使其他操作更容易的替代方法是为Enum
和Bounded
定义Suit
和Rank
个实例:< / 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