哈斯克尔:制作一副纸牌

时间:2011-09-20 05:57:32

标签: list haskell

我是Haskell的新手,并且在使用递归制作一副牌时遇到了问题。

我有这些定义:

data Suit = Club | Diamond | Heart | Spade

data Value = Two | Three | Four | Five | Six | Seven
              | Eight | Nine | Ten | Jack | Queen
              | King | Ace

type Card = (Suit, Value)
type Deck = [Card]

instance Show Suit where
   show Club = "Club"
   show Diamond = "Diamond"
   show Heart = "Heart"
   show Spade = "Spade"

instance Show Value where
  show Two = "Two"
  show Three = "Three"
  show Four = "Four"
  show Five = "Five"
  show Six = "Six"
  show Seven = "Seven"
  show Eight = "Eight"
  show Nine = "Nine"
  show Ten = "Ten"
  show Jack = "Jack"
  show Queen = "Queen"
  show King = "King"
  show Ace = "Ace"

我正在尝试编写一个函数

makeDeck :: Deck

按照数据的顺序返回一张牌列表,所以(Club,Two - Ace),(Diamond,Two-Ace)等等。

我想用递归来做这件事,这就是我遇到这么多困难的原因。

非常感谢任何帮助。

谢谢!

4 个答案:

答案 0 :(得分:10)

这不使用递归,但更常规的做法是:

deriving Enum添加到Suit和Value的定义中,然后:

makeDeck :: Deck
makeDeck = [(suit, value) | suit <- [Club..Spade], value <- [Two..Ace]]

或者,提供所有套装和值的完全定义的列表,而不是使用Enum

答案 1 :(得分:4)

假设:

data Suit = Club | Diamond | Heart | Spade   deriving (Show, Enum)

data Value = Two | Three | Four | Five | Six | Seven
          | Eight | Nine | Ten | Jack | Queen
          | King | Ace  deriving (Show, Enum)

type Card = (Suit, Value)
type Deck = [Card]

makeDeck :: Deck

有很多方法可以计算Cartesian Product - 集合的乘积。 可以在Hoogle上查找这些示例中使用的所有函数:

单子

使用附带说明:

makeDeck = do
    suit <- [Club ..]
    value <- [Two ..]
    return (suit, value)

以上未经修饰的形式,使用>>=

makeDeck = [Club ..] >>= \suit ->
    [Two ..] >>= \value ->
    return (suit, value)

起重:

makeDeck = liftM2 (,) [Club ..] [Two ..]

monads中的函数应用程序:

makeDeck = return (,) `ap` [Club ..] `ap` [Two ..]

Applicatives

起重:

makeDeck = liftA2 (,) [Club ..] [Two ..]

应用程序中的功能应用程序:

makeDeck = pure (,) <*> [Club ..] <*> [Two ..]

与以下内容相同:

makeDeck = (,) <$> [Club ..] <*> [Two ..]

列表理解

请参阅@ ivanm的答案。

没有任何类型

makeDeck = concatMap (\suit -> map ((,) suit) [Two ..]) [Club ..]

如果您需要显式使用递归,可以将concatMap替换为definition,依此类推,直到到达foldr,真正的递归操作发生在那里。

答案 2 :(得分:1)

首先,您是否了解list comprehensions以及您可以使用它们做的所有令人敬畏的事情? makeDeck函数可以写成一个相当简单的列表理解(正如ivanm在他的回答中指出的那样),特别是考虑到你希望该套牌的顺序。

但是,如果你仍然想使用递归,有几种方法可以解决它。首先,找出在整个操作过程中需要跟踪的变量。您需要保留当前的卡片列表,并记录您所处的套装和价值。所以类型签名可能看起来像makeDeck :: Suit -> Value -> Deck。然后,您需要使用模式匹配来处理计算过程中的几种可能情况,例如

makeDeck suit Ace deck = (suit,Ace):(makeDeck (succ suit) Two)
[...]

正如您所看到的,这可能会成为一个相当长且笨拙的定义。列表理解方法将产生更简短,更优雅和规范的解决方案。无论您选择哪种方式,都需要在数据类型声明的末尾添加deriving Enum。如果您不熟悉,请在线阅读deriving关键字here或其他地方。

答案 3 :(得分:0)

其他人已经向你指出了列表理解,但是如果你想以递归的方式写它,请继续阅读 这听起来像是家庭作业,所以我会尝试给你一些提示,指出你正确的方向(而不是为你写作)。

要实现#1:
如果为代数数据类型派生Enum类型类,则可以创建如下的枚举:

*Main> [Club .. Spade]
[Club,Diamond,Heart,Spade]

要实现#2:
您可以使用地图创建这样的元组列表。您最终会以[[Suit]]结束,因此您应该使用concat函数将它们加入Deck

*Main> concat $ map (\x -> map (\y -> (x, y)) ['a'..'c']) [1..3]
[(1,'a'),(1,'b'),(1,'c'),(2,'a'),(2,'b'),(2,'c'),(3,'a'),(3,'b'),(3,'c')]

要实现#3: 您可以递归地实现地图。以下是前奏中的定义:

map :: (a -> b) -> [a] -> [b]
map _ []     = []
map f (x:xs) = f x : map f xs

所以你可以把(map。map)写成一个很大的递归函数。祝你好运!