Search code examples
perlglob

Demystifying the Perl glob (*)


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.


Solution

  • 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.