我是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)等等。
我想用递归来做这件事,这就是我遇到这么多困难的原因。
非常感谢任何帮助。
谢谢!
答案 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 ..]
起重:
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)写成一个很大的递归函数。祝你好运!