
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