The Universe of Discourse

Tue, 08 Jan 2008

Clubbing someone to death with a loaded Uzi
I once had an intern who wrote wrote the following code to process a web survey form. The form input widgets were named q1, q2, and so forth:

    foreach $k (keys %in) { if ($k eq q1) {
if ($in{$k} eq agree) {
$count{q10} =$count{q10} + 1;
}
if ($in{$k} eq disaagree) {
$count{q11} =$count{q11} + 1;
}
}
if ($k eq q2) { @q2split = split(/\0/,$in{$k}); foreach (@q2split) {$count{$_} =$count{$_} + 1; } } if ($k eq q3) {
$count{$in{$k}} =$count{$in{$k}} + 1;
}

...
}

There is a lot wrong with this code, but it's all trivial compared with the one big problem, which is the wholly unnecessary loop and tests. The whole thing could be (and should be, and was) rewritten as:

    if ($in{q1} eq agree) {$count{q10} = $count{q10} + 1; } if ($in{q1} eq disaagree) {
$count{q11} =$count{q11} + 1;
}

@q2split = split(/\0/, $in{q2}); foreach (@q2split) {$count{$_} =$count{$_} + 1; }$count{$in{q3}} =$count{$in{q3}} + 1; ...  After which one could start addressing the smaller problems, like the fact that "disagree" is misspelled. This is the sort of mistake you expect from an intern. I chuckled and corrected him. But I've seen it several times since from non-interns. Here's another example. I am not making this up. Whether it's more or less odious than the intern code is up to you to decide:  foreach$location_name (%LOCATION ) {
$location_code =$LOCATION{$location_name}; if ($location_name eq $location ) { printf FILE "$location_code\,";
printf FILE "%4s", "$min3\,"; printf FILE "%4s", "$max3\,";
printf FILE "%1s", "$wx3\n"; } }  It could have been written like this:  printf FILE "$LOCATION{$location}\,"; printf FILE "%4s", "$min3\,";
printf FILE "%4s", "$max3\,"; printf FILE "%1s", "$wx3\n";

I started using this problem as an interview question. I'll present the subject with trivial code like this:

        for my $k (keys %hash) { if ($k eq "name") {
$hash{$k}++;
}
}

and then ask if they have any comments about it. One nice thing about the question is that it translates naturally into whatever imperative language they claim expertise in.

It's appalling how many supposedly professional programmers see nothing wrong here. They squint at the code, and say "I think you need parentheses around %hash there", or they criticize the choice of variable names.

I first used this as an interview question because the Python code sample submitted by a job applicant contained an example of it. "Weird," I thought, "but maybe she's outgrown that." Since she claimed to be an expert Perl user, I asked her about it in Perl, using code like the example above. After she made a syntactic suggestion, I said "It's not a syntax problem, and it's not a trick question." She criticized the syntax some more. Finally I told her the answer: "Couldn't you just use \$hash{name}++?"

"Oh, yeah, I guess so," she said.

A few minutes later we were going over her Python code sample and I pointed out the place where she had done the exact same thing, and asked if she was happy with that loop and wanted to change it. No, she thought it was just fine.

"Doesn't this look like the example I showed you on the whiteboard a little while ago?"

"Oh, I guess it does."

We didn't hire her.

Larry Wall once said that iterating over the keys of a hash is like clubbing someone to death with a loaded Uzi.

I had already realized that you could, in principle, commit this error with a regular array instead of with a hash, but I had never seen an example until today's episode of the Daily WTF. The Daily WTF code is so awful, all the way through, that I was afraid that people might miss this slightly-more subtle gem lurking in the middle, and that was what motivated me to write this article in the first place. Here's the gem:

        // Java
for (int a=1;a<=params.size();a++) switch (a)
{
case 1 : if (params.get(0) != null)
this.one=params.get(0).toString();
break;
case 2 : if (params.get(1) != null)
this.two=params.get(1).toString();
break;
...
case 14 : if (params.get(13) != null)
this.fourteen=params.get(13).toString();
break;
}
}

Wow, that is just, uh, stunning.

[ Addendum 20080201: A bit more. ]

[ Addendum 20090213: A counterexample. ]