Between thought and expression
Comparing computer languages can usually be interesting, sometimes informative, often amusing and in lots of cases just damn annoying!
Still this recent Hacker News post about Python vs Clojure did catch my eye.
Despite the fact the author of the blog post had pulled the Python code from answers to Project Euler the pythonista’s quite correctly cried foul because it wasn’t well written idiomatic python example and so was an unfair comparison.
There’s a lot of code out there posted on those intertubes over the years. However languages and practises evolve but unfortunately intertube posts can remain set in stone! Perl is often weighed down by its past history on the intertubes. One good way forward is just to produce more Modern Perl posts.
Anyway i digress, so moving on lets take a look at the Euler 4 – Finding Palindroms. This was the Clojure example that was put forward in the blog post:
(reduce max (filter #(let [s (str %)] (= (seq s) (reverse s))) (for [x (range 100 1000) y (range 100 1000)] (* x y))))
Now in reply to this a much more idiomatic python example was provided:
print max(s for s in (x * y for x in range(111, 1000) for y in range(x, 1000)) if list(str(s)) == list(reversed(str(s))))
This uses Python’s list comprehension. Definitely a very powerful construct. However i do find i get lost very quickly when trying to follow long nested comprehension’s 😦
In this same Python post there is also mention of a twitter post by Yukihiro Matz, the creator of Ruby. Matz provided the following Ruby example:
p [*100..1000].product([*100..1000]).map{|x,y| x*y}.select{|s|s=s.to_s; s==s.reverse}.max
Despite being formatted for twitter (ie. one line) i found it easy to follow through the logic.
Ruby & Python are not the only languages that can produce an elegant solution! So not to be out done here is a Perl example. In fact its the exact same code repeated three times but formatted differently:
use Modern::Perl; use List::Util q(max); # twitter one liner say max grep { $_ eq reverse $_ } map { my $x = $_; map { $x * $_ } 100..1000 } 100..1000; # again in lispy style format say max grep { $_ eq reverse $_ } map { my $x = $_; map { $x * $_ } 100..1000; } 100..1000; # and finally in my preferred more perlish alignment say max grep { $_ eq reverse $_ } map { my $x = $_; map { $x * $_ } 100..1000; } 100..1000;
The perl code speaks for itself.. its wonderfully clear and succinct. Also its fast, each version runs in under a second on my machine!
/I3az/
Just to confirm your findings, the python example took about 2.5 seconds to run on my machine (python 2.6) and then Perl example took less than .5 seconds (perl 5.8.9). And I couldn’t get the ruby example to run (I’m guessing it didn’t work on 1.8.6).
Hi Michael,
I didn’t bother testing it with Python from here because the Python that ships with Mac OSX Tiger is notoriously slow for some reason and it wouldn’t have been fair to compare it to the Perl times (which comes in on average at 0.85 secs using my own compiled 5.10.1 here).
The Ruby example requires 1.9.1. Don’t have that or Clojure loaded here but based on times given in the blog posts it doesn’t look likely that they will beat the Perl times.
/I3az/
I have Ruby 1.9.1 installed now and the code comes in at 1.2s here. Thats identical to the autobox example I gave in my other post, so not bad at all! (for both autobox and Ruby 1.9.1 !!)
/I3az/
Yes, you’re right. But I psaoenrlly consider arrays and hashes to be data structures, not primitive data types.