Search code examples
arraysperlsubroutine

Perl: splice() array using subroutine


I need a subroutine that completely removes an array element in place. The following code fails:

sub del
{
    splice(@_,2,1);
}

@array=(0..5);
print "@array"."\n";
del(@array);
print "@array"."\n";

The same array is printed again, i.e. the element has not been removed. However, if I use the splice() in the main body of the program instead of calling a subroutine, it works.


Solution

  • While the scalar elements of @_ are aliased to the data which is passed in, @_ itself is a different variable. This means $_[1] = "foo" will alter $_[1] but push @_, "foo" will not alter @_. Otherwise my $self = shift would be a Bad Thing.

    You need to pass in the array as a reference.

    sub del {
        my $array_ref = shift;
    
        splice @$array_ref, 2, 1;
    
        return;
    }
    
    del \@array;
    

    If you absolutely must keep the del @array interface, this is one of the few places where it's appropriate to use a prototype.

    sub del(\@) {
        my $array_ref = shift;
    
        splice @$array_ref, 2, 1;
    
        return;
    }
    
    del @array;
    

    The \@ prototype tells Perl to pass in @array by reference. I would recommend against doing this for two reasons. First, prototypes have a pile of caveats which make them not worth the trouble.

    More importantly, it makes it non-obvious that del will modify its arguments. Normally user defined Perl functions copy their arguments, so you can look at foo @array and be reasonably sure @array will not be altered by foo. This enables one to skim the code quickly for things which will affect a variable. A reference prototype throws this out the window. Now every function must be examined for a possible hidden pass-by-reference.