Why Not Python?, Part 1

by Collin Park

Editor's Note: This article has been updated since its original publication.

It's been 20 years or more since I wrote my first few hundred lines of code in C, so it's about time I learned a more modern language, maybe something object oriented. But what?

One of the great things about running free software is a lot of programming languages are included on the installation media. It's like in the good old days. Back then if you bought, say, an HP 3000, you got Fortran, SPL/3000, a linker, a debugger and printed manuals, not to mention support for shared libraries since 1972. But I digress.

Today, if you buy a proprietary system and want to program it in, say, ANSI C, you might have to shell out hundreds or even thousands of dollars for some vendor's compiler. That's the case unless you can find free software, such as the GNU compiler collection (GCC), ported to your particular platform.

Now, in the good new days, I have C, C++, Ruby, Perl, Python, Pascal and Fortran 77 on my SuSE 8.0 CDs. I also have Tcl, Scheme, Smalltalk, Modula-2, Forth, Prolog, REXX, LISP, Ada95 and so on.

So a couple of years ago, when my teenage daughter became interested in programming, we worked out a few "toy" programs in Python. I had heard it would be a better choice than Perl for a first language. Python is current, it has object-oriented features, books are available for it and I'd seen some recent articles praising it. So I decided to give it a whirl on something a little bigger. I'll share that journey with you in Part 2 of this article, but for now I thought I'd revisit our answers to the Coconuts problem.

This problem, which appeared in a short story by Ben Ames Williams, concerns five men on a deserted island who gather a pile of coconuts. During the night, one of the men wakes up and decides to take his share in advance. He divides the coconuts into five equal piles. One coconut is left over, and he tosses it to a nearby monkey. Hiding his pile and consolidating the four remaining piles, he returns to sleep.

The second man does the same thing, dividing the now slightly smaller pile into five equal ones, tossing the one leftover coconut to the monkey, hiding his pile and returning to sleep. The third, fourth and fifth man all repeat the same pattern.

When all awake in the morning, the pile looks smaller, but each man is as guilty as the others, so no one says anything. They divide the remaining coconuts into five equal piles as the monkey watches. This time, though, there are no coconuts left over.

How many coconuts did they start with?

I remembered that the answer was less than 100,000, so what we needed was a program that did this:

  • A. run steps B-G with origPile in the range of 5 to about 100,000

  • B. set tempPile = origPile

  • C. do steps D-E five times, i.e., once for each man

  • D. if (tempPile mod 5) isn't 1, try a new value of origPile, that is, don't do E-G)

  • E. set tempPile to 4/5 of (tempPile-1)

  • F. if (tempPile mod 5) isn't zero, try a new value of origPile, that is, don't do G)

  • G. print the value of origPile, and exit the A-G loop

Or, pictorially, the program needed to do this:

The Coconuts Program in the Language of the 1980s

Back in the day, to implement the program I outline above, I coded the following in C:


    1   main()
    2   {
    3      int origPile, tempPile, man;
    4      for (origPile=5; origPile<99999; ++origPile) {   //A
    5          tempPile = origPile;                         //B
    6          for (man=0; man<5; ++man) {                  //C
    7              if ((tempPile%5) != 1) goto foo;         //D
    8              tempPile = (tempPile-1) * 4 / 5;         //E
    9          }
   10          if (tempPile%5) continue;                    //F
   11          printf("Victory! %d\n", origPile);           //G
   12          break;
   13   foo:
   14      }
   15   }

Here's a brief description of the C program; you can skip this if you like. Lines 1-2 are simply C boilerplate, so the compiler knows what you want to run as your main program.

Line 3 is a declaration; that is, we declare that origPile, tempPile, and man all are variables of type "int", a subset of integer.

Line 4 is essentially step A in the summary. It tells the compiler that we want to execute some statements--until the matching } in line 14--with origPile in the range of 5 up to but not including 99,999. Don't worry if the syntax isn't obvious to you; we're really here to talk about Python, not C, but this is just an illustration.

Line 5 does step B in the summary. That was easy!

Line 6 does step C, which tells the compiler we want to execute a loop--until the matching } in line 9--five times, with "man" taking on values 0, 1, 2, 3 and 4.

Line 7 does step D. It tells the compiler we want to jump to the label foo: if there isn't exactly one left over after dividing tempPile by five. In Perl, you could say:


    next OuterLoop unless ($tempPile%5 == 1);

In a bash(1) script, you could continue 2, but in C, goto probably is the easiest way to implement this step.

In line 10 it says to try a new value of origPile if tempPile doesn't divide evenly into five piles in the morning. That is essentially step F. Lines 11-12 do step G.

