Eugen Konkov
Eugen Konkov

Reputation: 25223

Why shifted value from @_ is not alias?

shift
Shifts the first value of the array off and returns it...

This is done for speed optimization and to avoid copying by value.

Also in perlsub

The array @_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated

Thus if we do my $self = shift in our sub, we shift the first value from @_, which is an alias, is it not?

But when we compare these two:

sub test {
     print \$_[0];    # SCALAR(0xf73c38)
     my $x =  shift;
     print \$x;       # SCALAR(0xf79800)
}

We see that $x is the copy.

Why is the shifted value from @_ not an alias?

So if the value is copied for the case my $x = shift too, what benefit does it provide over my $x = $_[0]?

Upvotes: 3

Views: 177

Answers (2)

ikegami
ikegami

Reputation: 386461

shift isn't an lvalue operator.

$ perl -e'
   use feature qw( say );
   sub f { shift = 456; }
   { my $x = 123; f($x); say $x; }
'
Can't modify shift in scalar assignment at -e line 3, near "456;"
Execution of -e aborted due to compilation errors.

However, the values shifted from @_ are aliases nonetheless.

$ perl -e'
   use feature qw( say );
   sub f { my $p = \shift; $$p = 456; }
   { my $x = 123; f($x); say $x; }
'
456

or

$ perl -e'
   use feature qw( say );
   sub f { ${ \shift } = 456; }
   { my $x = 123; f($x); say $x; }
'
456

The problem is that you assigned the value of the aliased array element to $x rather than aliasing $x to array element. If you want to name the element of @_, you will need to alias a variable to it.

$ perl -e'
   use feature qw( say );
   sub f { our $x; local *x = \shift; $x = 456; }
   { my $x = 123; f($x); say $x; }
'
456

or

$ perl -e'
   use feature qw( say );
   use Data::Alias qw( alias );
   sub f { alias my $x = shift; $x = 456; }
   { my $x = 123; f($x); say $x; }
'
456

or

$ perl -e'
   use feature qw( say refaliasing );
   no warnings qw( experimental::refaliasing );
   sub f { \my $x = \shift; $x = 456; }
   { my $x = 123; f($x); say $x; }
'
456

The benefits of my $x = shift over my $x = $_[0]; are numerous, although minor.

  • shift is easier to type than $_[0].
  • When there are more than one parameter, you can use shift for each rather than keeping count.
  • shift supports complex and open-ended parameters lists. (e.g. sub f { my $cb = shift; $cb->($_) for @_; })
  • shift is microscopically faster than $_[0] (IIRC).

Upvotes: 3

mob
mob

Reputation: 118665

The shifted value is an alias

$ perl -E 'sub F{say \$_[0]; say \shift} $x=42; say \$x; F($x)'
SCALAR(0x1d7f1e0)
SCALAR(0x1d7f1e0)
SCALAR(0x1d7f1e0)

The assignment operation (e.g., $x = $_[0] or $x = shift) creates a copy of the scalar on the right hand side, so the newly assigned value is no longer an alias.

As toolic said, the benefit of shift is to modify @_, which sometimes makes it easier to use in the rest of your subroutine.

You could still work with a reference to the shifted value if you still wanted to be able to modify the input

$ perl -E 'sub G { my $x=\shift; $$x = 19 } my $z = 42; G($z); say $z'
19

Upvotes: 9

Related Questions