How not to count the elements in an array

Ovid on 2006-07-14T14:54:09

Due to a question asked at work, I remembered that I see a lot of Perl books (PERL books, actually), recommend the following as a way of counting the elements in an array:

my $count = $#array + 1;

On the off chance you do this, it's wrong. $count is now assigned a value one greater than the last index value, not the number of elements in the array. Almost every single time you do this, you're probably going to get the answer you expect, but because you're using something for a purpose other than what it was intended for, you should not be surprised when it doesn't always do what you want.

$ perl -le '$[ = 4; my @a = qw; print $#a + 1'

Sure, you could argue that you will never alter the contents of $[, so you're therefore safe. The problem is, you know that sometimes that can give the wrong answer. Why use something you know won't always work? I don't get it. Just use context. It's both correct and easier to read.

my $count = @array;
# or, if you must be explict:
print "We have ", scalar @orders, " order(s)";


Perly Warts

ziggy on 2006-07-14T16:08:48

This one (mis-)feature of Perl is one of my least favorite.

The problem is that $[ was added for one specific purpose: to vary the base index between 0 and 1 as an aid to Fortran programmers who had trouble adjusting to the world of zero based indexing. Most people don't realize that $[ exists, or why it exists, sadly.

The issue here is that the idiom for counting elements in an array should be $count = @array. However, the $count = $#array + 1 idiom (mostly) works, and meets another criteria Perl aims to fufill: it should be comfortable to use regardless of your background. If you're a dyed in the wool C programmer, you should feel free to use a C-like pidgin of Perl and C (including $c = $#a + 1). However, you should be encouraged, but not required to adopt Perlish idioms at your own pace (including $c = @a).

But because $[ exists, it could be set to some nonsensical value like 3 or -4. That's possible, but quite improbable. Using an argument about something improbable to convince someone to stop using a C-like idiom and start using a native Perl idiom is, well, unconvincing.

For comparison, consider the task of building a soundproof room. You could take into account all of the ambient noise, all of the vibration from the surroundings, and so forth to make a room mostly soundproof. However, if the sun were to go supernova, the pressure wave would hit the earth at 300db. But do you really need to isolate your soundproof room from this? Not really. First of all, if a 300db soundwave went past your soundproof room, you wouldn't hear it, because the bones in your ear start to break down at 160db. Second, the probably won't go supernova, and any kind of explosion would be billions of years in the future (i.e., not in your lifetime). Third, even if the sun did go supernova, dealing with busted ears is going to be the least of your problems. :-)

If you want to convince people from using $c = $#a + 1, please find a better, less improbable argument, or just accept that it exists for a reason (albeit a dubious one).

Re:Perly Warts

sigzero on 2006-07-14T16:31:46

If it (mostly) works then you should tell them how it always works. No?

It's not any worse than...

bart on 2006-07-14T16:55:34

... looping through the index for all elements in an array using
for my $i (0 .. $#array) {
    ...
}
Again, the code is written under the assumption that the first array element has index 0.

Re:It's not any worse than...

jmcada on 2006-07-14T18:25:30

Eeek, so our loops that require indexes should look like this?

for my $i ($[ .. $#array) { ... }

Wow, I have a lot of almost working code :)

Re:It's not any worse than...

jdavidb on 2006-07-14T20:52:25

Worse yet, you should almost never use that index. Try to do

foreach my $elem (@array)
if at all possible. The only time I use an index is when I have two parallel arrays. Using this other idiom completely eliminates the index: you just access the things you want to get to directly.

Re:It's not any worse than...

Ovid on 2006-07-14T19:38:20

Ugh. My beautiful argument is lying there twitching. I'll shut up now.

Won't Always Work

chromatic on 2006-07-14T17:53:15

Why use something you know won't always work?

Good luck with that argument. Meanwhile, my campaign to get people to call methods as methods, not functions, has stalled.

OTOH...

Abigail on 2006-07-16T00:47:29

There is a small advantage of using $#array + 1 over @array: the former will give you the number of elements regardless of its context (assuming $[ isn't set). @array won't give you the number of elements when used in list context.

And I bet more people get bitten by an unexpected context, then by $[ being set to a non-zero value.

Nooo! Don't tell people to fix this!

Adrian on 2006-07-16T20:49:48

I'll lose one of my major "I really need to look at this code closely" flags :-)