views:

575

answers:

5

I've come across the following line of code. It has issues:

  • it is intended to do the same as push
  • it ought to have used push
  • it's hard to read, understand
  • I've since changed it to use push
  • it does something I thought was illegal, but clearly isn't

here it is:

$array [++$#array] = 'data';

My question is: what does it mean to pre-increment $#array? I always considered $#array to be an attribute of an array, and not writable.

+3  A: 

Assigning a value larger than the current array length to $#array extends the array.

Manu
I believe the link is to an unauthorized copy of a book. I would suggest you remove it.
mirod
Actually, the link is already dead, but I agree that it's a bad idea to link to pirated books.
Telemachus
It wasn't dead when I checked it, and there were a whole bunch of other books there too.
mirod
The link is still up BTW.
mirod
Welcome to the Internet.Why should Manu be punished for linking to something he found via Google?
jrockway
"While these pirated copies are annoying, they hardly destroy our business...many of those who do infringe respond to...a polite letter. Those servers that ignore our requests are typically in countries where the books are not available...or...far too expensive" - some guy named Tim :)
ysth
(but I agree that removing the link is appropriate)
ysth
Thx to brian for removing the link. Just wanted to make it clear that, as jrockway mentioned, I found the link through Google being completely unaware that anything was wrong with it. I wonder though if the zealousy with which people started to downvote the answer isn't a little bit exaggerated.
Manu
+3  A: 

This code works too:

$ perl -le 'my @a; $a[@a]="Hello"; $a[@a]=" world!"; print @a'
Hello world!

Perl array is dynamic and grows when assign beyond limits.

Hynek -Pichi- Vychodil
I don't see how ++$#array wouldn't change it. I assume that after the assignment, $#array is modified again, probably to the same value.
Paul Beckingham
I think that `++$#array` is useless here.
Hynek -Pichi- Vychodil
You do need the ++$#array, if you use $#array you will simply overwrite the last element of the array (or get a 'Modification of non-creatable array value attempted, subscript -1' error if the array is empty)
mirod
The writeback part of the preincrement is indeed useless, but a `+1` operation is necessary to compute the correct array index (considering you really really want to do it by subscripting an array instead of pushing). FWIW, `++` is quite easy to type. How about $a[@a] = 1 for additional confusion?
JB
How odd: @a[--$|+$#a]=$_ for 0..9; print @a
ysth
+1  A: 

First of all, that's foul.

That said, I'm also surprised that it works. I would have guessed that ++$#array would have gotten the "Can't modify constant" error you get when trying to increment a number. (Not that I ever accidentally do that, of course.) But, I guess that's exactly where we were wrong: $#array isn't a constant (a number); it's a variable expression. As such you can mess with it. Consider the following:

my @array = qw/1 2 3/;

++$#array;
$array[$#array] = qw/4/;

print "@array\n"

And even, for extra fun, this:

my @array = qw/1 2 3/;

$#array += 5;

foreach my $wtf (@array) {
  if (defined $wtf) {
    print "$wtf\n";
  }
  else {
    print "undef\n";
  }
}

And, yeah, the Perl Cookbook is happy to mess with $#array to grow or truncate arrays (Chapter 4, recipe 3). I still find it ugly, but maybe that's just a lingering "but it's a number" prejudice.

Telemachus
I'm not sure about you but *I* think it's ugly because it has 4 punctuation symbols, that aren't separators or parens, in a row.... I mean, I also thing a = b + (foo ?-$#array:+$#array); is ugly B-)
Brian Postow
I too was surprised it worked.
Paul Beckingham
+14  A: 

perldata says:

"The length of an array is a scalar value. You may find the length of array @days by evaluating $#days , as in csh. However, this isn't the length of the array; it's the subscript of the last element, which is a different value since there is ordinarily a 0th element. Assigning to $#days actually changes the length of the array. Shortening an array this way destroys intervening values. Lengthening an array that was previously shortened does not recover values that were in those elements."

Modifying $#array is useful in some cases, but in this case, clearly push is better.

ysth
One of the cases where it would be better is when you know in advance how big your array will be at the end. With huge arrays this will give you a big performance boost.
innaM
On older Perls lengthening using $#array, would get back previous values.
Brad Gilbert
That was perl4, I believe.
ysth
+4  A: 

A post-increment will return the variable first and then increment it.

If you used post-increment you would be modifing the last element, since its returned first, and then pushing an empty element onto the end. On the second loop you would be modifing that empty value and pushing a new empty one for later. So it wouldn't work like a push at all.

The pre-increment will increment the variable and then return it. That way your example will always being writing to a new, last element of the array and work like push. Example below:

my (@pre, @post);

$pre[$#pre++] = '1';
$pre[$#pre++] = '2';
$pre[$#pre++] = '3';


$post[++$#post] = '1';
$post[++$#post] = '2';
$post[++$#post] = '3';

print "pre keys: ".@pre."\n";
print "pre: @pre\n";
print "post keys: ".@post."\n";
print "post: @post\n";

outputs:

pre keys: 3
pre: 2 3
post keys: 3
post: 1 2 3
10rd_n3r0
The "return" value of the increment operator(s) is an important distinction imo, I'm glad someone made it.
jettero
@pre = ( 2, 3, undef );
Brad Gilbert