d9h
d9h

Reputation: 89

Modify subroutine parameter (Perl)

I want to write a Perl subroutine first which takes a list as an input, returns the first element in the list and removes the first element from the list.

Like this:

@list = (1,2,3);
print first(@list); // 1
print @list; // 23

This is not quite working:

sub first(@) {
    return shift @_;
}

What I get is:

print first(@list); // 1
print @list; // 123

The stack variable @_ changes the way I expect it to (first it is (1, 2, 3) then it is (2, 3)), but the list I give as an input (@list) is not changed. I thought the stack variable saves a reference to the variable it refers to.

When I change a list element in the subroutine it also changes something in @list but not the one I wanted to, but that one + 1. So if I in the subroutine I were to write:

@_[0] = "X";

and after executing the subroutine print @list, I would get 2X6.

Upvotes: 2

Views: 268

Answers (2)

Dave Cross
Dave Cross

Reputation: 69314

You don't have lists there, you have arrays. Arrays and lists are different in Perl (as this great blog post explains). If you have an array called @list then you're just guaranteed to confuse yourself (and whoever maintains your code in the future).

Upvotes: 1

Håkon Hægland
Håkon Hægland

Reputation: 40778

You need to put a slash in front of the @ prototype to get an array reference, and then modify the reference. If you just use @ you will get a copy of the array @list in the subroutine (and the array in the parent would therefore not be modified). From http://perldoc.perl.org/perlsub.html#Prototypes:

Unbackslashed prototype characters have special meanings. Any unbackslashed @ or % eats all remaining arguments, and forces list context.

So you could write:

use strict;
use warnings;

sub first (\@) {
    my $a = shift;
    return shift @$a;
}

my @list = (1,2,3);
print first(@list) . "\n"; 
print "@list" . "\n"; 

Output:

1
2 3

Upvotes: 2

Related Questions