So basically, the program should work. Does it?


    % make c0
    cc     c0.c   -o c0
    % ./c0
    Victory! 3121
    %

Did the program return a good answer? Suppose the group of five start with 3,121 coconuts, as the program says. The first man divides 3,121 coconuts into five piles of 624 each, with one left over. He gives the leftover to the monkey, hides 624 and goes back to bed. 3,121-1-624 = 2,496.

The second man divides 2,496 coconuts into five piles of 499 each, with the one leftover going to the monkey. He hides 499. 2,496-1-499 = 1,996.

The third man divides 1,996 coconuts into five piles of 399, plus one leftover for the monkey. He hides 399. 1,996-1-399 = 1,596.

The fourth man divides 1,596 coconuts into five piles of 319 each, with one going to the monkey and hides 319. 1,596-1-319 = 1,276.

The fifth man divides 1,276 coconuts into five piles of 255 each and has one left over for the monkey. He hides 255. 1,276-1-255 = 1,020.

In the morning, 1,020 coconuts are left. These are divided into five piles of 204 each. So the program gave a reasonable answer.

The Coconuts Program in Python

How could I go about writing a similar program in Python? Looking at some examples in Learning Python, (Mark Lutz and David Ascher, O'Reilly 1999), the first thing I noticed was there are no declarations. That is, we don't say int origPile, we simply start using a name.

As in a Perl or shell script, the first use of a name brings its variable into existence. We also don't have to say main(). Python thinks you're in main from the first executable statement. Therefore, the basic Python loop is easier to write than is the basic C iteration loop. Instead of writing:


    for (origPile=5; origPile<99999; ++origPile) {

you write:


    for origPile in range(5,99999):

That can be line 1. For the rest, how about using this:


    1   for origPile in range(5,99999):             #A
    2      tempPile = origPile                      #B
    3      for man in range(5):                     #C
    4         if tempPile%5 != 1: break             #D
    5         tempPile = (tempPile-1) * 4 / 5;      #E
    6      else:                                    #if you didn't "break"
    7         if tempPile%5 != 0: continue          #F
    8         print "Victory!", origPile            #G
    9         break

Here are a few more differences: instead of using { curly braces } to show the range of a block--a loop or whatever--you use indentation. You don't need semicolons at the end of every statement, although I did slip and toss in a useless one in line 5. Python didn't object. Comments are signaled with # rather than // or /* */ .

Python doesn't have a "goto" statement, but in this case I didn't need it. The "else" after a loop is if you never execute break.

Make sense? Okay, let's call that code c0.py and run it:


    % python c0.py
    Victory! 3121
    % 

That sure was easy! You don't have to be a genius to write working Python code immediately.

Here is what my daughter wrote (I added the comments):


    1   import sys
    2   MoreCoconuts ="Whatever"
    3   
    4   for Coconuts in range(5,99999):                 #A
    5       Pile=Coconuts                               #B
    6       try:
    7           for dummy in range(0,5):                #C
    8               if (Pile%5)!=1: raise MoreCoconuts  #D
    9               Pile=(Pile-1)/5*4                   #E
   10           if Pile%5==0:                           #F (skip G if nonzero)
   11               print "Victory! "+`Coconuts`        #G
   12               sys.exit()
   13   
   14       except MoreCoconuts:
   15           continue

(I might have helped her a little with this.)

Because we want to call the system's exit routine in line 12, we have to say import sys. When a Python program wants to access that system call, it needs a Python module that knows how to do that. In hindsight, line 12 also could have been coded simply as break, which would have been fine. Then, we wouldn't need the "import sys" in line 1. But there are times when sys.exit(), which terminates the whole program, is just what you need.

The try/except construction may be familiar to C++ programmers. The basic idea is that anywhere in the "try" block, you can "raise" an exception, which is caught in the "except" block. Uncaught exceptions go to the next enclosing "try". If uncaught there, they propagate to the Python runtime system, which typically terminates your program.

Line 8 says that if the pile of coconuts doesn't have exactly one leftover when it is divided into the five smaller piles, we need to abandon this theory (or this "try") and try again with more coconuts.

Line 10 could also have been coded this way:


    if Pile%5 != 0: raise MoreCoconuts

As in Perl, there's more than one way to do it in Python.

So we've seen a few C/Python differences in action on a toy program. In Part 2, I'll show you a program to solve the Sudoku puzzle.

Collin Park works for Network Appliance, where he uses Linux on his desktop and laptop computers. He does data recovery and other Linux-related stuff at home, where he lives with his wife and their two teenage daughters. All use Linux to meet their computing needs.

Load Disqus comments