dealing out 5 hands of cards, each containing 5 cards

Submitted by metaperl on Sun, 09/04/2005 - 10:22pm.

I helped someone out in #haskell with the problem stated in the subject by writing the code below. I'm a bit rusty (and never was very good) at Haskell, so any pointers on how to improve the code I supplied to him are appreciated.

cards = [1..52]

dropAmount = [ d*5 | d <- [0 .. 4 ] ]

hand cards = map (\d -> (take 5 $ drop d cards)) dropAmount

Submitted by dtlin on Tue, 09/06/2005 - 1:27pm.

Choose whatever you can read and understand.

I think I like this one best:
hand = take 5 . List.unfoldr (\l -> Just (take 5 l, drop 5 l))

This works too:
hand cards = take 5 $ map (take 5 . (`drop` cards)) [0,5..]

Submitted by sandr on Wed, 09/07/2005 - 2:32am.

Making dtlin's first solution even more succint (and probably less readable to beginners), use the Prelude function splitAt, which combines take and drop. So:


hands = take 5 . List.unfoldr (Just . splitAt 5)

Submitted by j3h on Tue, 09/06/2005 - 2:52pm.

I came up with two approaches, although I agree that whatever you (or he) find most readable is best. I generalized the problem slightly to dealing N hands of M cards out of a deck.

Here was my first solution:

dealHands :: Int -> Int -> [a] -> [[a]]
dealHands handSize numHands deck = take numHands $ allHands handSize deck
    where allHands handSize deck =
              let (hand, deck') = splitAt handSize deck
                  fullHand = length hand == handSize
                  remaining = allHands handSize deck'
              in if fullHand
                 then hand:remaining
                 else []

And the second (which can be made much more succinct):

dealHands :: Int -> Int -> [a] -> [[a]]
dealHands handSize numHands deck =
    let
        -- Decks with the top handSize cards successively removed
        decks = iterate (drop handSize) deck
 
        -- The top handSize cards from each of the above decks
        potentialHands = map (take handSize) decks
 
        -- All hands of handSize from the deck, down to a hand with not
        -- enough cards (the deck is exhausted)
        allHands = takeWhile fullHand potentialHands
 
    in -- Get the top numHands hands from all possible hands
        take numHands allHands
 
    where
        -- Return whether this hand has a sufficient number of cards
        fullHand hand = length hand == handSize

Submitted by dtlin on Fri, 09/09/2005 - 9:15am.

Hmm, hand = take 5 . map (take 5) . iterate (drop 5) (stolen from the above) is possibly more elegant than hand = take 5 . List.unfoldr (Just . splitAt 5).  Very nice.

And I think that the fullHand checks are silly... I'd be perfectly happy with dealHands 2 2 [1] == [[1], []].

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.