In my previous post, I wrote some code in Perl because I wanted the code to be clear and obvious. Wait? What? Who uses Perl when they want clarity?
Perl
I admit: I was surprised myself. Seriously, I tried several other languages first. Let’s just focus on the first loop here. Here’s the Perl again for reference:
foreach my $item ( @list_of_items ) {
$total += weight( $item );
}
Objective C
The actual code that I was attempting to explain was in Objective C with the NeXTStep foundation classes. I couldn’t imagine that anyone would want to try to understand it:
NSEnumerator* ee = [itemList objectEnumerator];
Item* item;
while ( ( item = (Item*)[ee nextObject] ) != nil ) {
total += [item weight];
}
C
My first attempt for the article was to write it in C using an array of items.
unsigned int ii;
for ( ii = 0; ii < itemCount; ++ii ) {
total += weight( itemArray[ ii ] );
}
That’s not too terrible. There is, however, a great deal of syntax and code devoted to handling the array including an extra variable to track its length. Beyond that, the loop counter is of little use to someone trying to understand the concept.
C++
My next go was C++. My hope was that iterators would make the looping less painful. C++ iterators, however, are the very model of pain.
std::list< Item* >::const_iterator it;
for ( it = itemList.begin(); it != itemList.end(); ++it ) {
total += weight( *it );
}
This requires the reader to wade through a syntactic thicket to get the iterator into the picture at all. It also requires a certain comfort with pointers that one wouldn’t have coming from another language.
Java
My next attempt was Java. I hate Java. It may not be terrible for this code with the new looping constructs that came in Java 1.5. Alas, I quit coding it before I made it through this first loop.
Lisp
Of course, I wanted to code this in Lisp:
...)
That would be perfectly clear to someone who has used Lisp or Scheme. Alas, not everyone has.
So… Perl…
If you want to write the Perl code, you have to understand the difference between scalars and arrays. To read the code, however, you don’t have to grok that distinction to see what’s going on. I didn’t try it in Python. Maybe it would be clear to a wider audience. I may have to try it next time.
You may want to try this in Lisp:
Excellent point. That is quite readable. I didn’t use loop in my Lisp attempt because I still am not so very comfortable with all it has to offer. In particular, the second half of the choice (looping through decrementing the random number be the weights until we get below zero) was going to require me to read tons of the hyperspec or get lucky with CLTL.
Perl 6 code is closer to your Lisp/Scheme example:
This works in Rakudo Perl 6 right now.
Wow. I can see where they are coming from, but that is definitely not code for non-programmers.
Aside from the syntax (which non-programmers wouldn’t know), I believe it reads very well. “Total is the sum of all of the weights of every item.”
The Perl 5 map-in-void-context version reads less well, because it relies on a side effect of the map block.
I agree the map-in-void-context with side-effects is annoying.
But, coming from Perl 5, I am not jiving with much of the Perl 6 at all. The [+] seems to be floating in there. (Can I use any binary function in its place? Can I only use certain math operators?) Map is a method or property of an array, now? And, we’ve mixed in another meaning for the colon :?
I think the Perl 5 map version might be slightly more readable to someone who hasn’t used Perl:
map { $total += weight( $_ ) } @list_of_items;
It still leaves something to be desired though for clarity.
This is an interesting presentation: “Readable Perl” as slideshow or Readable Perl as pdf.
That’s a great presentation. I’m on board with all of it except the Sigils. I know that some believe that Perl’s sigils make things more readable and obviate the need for Hungarian notation. (I am not a big fan of Hungarian notation either.) I think sigils conflate as many things as they clear up. In particular, when one references an item in an array the $ sigil feels contradictory to the square-bracket indexing:
Admittedly, something like the following would be way out of the question:
Further, sigils get flattened entirely to just $ when you’re dealing with references.
Beyond that, I totally agree with the presentation.
Also, that was my first exposure to Moose. Interesting…
[…] written in several programming languages. The loop had to sum the weights of each items in a list. Dmitry pointed out a much clearer loop in Lisp using the (loop …) construct. 1(loop for item in item-list […]
If, for legacy reasons, you are confined to C++, I’ve found the Boost FOREACH to be a nice bit of syntactic sugar:
BOOST_FOREACH( itemType item, itemList) {
total += weight( item );
}
Oh, man… Admittedly, I haven’t tried building Boost for about a year and a half, but compiling Boost from source is pain.
Java 1.5 does vastly improve the situation over 1.4.
for (Item item : itemList) {
total += item.getWeight();
}
Last summer I was maintaining a poorly written Java 1.4 application, so I still vividly recall 1.4’s awkwardness.
Here’s the full Java code to set up the example (not so succinct as the example its self).
import java.util.List;
class Item {
private double weight;
Item() {};
public double getWeight(){ return weight; }
public Item setWeight(double weight){ this.weight = weight; return this; }
}
public class ItemLoop {
@SuppressWarnings("unchecked")
public static List<Item> makeList(double[] numbers) {
List<Item> itemList = new ArrayList();
for (double nn : numbers) {
itemList.add(new Item().setWeight(nn));
}
return itemList;
}
public static void main(String args[]) {
double[] numbers = {1.1, 2.2, 3.14159, 4.4};
List<Item> itemList = ItemLoop.makeList(numbers);
double total = 0.0;
for (Item item : itemList) {
total += item.getWeight();
}
}
}