Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: Special array @_ is not really an alias?

Tags:

perl

The special array, @_ , where all the arguments passed to a function are present, is actually an alias to the arguments passed. Hence, any change we make directly to this special array @_ will reflect in the main as well. This is clear.

#!/usr/bin/perl

use warnings;
use strict;

$\="\n";

sub func {
        print \@_;
        $_++ for(@_);
}

my @arr=(2..4);
print \@arr;
func(@arr);
print "@arr";

For the above program, I expected the reference of @arr and @_ to point to the same location since it is an alias. But it is not so.

On running the above:

ARRAY(0x1b644d0)
ARRAY(0x1b644e0)
3 4 5

If they are pointing to 2 different locations, how the changes done in @_ is reflecting in @arr?

Am I seeing something wrong? Please advice.

like image 753
Guru Avatar asked Mar 07 '13 04:03

Guru


2 Answers

This might answer you question:

use warnings;
use strict;

$\="\n";

sub func {
        print \@_;
        $_++ for(@_);
        print \$_ for @_; 
}

my @arr=(2..4);
print \@arr;
func(@arr);
print "@arr";
print \$_ for @arr;

Output

ARRAY(0x17fcba0)
ARRAY(0x1824288)
SCALAR(0x17fcc48)
SCALAR(0x18196f8)
SCALAR(0x1819710)
3 4 5
SCALAR(0x17fcc48)
SCALAR(0x18196f8)
SCALAR(0x1819710)

As you see, individual arguments have the same address but the container is not the same. If you push an item to @_ in func the @arr will not change (so you can do shift in funct). So, each argument is an alias and array elements are passed as individual items. @_ contains all items passed into the subroutine. If you want to modify an array argument you need to pass it by reference.

like image 128
perreal Avatar answered Nov 17 '22 06:11

perreal


@_ isn't aliased; its elements are.

Remember that

func(@arr);

is the same as

func($arr[0], $arr[1], ...);

because the only thing that can be passed to a sub is a list of scalars, and an array evaluates to a list of its elements in list context.

So that means

func(@arr);

is basically the same as

local @_;
alias $_[0] = $arr[0];
alias $_[1] = $arr[1];
...
&func;

Changing the elements of @_ will change elements of @arr, but adding and removing elements of @_ won't change @arr since they are different arrays.

>perl -E"@a=(4..6); sub { $_[0] = '!';        say @_; }->(@a); say @a;"
!56
!56

>perl -E"@a=(4..6); sub { splice(@_,0,1,'!'); say @_; }->(@a); say @a;"
!56
456
like image 11
ikegami Avatar answered Nov 17 '22 05:11

ikegami