# Function for shuffling cards

Assignment from here

Q6. Define a function shuffle that takes a list with an even number of elements (cards) and creates a new list that interleaves the elements of the first half with the elements of the second half:

def card(n):
"""Return the playing card type for a positive n <= 13."""
assert type(n) == int and n > 0 and n <= 13, "Bad card n"
specials = {1: 'A', 11: 'J', 12: 'Q', 13: 'K'}
return specials.get(n, str(n))

def shuffle(cards):
"""Return a shuffled list that interleaves the two halves of cards.

>>> suits = ['♡', '♢', '♤', '♧']
>>> cards = [card(n) + suit for n in range(1,14) for suit in suits]
>>> cards[:12]
['A♡', 'A♢', 'A♤', 'A♧', '2♡', '2♢', '2♤', '2♧', '3♡', '3♢', '3♤', '3♧']
>>> cards[26:30]
['7♤', '7♧', '8♡', '8♢']
>>> shuffle(cards)[:12]
['A♡', '7♤', 'A♢', '7♧', 'A♤', '8♡', 'A♧', '8♢', '2♡', '8♤', '2♢', '8♧']
>>> shuffle(shuffle(cards))[:12]
['A♡', '4♢', '7♤', '10♧', 'A♢', '4♤', '7♧', 'J♡', 'A♤', '4♧', '8♡', 'J♢']
>>> cards[:12]  # Should not be changed
['A♡', 'A♢', 'A♤', 'A♧', '2♡', '2♢', '2♤', '2♧', '3♡', '3♢', '3♤', '3♧']
>>> repeated(shuffle, 8)(cards) == cards
True
"""
assert len(cards) % 2 == 0, 'len(cards) must be even'


Solution:

def card(n):
"""Return the playing card type for a positive n <= 13."""
assert type(n) == int and n > 0 and n <= 13, "Bad card n"
specials = {1: 'A', 11: 'J', 12: 'Q', 13: 'K'}
return specials.get(n, str(n))

def shuffle(cards, index = 1, incr = 0):
"""Return a shuffled list that interleaves the two halves of cards.

>>> suits = ['♡', '♢', '♤', '♧']
>>> cards = [card(n) + suit for n in range(1,14) for suit in suits]
>>> cards[:12]
['A♡', 'A♢', 'A♤', 'A♧', '2♡', '2♢', '2♤', '2♧', '3♡', '3♢', '3♤', '3♧']
>>> cards[26:30]
['7♤', '7♧', '8♡', '8♢']
>>> shuffle(cards)[:12]
['A♡', '7♤', 'A♢', '7♧', 'A♤', '8♡', 'A♧', '8♢', '2♡', '8♤', '2♢', '8♧']
"""
assert len(cards) % 2 == 0, 'len(cards) must be even'
if index > (len(cards) // 2):
element = cards.pop((len(cards) // 2) + incr)
cards.insert(index, element)
return cards
else:
element = cards.pop((len(cards) // 2) + incr)
cards.insert(index, element)
return shuffle(cards, index + 2, incr + 1)


Can I improve this solution?

First of all, you are not supposed to modify the list in-place. Instead, return a shuffled copy of the original list. This is also checked by this doc test:

>>> cards[:12]  # Should not be changed
['A♡', 'A♢', 'A♤', 'A♧', '2♡', '2♢', '2♤', '2♧', '3♡', '3♢', '3♤', '3♧']


Furthermore, using recursion makes this overly complicated. After all, the solution to get the desired result is pretty simple: Cut the list in half and then take one item alternatively from each sublist. The first part is simple splicing, and ways to do the latter is covered in this question. So you could end up with this:

def shuffle (cards):
# split the list in halves
first, second = cards[:len(cards) // 2], cards[len(cards) // 2:]

# combine them alternatively, e.g.
return sum(map(list, zip(first, second)), [])


You can also do it without splitting the list by just iterating it alternatively from the beginning and from the middle:

def shuffle (cards):
result = []
half = len(cards) // 2
for i in range(half):
result.append(cards[i])
result.append(cards[i + half])
return result