How can I pass an array to a function in Perl?

arrays, function, perl

Solution

I want to pass an array to a function [...] has different output. Why did it occur?

You cannot pass an array to a function sub. Subs can only take a list of scalars as arguments.

test_func("test_func", 10, @test1);

is the same as

test_func("test_func", 10, $test1[0], $test1[1], $test1[2], $test1[3], $test1[4]);

You are creating a new array in `test_func` when you do

my ($name, $num, @array) = @_;

`shift` returns the first element of `@_`, which is necessarily a scalar. `@_` is an array, and elements of arrays are scalars. The equivalent would be

my $name  = shift(@_);
my $num   = shift(@_);
my @array = splice(@_);

To pass an array to a sub, one would normally pass a reference to it.

test_func("test_func", 10, \@test1);

my ($name, $num, $array) = @_;

my $name  = shift;
my $num   = shift;
my $array = shift;

say "@$array";

But the passed argument is changed in the function. Is it called by value?

Perl never passes by value. It always passes by reference. If you change any element of `@_`, it will change the corresponding argument in the caller.

$ perl -E'sub f { $_[0] = "def"; }  my $x = "abc"; f($x); say $x;'
def

But that's not the issue. You don't change any elements of `@_`. What you are doing is changing the single array referenced by both `$test[0]` and `$array[0]`.

This is what you are doing:

my $ref1 = [ 'a', 1 ];  # aka $test1[0]
my $ref2 = $ref1;       # aka $array[0]
$ref2->[0] = 'z';       # Changes the single array (not $ref1 or $ref2).

It's short for

my @anon = ( 'a', 1 );
my $ref1 = \@anon;      # aka $test1[0]
my $ref2 = $ref1;       # aka $array[0]
$ref2->[0] = 'z';       # Changes @anon (not $ref1 or $ref2).

Storable's `dclone` can be used to make a "deep copy" of an array.

my $ref1 = [ 'a', 1 ];
my $ref2 = dclone($ref1);  # clones the reference, the array, 'a' and 1.
$ref1->[0] = 'y';          # Changes the original array
$ref2->[0] = 'z';          # Changes the new array

Problem

Question 1: I want to pass an array to a function. But the passed argument is changed in the function. Is it called by value? Question 2: ``` #my ($name, $num, @array)= @_; <=1 ) my $name = shift; <=2 ) my $num = shift; my @array = shift; ``` Case 1 and 2 has different output. Why did it occur? ``` #!/usr/bin/perl use strict; my @test1; push @test1, ['a', 1]; push @test1, ['b', 1]; push @test1, ['c', 1]; push @test1, ['d', 1]; push @test1, ['e', 1]; for (my $i=0; $i< scalar(@test1); $i++) { print "out1: $test1[$i][0] $test1[$i][1]\n"; } test_func("test_func", 10, @test1); sub test_func { #my ($name, $num, @array)= @_; <=1) my $name = shift; <=2) my $num = shift; my @array = shift; print "$name\n"; print "$num\n"; for (my $i=0; $i< scalar(@test1); $i++) { print "$array[$i][0] $array[$i][1]\n"; } for (my $i=0; $i< scalar(@test1); $i++) { if ($array[$i][0] eq 'a') { $array[$i][0] = 'z'; } } for (my $i=0; $i< scalar(@test1); $i++) { print "change: $array[$i][0] $array[$i][1]\n"; } } for (my $i=0; $i< scalar(@test1); $i++) { print "out2: $test1[$i][0] $test1[$i][1]\n"; } ``` # Below is the test output. ``` out1: a 1 out1: b 1 out1: c 1 out1: d 1 out1: e 1 test_func 10 a 1 b 1 c 1 d 1 e 1 change: z 1 change: b 1 change: c 1 change: d 1 change: e 1 out2: z 1 <= Why did it change? out2: b 1 out2: c 1 out2: d 1 out2: e 1 ```

Original source