19

I think I have misunderstood some aspects of argument passing to functions in Perl. What's the difference between func(\@array) and func(@array)?

AFAIK, in both functions, arguments are passed by reference and in both functions we can change the elements of @array in the main program. So what's the difference? When should we use which?

@array = (1,2,3);
func(@array);
func(\@array);

sub func {
    ...
}

Also, how do I imitate pass-by-value in Perl? Is using @_ the only way?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
aminfar
  • 2,297
  • 3
  • 27
  • 37

3 Answers3

22

It's impossible to pass arrays to subs. Subs take a list of scalars for argument. (And that's the only thing they can return too.)

You can pass a reference to an array:

func(\@array)

You can pass the elements of an array:

func(@array)

When should we use which?

If you want to pass more than just the elements of the array (e.g. pass $x, $y and @a), it can become tricky unless you pass a reference.

If you're going to process lists (e.g. sum mysub grep { ... } ...), you might not want to pass a reference.

If you want to modify the array (as opposed to just modifying the existing elements of the array), you need to pass a reference.

It can be more efficient to pass a reference for long arrays, since creating and putting one reference on the stack is faster than creating an alias for each element of a large array. This will rarely be an issue, though.

It's usually decided by one of the first two of the above. Beyond that, it's mostly a question of personal preference.


Also, how do I imitate pass-by-value in Perl?
sub foo {
   my ($x) = @_;   # Changing $x doesn't change the argument.
   ...
}

sub foo {
   my @a = @_;   # Changing @a or its contents
   ...           #    doesn't change the arguments.
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • `(And that's the only thing they can return too.)` no, they can return arrays. To see it check out the difference between `sub { return (qw/a b c/);}` and `sub { @array = (qw/a b c/); return @array }` in scalar context. This first returns `c`, the second returns `3`, the number of elements in the returned array!!! – Joel Berger Aug 30 '11 at 03:14
  • 1
    @Joel, no they can't return arrays. `sub { return (qw/a b c/);}` in scalar context returns one scalar ("c"), and `sub { @array = (qw/a b c/); return @array }` in scalar returns one scalar (3). The scalar is returned by `qw` or `@array` before `return` is even evaluated. – ikegami Aug 30 '11 at 04:54
  • @TLP, There must have been an error in your test. Try `perl -E"say scalar sub { return (qw/a b c/);}->()"` – ikegami Aug 30 '11 at 04:55
  • @Joel, You can see what happens when you return an array using this XS code: `void f() { dXSARGS; EXTEND(SP, 1); ST(0) = newAV(); XSRETURN(1); }`. If you do `$x = f();`, you get `Bizarre copy of ARRAY in scalar assignment`. – ikegami Aug 30 '11 at 05:18
14

AFAIK, in both functions, arguments are passed by reference and in both functions we can change the elements of @array in the main program.

"change the elements of", yes. However, in the func(@array) case, the sub has no means to make other changes to the array (truncating it, pushing, popping, slicing, passing a reference to something else, even undef'ing it).

I would avoid using the term "passed by reference", since the mechanism is completely different than Perl's references. It is less overloaded :) to say that in the sub, @_'s elements start off aliased to the elements passed to the sub.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ysth
  • 96,171
  • 6
  • 121
  • 214
4

func(\@array) passes a reference. func(@array) passes a list (of the elements in @array). As Keith pointed out, these elements are passed by reference. However, you can make a copy inside the sub in order to pass by value.

What you are after is this:

sub func {
    my @array = @_;
}

This will pass a copy of the arguments of func to @array, which is a local variable within the scope of the subroutine.

Documentation here

TLP
  • 66,756
  • 10
  • 92
  • 149
  • 1
    Actually `func(@array)` passes the elements of `@array` by reference. For example: `sub func { map { $_ ++ } @_; } } my @arr = ( 10, 20, 30); func(@arr);` will set `@arr` to `(11, 21, 31)` -- and `func(10, 20, 30)` will fail with "Modification of a read-only value attempted". One purpose of the `my @arr = @_;` idiom is to copy the `@_` values to a local array, to achieve the effect of pass-by-value. – Keith Thompson Aug 30 '11 at 01:44
  • 1
    As `perldoc perlsub` says: "Any arguments passed in show up in the array `@_`. Therefore, if you called a function with two arguments, those would be stored in `$_[0]` and `$_[1]`. The array `@_` is a local array, but its elements are aliases for the actual scalar parameters." – Keith Thompson Aug 30 '11 at 01:46
  • re "by reference", see my answer – ysth Aug 30 '11 at 02:36