19

In this question the poster asked how to do the following in one line:

sub my_sub {
    my $ref_array = shift;
    my @array = @$ref_array;
}

which with my knowledge of the basic Perl magic I would avoid by simply using something like:

sub my_sub {
    my $ref_array = shift;
    for (@$ref_array) {
      #do somthing with $_ here
    };

    #use $ref_array->[$element] here
}

However in this answer one of SO's local monks tchrist suggested:

sub my_sub {
  local *array = shift();
  #use @array here
}

When I asked

In trying to learn the mid-level Perl magic, can I ask, what is it that you are setting to what here? Are you setting a reference to @array to the arrayref that has been passed in? How do you know that you create @array and not %array or $array? Where can I learn more about this * operator (perlop?). Thanks!

I was suggested to ask it as a new post, though he did give nice references. Anyway, here goes? Can someone please explain what gets assigned to what and how come @array gets created rather than perhaps %array or $array? Thanks.

Community
  • 1
  • 1
Joel Berger
  • 20,180
  • 5
  • 49
  • 104
  • You can probably glean several important insights into typeglobs from [this answer](http://stackoverflow.com/questions/3807231/how-can-i-test-if-i-can-write-to-a-filehandle/4200474#4200474), none of which have yet been touched upon here. – tchrist Feb 01 '11 at 17:47

2 Answers2

24

Assignment to a glob

*glob = VALUE

contains some magic that depends on the type of VALUE (i.e., return value of, say, Scalar::Util::reftype(VALUE)). If VALUE is a reference to a scalar, array, hash, or subroutine, then only that entry in the symbol table will be overwritten.

This idiom

local *array = shift();
#use @array here

works as documented when the first argument to the subroutine is an array reference. If the first argument was instead, say, a scalar reference, then only $array and not @array would be affected by the assignment.

A little demo script to see what is going on:

no strict;

sub F {
  local *array = shift;

  print "\@array = @array\n";
  print "\$array = $array\n";
  print "\%array = ",%array,"\n";
  print "------------------\n";
}

$array = "original scalar";
%array = ("original" => "hash");
@array = ("orignal","array");

$foo = "foo";
@foo = ("foo","bar");
%foo = ("FOO" => "foo");

F ["new","array"];        # array reference
F \"new scalar";          # scalar reference
F {"new" => "hash"};      # hash reference
F *foo;                   # typeglob
F 'foo';                  # not a reference, but name of assigned variable
F 'something else';       # not a reference
F ();                     # undef

Output:

@array = new array
$array = original scalar
%array = originalhash
------------------
@array = orignal array
$array = new scalar
%array = originalhash
------------------
@array = orignal array
$array = original scalar
%array = newhash
------------------
@array = foo bar
$array = foo
%array = FOOfoo
------------------
@array = foo bar
$array = foo
%array = FOOfoo
------------------
@array =
$array =
%array =
------------------
@array = orignal array
$array = original scalar
%array = originalhash
------------------

Additional doc at perlmod and perldata. Back in the days before references were a part of Perl, this idiom was helpful for passing arrays and hashes into subroutines.

mob
  • 117,087
  • 18
  • 149
  • 283
  • Don't forget what happens when the RHS to a typeglob assignment happens to be neither a typeglob nor a reference but string. Also consider what happens when the operand of the `*` sigil is a string not a bareword. Finally, consider `local`. – tchrist Feb 01 '11 at 17:30
  • 1
    As this answer shows, assignment to a glob fills different slots depending on the reference type. You can tersely ensure that you have the proper type like so: `local *array = \@{shift @_}`. The reference/dereference pair `\@` is transparent if the ref is an array, but will throw an error otherwise. – Eric Strom Feb 01 '11 at 17:38
  • I am still trying to read some of the things in the link that tchrist posted in the comment to the question, however, this answers my actual question very well. Thanks! and I think I am understanding at least some of the basics of the symbol table to boot! – Joel Berger Feb 01 '11 at 18:31
3

With my admittedly less-than-wizard knowledge of Perl, I'll venture an answer. The * operator assigns the symbol table entry. As I understand it, @array, %array, and $array all refer to the same symbol table entry for the string 'array', but to different fields in that entry: the ARRAY, HASH, and SCALAR fields. So assigning local *array = shift; actually assigns the entire local symbol table entry for 'array' (including the ARRAY, HASH, and SCALAR fields) to what was passed used in the caller.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • 1
    If the thing assigned is a glob or a globref, yes; otherwise it's more complicated. Actually, its more complicated anyway, because a symbol table entry is like a "glob variable" that holds a "glob value", and the assignment sets the value part, not the variable itself. – ysth Feb 01 '11 at 19:43