[Tutor] Use functions re avoid Re: Can the following algorithm be improved?

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Mon Aug 8 03:30:24 CEST 2005


Answer: yes, very much so.  *grin*


Ok, let's take a look at some snippets.

######
    cd = int(raw_input("How many cards to deal (1-6) or 9 to exit:"))
    if cd == 1:
        a = random.choice(range(52))
        if a == 0:
            a = "Ace of Hearts"
        elif a == 1:
            a = "Two of Hearts"
[... code cut]
    elif cd == 2:
        b = random.choice(range(52))
        c = random.choice(range(52))
        for item in [b,c]:
            if item == 0:
                item = "Ace of Hearts"
            elif item == 1:
                item = "Two of Hearts"
[... code cut]
    elif cd == 3:
        d = random.choice(range(52))
        e = random.choice(range(52))
        f = random.choice(range(52))
        for item in (d,e,f):
            if item == 0:
                item = "Ace of Hearts"
            elif item == 1:
                item = "Two of Hearts"
######

Ok, let's stop there.

The block of code above is taking a number (0-52) and trying to transform
it into a readable definition.  But there is repetition here that can be
improved if you take advantage of the problem's structure.


If you can write a function that takes a number from 0 to 52, and turns it
into a card name, then instead of having to repeat the same if/elif code
the number of times that you're dealing a card, you can save a lot of
typing.  You'll only need maybe eighty lines of code, instead of six
hundred.

(Actually, if you take advantage of the essence of the program, the
program can be reduced to about ten lines total.  That's the general
direction that we'll try to lead you towards.  Short programs are much
nicer than long ones.)



To make it clear what we mean, here's a similar situation: let's say that
we wanted to flip two coins and get back a nice description of what we
got.  We could write code like this:

######
first_flip = random.choice(range(2))
second_flip = random.choice(range(2))

if first_flip == 0 and second_flip == 0:
    print "heads", "heads"
elif first_flip == 0 and second_flip == 1:
    print "heads", "tails"
elif first_flip == 1 and second_flip == 0:
    print "tails", "heads"
elif first_flip == 1 and second_flip == 1:
    print "tails", "tails"
#######


But there is a much nicer way of writing this:

######
def number_to_coin(n):
    if n == 0:
        return "heads"
    else:
        return "tails"

first_flip = random.choice(range(2))
second_flip = random.choice(range(2))
print number_to_coin(first_flip), number_to_coin(second_flip)
######


There is a concrete reason why the second version is better: it scales if
we want to use more coins.  Once we have number_to_coin(), we can easily
handle three coins:

######
first_flip = random.choice(range(2))
second_flip = random.choice(range(2))
third_flip = random.choice(range(2))
print number_to_coin(first_flip), number_to_coin(second_flip),
print number_to_coin(third_flip)
######

The original approach, on the other hand, simply won't scale at all, and
we'll end up with huge source code for very little gain.


Avoiding code duplication is one major reason that we use functions.  As
soon as you start cutting and pasting code, that's a honking red warning
light that you should be writing functions.  But it takes practice, which
is what we're pointing out to you now.  *grin*


Try rewriting your program so you don't copy-and-paste those huge if/elif
blocks on each card flip.

That is, write a function number_to_card() that takes a number from 0 to
52 and returns the string description of that card.  Your program will be
much more managable, and will probably fit on a single screen.



More information about the Tutor mailing list