Search code examples
perlparameterssubroutine

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.


Solution

  • 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