|Think about Loose Coupling|
Adventures in multilingualismby revdiablo (Prior)
|on May 04, 2005 at 06:42 UTC||Need Help??|
A few weeks ago, someone I know was trying to solve a somewhat interesting problem (interesting to me, at least). They wanted to know all the words that could be formed from all the combinations of the last 4 digits of a phone number. We were assuming each digit has 3 letters (no Q or Z). So, for each combination of 2..9 choose 4 digits (0 and 1 don't have any letters on them), there are combinations of 0..2 choose 4 letters. These assumptions do not hold perfectly, as some phones have Q and Z on them, and the situation is likely much more complex outside of the US, but they will work for the sake of the problem.
The naive solution is to nest as many for loops as required. This is a pain, and hardly elegant. I wanted to come up with something better. I searched around the Monastery, looked at a few combinations generators (it seems everyone has their own favorite method), and came up with something that fits nicely in my brainspace.
The most elegant algorithms count up in base N (where N is the length of the list), and map that representation back to the original list. Since counting in base N wasn't the natural way for me to think about the problem, I decided to count in decimal and then convert it to the base N. TMTOWTDI, and all that. :-)
I had been writing all this code in Perl, but the Other Guy was using Python. I thought, "hey, I'm flexible. I'll just port it from Perl to Python." It was a surprisingly simple port. Perhaps the code doesn't look perfectly Pythonic, but it works as advertised, and made me happy.
Then I started playing with Pugs, and thought, "hey, this is fun. I'll port it from Perl to Perl 6." Of course, in doing this I uncovered a few bugs in Pugs, and ended up writing and committing failing test cases for those bugs (which were apparently fixed 2 days later, as the tests stopped failing and the code started working as advertised).
At the end of this adventure, I decided to share it with the Monks. So here it is, and for those who are dying to see, here's all three versions of the code. First, the original Perl 5 implementation:
Next, Python (the output is slightly different, but I didn't feel like wrangling it into shape):
And finally, the Perl 6 version:
There are a few things I got from this experience. One thing that really stood out was how similar the solutions were. Perhaps that was a consequence of the fact that I was porting the code from one to the next, rather than starting all over, but it shows that the facilities used here are available in all three languages.
Also, I noticed that the Perl 6 version still looks very much like Perl. It just has a few changes, and I think they're for the better. Perhaps the code can be further P6-ified, but I don't think it will change the feel dramatically. I think the new language is going to turn out nicely.
Finally, I noticed that -- yes, I am master of the obvious -- Python does some things that Perl doesn't. The big standout here was the built in iterator support. Just using yield instead of return is a heck of a lot easier than using an anonymous sub that wrangles closures to provide the same functionality. It's not that I would want to get rid of the closures (which seems to be the Python solution), but I'd like to have the easier technique too. Perl is supposed to be the language that steals good ideas from all around, isn't it?
As always, code criticisms/critiques welcome. I'm especially interested in any Perl6ish improvements to that version. I tend to limit myself to things that work in Pugs, but I know there are others here who don't have that limitation, and I'd love to hear from them. Anything else welcome, too! Please reply early and often. :-)