In October 2001 I surveyed uses of $#array in about 3.5 months of articles from comp.lang.perl.misc. I did this because I believed that $#array was frequently misused and was therefore a 'red flag'.
My results did not indicate that $#array was a red flag in the sense of being 'almost always wrong'. About 19% of uses of $#array were inappropriate. Typical programmers will probably get a positive payoff by stopping briefly after each $#array to consider whether it shouldn't be replaced as described below.
for ($i=0; $i<= $#array; $i++) { # do something with $array[$i] # never use $i except as part of $array[$i] }
This is almost always clearer as:
for my $element (@array) { # do something with $element }
Here $#array is used to compute the length of an array for some sort of check or comparison on the number of elements in the array. Often, @array leads to clearer code. For example:
$#ARGV <= 0 or die "Usage:\n\t$0 [logfile]\n";
This would be clearer as:
@ARGV <= 1 or die "Usage:\n\t$0 [logfile]\n";because the intent is to check for at most one argument. (The original code that this example was based on got the test wrong.) Similarly, people sometimes write $n_elements = $#array + 1 when it would be simpler to write $n_elements = @array.
$#array is sometimes used to generate the last index of @array so that the last element can be extracted with $array[$#array]. It is almost always preferable to use $array[-1].
The original sample contained 9037 articles. 173 of these articles contained the sequence $#. I examined all 173 articles and found about 192 occurrences of $#.
Not all of these were instances of $#array. Some were coincidental (included below under nocode) or appeared in discussions of the deprecated $# ($OFMT) variable. I disregarded these.
Of the remainder, many appeared in the obfuscated signatures of Damian James, Jasper McCrea, Abigail, and others. I disregarded these. I also disregarded appearances of $#array in code quoted from previous articles.
I analyzed a total of 84 occurrences of $#array. The uses broke down as follows:
indices 50 60% badfor 9 11% bound 9 11% pre-extend 7 8% badlength 6 7% badlastelt 1 1% instring 1 1% length 1 1% obfuscation 53 quoted 32 nocode 18 ofmt 5
The inappropriate uses (badfor, badlength, and badlastelt) constituted 19% of the total uses of $#array.
The results are biased because of the presence of two or three large threads that mentioned the same code repeatedly. For example, there was an extensive discussion of
my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_] ? "->$_[$_]<-" : '*UNDEF*'), 0 .. $#_;
This discussion alone contributed 14/50 in the indices category.
These large, repetitive discussions tended to contribute appropriate uses of $#array. I believe that if they were corrected for, it would appear that at least 30% of uses of $#array were inappropriate.
means for($i=0; $i<=$#array; $i++) { ... $a[$i] ... } that would have been better if it had been converted to for $elt (@array) ( ... $elt ... )
means that $#array was used in the construction $array[$#array] to access the last element. $array[-1] would have been preferable.
means that $#array was used to get the length of the array for a check or comparison, and @array would have yielded clearer code.
means that $#array was used as an upper bound in a check on a variable that was about to be used to index @array. Exception: Cases like for ($i=0; $i<=$#array; $i++), where the bound is on an array index variable that loops over all indices of an array, are categorized under indices or badfor.
means for (0 .. $#array) where it was not possible to use for $elt (@array) instead.
means that $#array was interpolated into a string to indicate an array length. This use is marginal, because the only example I found was erroneous. I counted it as an appropriate use because the error was probably unimportant.
means that the poster did not show enough code to judge what was really going on, or was talking about the use of $#array rather than actually using it, or the mention of $#array appeared in the code, but only in a comment.
means that the $#array appeared only as part of an explicitly obfuscated program, usually as part of a signature file. (Damian James, Jasper McCrea, Abigail, and Sean McAfee were the big contributors here.) When $#array appeared as part of a signature file, and there was another appearance of $#array not related to the signature, the appearance in the signature was disregarded.
was a false hit. It was the deprecated $# variable that was being discussed, not the $#array construction.
means that the $#array notation was used as an lvalue to pre-extend the size of @array.
means that the $#array appeared only in quoted code from a previous article. When $#array appeared in quoted code, and there was another appearance of $#array, the appearance in the quoted code was disregarded.
Each line represents one appearance of the string $#. The numbers are article numbers. Each is linked to the complete text of the article. If the article number is annotated with a *, that indicates that I have additional comments about the code in the article. Each appearance of $# is annotated with the categorization I assigned to it, and, where relevant, a brief extract of the original article.
0051 badfor for ($i=0; $i<=$#scores; $i++){ 0053 quoted 0054 quoted 0056 nocode This should be $#data, the array you loop over, 0057 quoted 0058 quoted 0060 nocode "$ra[ index that is > $#ra ]" evaluates to undef. 0100 indices foreach $i (0 .. $#parts) { # dump each part... 0176 indices @sorted_index = sort{ $game[$a]{'score'} <=> $gam... 0457 badfor for(my $a = 0; $a <= $#contents; $a++) { 0458 quoted 0554 indices for ($a = 0; $a <= $#SomeArray; $a++) 0555 indices for( my $a = 0; $a <= $#some_array; $a++ ){ 0569 obfuscation 0581 indices for my $a ( 0 .. $#SomeArray ) { 0581 indices for my $a ( 0 .. $#SomeArray ) { 0641 badlength $#ARGV >= 1 or die "Usage:\n\t$0 [logfile]\n"; 0687 badfor foreach $i (0 .. $#in) 0723 quoted 0767 quoted 0818 badlength $total = $total / ($#spectrum + 1); 0823 quoted 0843 quoted 0847 quoted 0927 nocode %$#@! typos. 0956 badfor foreach my $i (0 .. $#site) { 1146 indices $hash{$keys[$_]} = $vals[$_] for 0..$#keys; 1168 indices while($index <= $#foo) 1170 indices @array_index{@keys}=(0..$#keys); 1171 indices my($subscript) = grep { $item eq $foo[$_]} 0 .. $#foo; 1172 quoted 1173 indices foreach my $index (0 .. $#foo) { 1174 indices for (0..$#foo) { 1352 * indices $element_count = $#Array; ... for ($iterate = 0; $itera... 1354 * indices $element_count = $#Array; ... for ($iterate = 0; $itera... 1377 indices foreach my $index (0..$#info) { 1379 badlength if ($#{ $non_null } > -1) 1380 quoted 1490 bound if ($#list > $last_item + $args{max_display}) { 1490 bound if ($last_item >= $#list) { 1490 indices $last_item = $#list; 1490 indices $last_item = $#list; ... foreach ($first_item .... 1532 badlastelt my ($alias, $email, $bid, $time, $add1, $add2, $add3) =... 1634 quoted 1747 nocode It should be either: for (my $i=0; $i <= $#temp; $i+... 1748 nocode *grin*. What does "$i <= $#temp" mean? 1748 nocode but how can I compare a numeric value to $#temp?? What ... 1749 nocode $#temp is the last array index of @temp. By using @tem... 1764 nocode $#temp is equal to 5 [1]. $# is another (deprecated) va... 1826 nocode I was referring to $#temp when I wrote $# - meaning the... 2047 indices for( 0 .. $#children ) { 2162 badfor for $i ( 0 .. $#abc ) { 2163 quoted 2164 quoted 2165 quoted 2272 nocode the line foreach my $n (0 .. $#connection) { ... does t... 2273 quoted 2326 indices @index = @index = sort { $x[$nth1][$a] cmp $x[$nth1][$... 2326 indices @index = sort { $x[$nth][$a] cmp $x[$nth][$b] } (0..$#... 2491 quoted 2587 badfor for (0..$#files) 2588 quoted 2650 quoted 2712 bound $hi = $#words if $hi > $#words; 2991 obfuscation 2992 obfuscation 3079 badfor for my $r (0 .. $#records) { 3117 obfuscation 3564 obfuscation 3993 indices foreach (0..$#list) { 4019 * indices @seen{ map { $linkarray->[$_ & ~1] } 2 .. $#$lin... 4019 * indices map { $la->[$_ & ~1] } 2 .. $#$la; 4027 obfuscation 4056 obfuscation 4091 obfuscation 4109 * indices map "\$a->{\$sortkeys[$_]} cmp \$b->{\$sortkeys[$... 4143 obfuscation 4175 obfuscation 4214 obfuscation 4222 obfuscation 4231 obfuscation 4338 obfuscation 4342 obfuscation 4345 obfuscation 4372 nocode Your solution has the advantage over other solutions th... 4391 obfuscation 4476 indices for my $i ( 0 .. $#{ $chain[7] } ) { 4506 obfuscation 4588 indices print join ', ', map { "Arg$_ ->$_[$_]<-" } 0 .. ... 4615 obfuscation 4798 nocode # make use of $#array+1 == @array 4832 nocode $#{$a[i][j]} #works 4832 nocode $#{$a[i]} #works 4832 nocode $#{$a} #DOES NOT WORK (I usually get -1) 4859 nocode You might try $#a instead and it will work. 4889 * nocode 4890 quoted 4913 quoted 4914 quoted 4937 * length print "$_: >@array1[map 1+2*$_, 0..($#array1-1)/2]&l... 4938 badfor foreach my $b (0 .. $#array1) { 4947 obfuscation 5083 indices join ', ', map { "Arg$_ ->$_[$_]<-" } 0 .. $#_; 5122 indices join ', ', map "Arg$_ " . ( defined $_[$_] ? "->$_[$... 5123 indices print join ', ', map { "Arg$_ ->$_[$_]<-" } 0 .. ... 5123 indices print join ', ', map {defined($_[$_]) ? "Arg$_ ->$_[... 5146 obfuscation 5151 obfuscation 5173 quoted 5177 obfuscation 5210 * pre-extend $#a=0; 5212 indices my $argtmp = join ', ', map { "Arg$_ ->$_[$_]<-"... 5212 indices my $argtmp = join ', ', map {defined($_[$_]) ? "Arg$_ -... 5212 indices my $argtmp = join ', ', map {defined($_[$_]) ? "Arg$_ -... 5240 quoted 5294 obfuscation 5401 obfuscation 5444 pre-extend $#array = 1_000_000; # a million elements 5477 indices $array[$_] =~ /$val/ and splice @array, $_, 1 for rever... 5482 quoted 5535 obfuscation 5545 obfuscation 5655 obfuscation 5690 obfuscation 5692 obfuscation 5824 indices foreach $line (@lines[1..$#lines]) { 5869 obfuscation 5870 obfuscation 5953 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 5953 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 5992 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 5992 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 6027 ofmt 6028 ofmt 6100 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 6100 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 6100 indices my $argtmp = join ', ', map "Arg$_ " . ( defined $_[$_]... 6155 obfuscation 6169 obfuscation 6186 obfuscation 6209 obfuscation 6284 ofmt 6320 obfuscation 6413 ofmt 6414 ofmt 6709 bound /(\d{4})/&& $1<=$#k && s/$1/$k[$1]/ 6710 quoted 6741 bound /(\d{4})/&& $1<=$#k && s/$1/$k[$1]/; 6742 quoted 6743 quoted 6751 bound for (my $i=0; $i <= $ips_shown && $i <= $... 6767 bound /(\d{4})/&& $1<=$#k && ($a=$1,1) &am... 6789 bound /(\d{4})/&& $1<=$#k && ($_=$`.$1.$')... 6790 bound /(\d{4})/&& $1<=$#k && ($_=$`.$k[$1]... 6791 quoted 6817 pre-extend $#{$ptr->{big}}=5000000; 6949 pre-extend $#{$ptr->{big}}=5000000; 6950 pre-extend $#{$ptr->{big}}=5000000; 6984 pre-extend $#{$ptr->{big}}=2500000; 6984 pre-extend $#{$ptr->{big}}=5000000; 7114 obfuscation 7311 obfuscation 7341 obfuscation 7484 obfuscation 7580 obfuscation 7603 indices my @create = map "$names[$_] $types[$_]", 0..$#names; 7620 obfuscation 7628 obfuscation 7634 obfuscation 7641 nocode print "Index of last element in array: $#items\n"; 7653 indices while ($index <= $#dirs) { 7653 instring print "$#dirs is the length of the array \n"; 7658 quoted 7746 obfuscation 7782 badlength shift @lines if $#lines > 5; 7968 indices for my $i ( 0 .. $#words ) { 8197 obfuscation 8227 obfuscation 8272 * indices for($r = 0;$r <= $#array;$r++) 8319 indices my %line_num = map {$line_break[$_] => $_} 0..$#line... 8470 badfor for my $i (0..$#data) { 8501 indices for( my $i = $#names; $i >= 2; --$i ) { 8534 obfuscation 8573 badlength if ( $#ARGV == -1 ) 8573 badlength if ( $#tokenRegex < 0 ) 8592 quoted 8608 indices @line_num{@line_break} = 0..$#line_break; 8698 indices for my $i ( 0..$#data ) { 8742 obfuscation 8743 obfuscation 8823 obfuscation 8920 obfuscation
Return to: Universe of Discourse main page | Perl Paraphernalia | Classes and Talks | Program Repair Shop and Red Flags
mjd-perl-yak@plover.com