To Python from Perl

I’ve recently switched to Python, after having programmed in Perl for many years. I’m sacrificing all my knowledge of the libraries and language quirks of Perl. The reason I moved despite that is for a somewhat trivial reason, actually. It’s because Python doesn’t require a closing brace.

Consider this Javascript (or very nearly C or Java) code:

1
2
3
4
5
6
var s=0;
for (var i=0; i<10; i++) {
    for (var j=0; j<10; j++) {
        s = s + i * j
    }
}

That’s 6 lines, with two lines just containing the closing brace. Or consider Perl.

1
2
3
4
5
6
my $s = 0
foreach my $i (1 .. 10) {
    foreach my $j (1 .. 10) {
        $s = $s + $i * $j
    }
}

Again, 6 lines with 2 for the braces. The $ before the variables also drops readability just a little bit. Here’s Python:

1
2
3
4
s = 0
for i in xrange(1, 10):
    for j in xrange(1, 10):
        s = s + i * j

On the margin, I like writing shorter programs, and it annoys me to no end to have about 20 lines in a 100-line program devoted to standalone closing braces.

What I find is that once you’ve really know one language, the rest are pretty straightforward. OK, that’s not true. Let me qualify. Knowing one language well out of C, C++, Java, Javascript, PHP, Perl, Python and Ruby means that you can program in any other pretty quickly — perhaps with a day’s reading and a reference manual handy. It does NOT mean that you can pick up and start coding with Lisp, Haskell, Erlang or OCaml.

Occasionally, availability constrains which programming language I use. If I’m writing a web app, and my hosting provider does not offer Ruby or Python, that rules them out. If I don’t have a C or Java compiler on my machine, that rules them out. But quite often, these can be overcome. Installing a compiler is trivial and switching hosting providers is not too big a deal either.

Most often, it’s the libraries that determine the language I pick for a task. Perl’s regular expression library is why I’ve been using it for many years. Ruby’s HPricot and Python’s BeautifulSoup make them ideal for scraping, much more than any regular expression setup I could use with Perl. Python Image Library is great with graphics, though for animated GIFs, I need to go to the GIF89 library in Java. And I can’t do these easily with other languages. Though each of these languages boast of vast libraries (and it’s true), there are still enough things that you want done on a regular basis for which some libraries are a lot easier to use than others.

So these days, I just find the library that suits my purpose, and pick the language based on that. Working with Flickr API or Facebook API? Go with their default PHP APIs. Working on AppEngine? Python. These days, I pick Python by default, unless I need something quick and dirty, or if it’s heavy text processing. (Python’s regular expression syntax isn’t as elegant as Perl’s or Javascript’s, mainly because it isn’t built into the language.)

To get a better hang of Python (and out of sheer bloody-mindedness), I’m working through the problems in Project Euler in Python. For those who don’t know about Project Euler,

Project Euler is a series of challenging mathematical/computer programming problems that will require more than just mathematical insights to solve. Although mathematics will help you arrive at elegant and efficient methods, the use of a computer and programming skills will be required to solve most problems.

Each problem has been designed according to a "one-minute rule", which means that although it may take several hours to design a successful algorithm with more difficult problems, an efficient implementation will allow a solution to be obtained on a modestly powered computer in less than one minute.

It’s a great way of learning a new programming language, and my knowledge of Python is pretty rudimentary. At the very least, going through this exercise has taught me the basics of generators.

I’ve solved around 40 problems so far. Here are my solutions to Project Euler. I’m also measuring the time it takes to run each solution. My target is no more than 10 seconds per problem, rather than the one-minute, for a very practical reason: the solutions page itself is generated by a Python script that runs each script, and I can’t stand waiting for more than that to refresh the page each time I solve a problem.

  1. Nicola says:

    Hi guy,

    I think you should add a comment form on your Euler page… this is not the right page for this comment.

    Anyway it seems your page is one of the favourites for the students that have project Euler probems as class assignments. So I’d like to contribute…

    This is a better solution for the problem #18 using an easy formulation od Dijkstra algorithm.

    tr = (
    (75, ),
    (95, 64, ),
    (17, 47, 82, ),
    (18, 35, 87, 10, ),
    (20, 4, 82, 47, 65, ),
    (19, 1, 23, 75, 3, 34, ),
    (88, 2, 77, 73, 7, 63, 67, ),
    (99, 65, 4, 28, 6, 16, 70, 92, ),
    (41, 41, 26, 56, 83, 40, 80, 70, 33, ),
    (41, 48, 72, 33, 47, 32, 37, 16, 94, 29, ),
    (53, 71, 44, 65, 25, 43, 91, 52, 97, 51, 14, ),
    (70, 11, 33, 28, 77, 73, 17, 78, 39, 68, 17, 57, ),
    (91, 71, 52, 38, 17, 14, 91, 43, 58, 50, 27, 29, 48, ),
    (63, 66, 4, 68, 89, 53, 67, 30, 73, 16, 69, 87, 40, 31, ),
    ( 4, 62, 98, 27, 23, 9, 70, 98, 73, 93, 38, 53, 60, 4, 23, ),
    )

    sums=[0]
    for i in xrange(len(tr)):
    ns=[0]*len(tr[i])
    for c,v in enumerate(tr[i]):
    if c>0:
    s0=sums[c-1]+v
    if s0>ns[c]:
    ns[c]=s0
    else:
    pass
    try:
    s1=sums[c]+v
    if s1>ns[c]:
    ns[c]=s1
    except:
    pass
    sums=ns
    max(sums)

    For each row the program stores the greater accumulator of the numbers leading to that row, so it is not an O(2^n) problem but a O(n^2).

    I have also a solution for problem #31:

    coins = (1, 2, 5, 10, 20, 50, 100, 200)
    count=0

    def coinFlip(level, residual, pattern):
    global count, tests
    p = residual / coins[level]
    # print level, residual, pattern, innResid
    while True:
    tests += 1
    innResid = residual-p*coins[level]
    if level>0:
    coinFlip(level-1, innResid, pattern + (p,))
    if p==0:
    break
    p=p-1
    else:
    if innResid==0:
    count += 1
    # print count, pattern + (p,)
    break

    count=0
    tests=0
    coinFlip(7, 200, ())

    It works by “changing” the coins: we start with one 2P and change it for two 1P, then we change one of the two 1P with two 50p… and so on.

    I’d be very glad if you like them and decide to publish my code on your site.

    Greetings,
    Nicola.