blokhead has asked for the wisdom of the Perl Monks concerning the following question:

A comment in the CB made me wonder if I could write $#{$#a} for an array @a and have it actually do something. For this, I would need $#a to actually be an array reference. Much to my surprise, this could actually be accomplished! (perl, v5.8.3 built for i386-linux-thread-multi)

Of course, setting $#a=$b has the (side-)effect of expanding @a to a huge size, so these examples take a few seconds (or may run out of memory even):

% perl -le '$b=[1..4]; $#a=$b; print $#{$#a}' 3
Can this actually be doing what I think it is?? Indeed, it looks as though $#a is actually the same reference as $b$:
% perl -le '$b=[1..4]; $#a=$b; print @{$#a}; push @$b, 5; print @{$#a} +' 1234 12345
In fact, this also works when $b is lexical, so it's not a symbolic reference.

You can put other types of references into $#a, and dereference them in any standard way:

% perl -le '$b=[1..4]; $#a=$b; print $#a->[2]' 3 % perl -le '$b=\"foo"; $#a=$b; print ${$#a}' foo % perl -le '$b={foo=>1}; $#a=$b; print keys %{$#a}' foo
However, you cannot preserve blessed-ness of a scalar:
% perl -le 'sub foo{"foo"} $b=bless[]; $#a=$b; print $#a->foo' Can't call method "foo" without a package or object reference at -e li +ne 1.
This is our first clue that this is not a fully-fledged reference.. we also see that ref is oblivioius to it:
% perl -le '$b=[1..4]; $#a=$b; print ref $#a' [nothing]
Also, I can't copy $#a into a variable and then use that variable as a reference:
% perl -le '$b=[1..4]; $#a=$b; $x=$#a; print @$x' [nothing]
In fact, after I copy $#a into a variable, I can't even use $#a itself as a reference anymore! Hello Heisenberg!
% perl -le '$b=[1..4]; $#a=$b; $x=$#a; print @{$#a}' [nothing]
Very odd.

So, Does anyone know what's going on? I would have expected this to behave either kinda like hash keys (which are always string values, and are not retrieved as full scalars with the requisite magic) or just a normal scalar. What seems to be happening to $#a is a little bit of both.

Remember, this is purely for curiosity's sake, as using $#a as a reference is quite mad.

PS: It is a damn shame that the following code runs out of memory, it would make quite a bizarre JAPH:

#/usr/bin/perl -l my@a=qw[just another perl hacker];$#a=\@a; print "@{$#{$#{$#{$#{$#{$#{$#{$#a}}}}}}}}"
(the out of memory error I get is another mystery -- if I replace the @ with an $#, I don't crash)

blokhead

Replies are listed 'Best First'.
Re: Putting weird things into $#array
by chromatic (Archbishop) on Mar 07, 2005 at 21:50 UTC

    Consider:

    my @a = qw( 1 2 3 ); print $#a + 1, $/; my $a = \@a; print $a + 1, $/;

      I don't see anything wrong. It prints what I expected.

      my @a = qw( 1 2 3 ); print $#a + 1, $/; # 3 my $a = \@a; print $a + 1, $/; # 27993901 print $#$a + 1, $/; # 3 print $a, $/; # ARRAY(0x1ab272c) printf "0x%x == %d$/", $a, $a; # 0x1ab272c == 27993900
      This is perl, v5.6.1 built for MSWin32-x86-multi-thread
      I'm well aware that a reference in numeric context is a very large number. I also understand that setting $#a to a reference makes @a really really big. That doesn't explain why $#a is behaving so oddly when I retrieve its value.

      I would expect

      $#a = $foo; $#a = $foo + 0;
      to be the same thing. Just like how
      $hash{$foo} = 1; $hash{"".$foo} = 1;
      is the same (unless %hash is tied). But this is not the case -- when $foo is a reference and I retrieve $#a, it is also acting much like (but not entirely like) a reference, not just a number.

      blokhead

        That doesn't explain why $#a is behaving so oddly when I retrieve its value.

        When you set $#a, Perl needs an integer. If you give it something that's more than just an integer, it takes out the integer part and stores that in the appropriate place in the data structure it uses to represent the array. It throws out the rest of the information, as it doesn't need it.

        When you read $#a, Perl looks in the underlying data structure and fetches the integer stored there. It returns it. It can't return any more information. It doesn't store it.

        This is C-level stuff. If you're curious, see av.h, specifically the xpvav struct declaration and the xav_fill member. It holds an integer (see uconfig.h, I think), not an SV.

        Update: I did misread the question. The right code to read is that of the av2arylen opcode in pp.c, which I really don't follow. Consider this program though, which shows that $#a isn't a plain integer, it's an SV with magic:

        use Devel::Peek; my @a; Dump( $#a ); my $b = {foo=>1}; exit if $b + 0 > 0x10125000; $#a=$b; Dump( $#a );
      I'm not sure whether to be impressed or freightened.
        You can't be freightened unless you are first palletized (and preferably shrink-wrapped if you have a tendency to flop about).

        ;-)
Re: Putting weird things into $#array
by betterworld (Curate) on Mar 07, 2005 at 23:21 UTC
    ...made me wonder if I could write $#{$#a} for an array @a and have it actually do something.

    If you just want $#{$#a} to make sense, then there is another possibility:

    As we are allowed to name arrays with numbers, $#a could in fact be the name of an array. Not using strict, you may use $#{$#a} to get the highest index of that very array:

    @a = (0..4); @4 = (0..17); print $#{$#a};

    This in fact prints 17, the highest index of the array @4.

Re: Putting weird things into $#array
by Roy Johnson (Monsignor) on Mar 07, 2005 at 22:37 UTC
    There was a thread not too long ago, which I can't find, in which a poster used some module or feature to dump the internal representation of a scalar, showing its numeric value and string values separately. It wasn't Data::Dumper.

    The thread was about the difference between

    print $i, "\n"; # and print "$i\n";
    It seems like dumping out $#a in a few of these situations would be interesting.

    Caution: Contents may have been coded under pressure.
      I remember that thread.

      If I'm not mistaken, print "$var\n" or print $var, "\n" looks like the thread, and Re: print "$var\n" or print $var, "\n" would be the post that uses Devel::Peek's Dump function to show the variable's internal state.

      Here are results of perl -MDevel::Peek -le "$b=[1,2,3]; $#a=$b;Dump $b;Dump $#a"

      The SV slot for both variables hold a reference to the anonymous array (PVAV(0x15d674c) at 0x15d50e0), and "SV = PVMG" means either "magic is attached or the value is blessed"1 The first SV is $b, the second SV is $#a.

      Maybe someone with a little more knowledge of perl internals can help interpret this...

      1 From PerlGuts Illustrated

        That isn't the thread. I don't know for sure whether it's the utility that was being used. In the thread I'm thinking of, one poster noted (using some utility) the memory consumed by a program that interpolates numbers into a string to print vs. the memory consumed by a program that passes the numbers as separate arguments to print. The respondant dumped the value of an interpolated number and a non-interpolated one, showing that the memory overhead was due to having stored a stringified value.

        Does that sound familiar at all? I know it wasn't very many weeks ago.


        Caution: Contents may have been coded under pressure.