views:

3043

answers:

7

I often have a subroutine in Perl that fills an array with some information. Since I'm also used to hacking in C++, I find myself often do it like this in Perl, using references:

my @array;
getInfo(\@array);

sub getInfo {
   my ($arrayRef) = @_;
   push @$arrayRef, "obama";
   # ...
}

instead of the more straightforward version:

my @array = getInfo();

sub getInfo {
   my @array;
   push @array, "obama";
   # ...
   return @array;
}

The reason, of course, is that I don't want the array to be created locally in the subroutine and then copied on return.

Is that right? Or does Perl optimize that away anyway?

+8  A: 

What about returning an array reference in the first place?

sub getInfo {
  my $array_ref = [];
  push @$array_ref, 'foo';
  # ...
  return $array_ref;
}

my $a_ref = getInfo();
# or if you want the array expanded
my @array = @{getInfo()};

Edit according to dehmann's comment:

It's also possible to use a normal array in the function and return a reference to it.

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return \@array;
}
That sounds like the best solution to me!
R. Bemrose
Actually, how about creating a real array in the function, but having it return a reference to it? Perl would keep the locally created array alive and return a reference efficiently.
@dehmann: good point, I incorporated your comment into my answer, thanks.
A: 

Hi

I know nothing about Perl so this is a language-neutral answer.

It is, in a sense, inefficient to copy an array from a subroutine into the calling program. The inefficiency arises in the extra memory used and the time taken to copy the data from one place to another. On the other hand, for all but the largest arrays, you might not give a damn, and might prefer to copy arrays out for elegance, cussedness or any other reason.

The efficient solution is for the subroutine to pass the calling program the address of the array. As I say, I haven't a clue about Perl's default behaviour in this respect. But some languages provide the programmer the option to choose which approach.

High Performance Mark
The "address of the array" in Perl is a reference. The question is whether Perl optimizes for it.
Max Lybbert
+7  A: 

Passing references is more efficient, but the difference is not as big as in C++. The argument values themselves (that means: the values in the array) are always passed by reference anyway (returned values are copied though).

Question is: does it matter? Most of the time, it doesn't. If you're returning 5 elements, don't bother about it. If you're returning/passing 100'000 elements, use references. Only optimize it if it's a bottleneck.

Leon Timmermans
+1  A: 

To answer the final rumination, no, Perl does not optimize this away. It can't, really, because returning an array and returning a scalar are fundamentally different.

If you're dealing with large amounts of data or if performance is a major concern, then your C habits will serve you well - pass and return references to data structures rather than the structures themselves so that they won't need to be copied. But, as Leon Timmermans pointed out, the vast majority of the time, you're dealing with smaller amounts of data and performance isn't that big a deal, so do it in whatever way seems most readable.

Dave Sherohman
+6  A: 

If I look at your example and think about what you want to do I'm used to write it in this manner:

sub getInfo {
  my @array;
  push @array, 'obama';
  # ...
  return \@array;
}

It seems to me as straightforward version when I need return large amount of data. There is not need to allocate array outside sub as you written in your first code snippet because my do it for you. Anyway you should not do premature optimization as Leon Timmermans suggest.

Hynek -Pichi- Vychodil
+1  A: 

This is the way I would normally return an array.

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return @array if wantarray;
  return \@array;
}

This way it will work the way you want, in scalar, or list contexts.

my $array = getInfo;
my @array = getInfo;

$array->[0] == $array[0];

# same length
@$array == @array;

I wouldn't try to optimize it unless you know it is a slow part of your code. Even then I would use benchmarks to see which subroutine is actually faster.

Brad Gilbert
Then you can't get the count by assigning getInfo() to a scalar value. http://perlmonks.org/?node_id=729965 has an interesting debate about the use of wantarray.
daotoad
I agree, I had used to using `wantarray` about three years ago. I had tough it is cool feature. After many years experience in big perl project with many different skilled developers I have made decision that context aware code is one of most worse thing in Perl.
Hynek -Pichi- Vychodil
@daotoad: You can never assume that a function returning a list in list context will return its length in scalar context, since that only happens when the function returns an array. If the function returns a list value, you'll receive the last element of the list. Why? Because Perl HATES you. :)
j_random_hacker
@j_random_hacker: Nice point ;-) And I hate Perl and using return @{[1,2,3]} when he hates me.
Hynek -Pichi- Vychodil
@Hynek: @{[1, 2, 3]} is a good trick, though (as you may have picked up) I hate that it's necessary. :)
j_random_hacker
This is the way this would work in Perl6
Brad Gilbert
I was showing how to avoid copying an array, and stating that it probably wasn't worth trying to do.
Brad Gilbert
I created an interesting puzzle for myself once by doing: "return $start..$end;" In scalar context, .. is the flip flop operator and does not behave at all like I expected. That taught me to always assign to an array before returning a group of results (unless I want non-array like behavior).
daotoad
@daotoad: Ouch! Yes, propagation of context to return statements in functions is definitely one of Perl's less intuitive aspects.
j_random_hacker
A: 

There's two considerations. The obvious one is how big is your array going to get? If it's less than a few dozen elements, then size is not a factor (unless you're micro-optimizing for some rapidly called function, but you'd have to do some memory profiling to prove that first).

That's the easy part. The oft overlooked second consideration is the interface. How is the returned array going to be used? This is important because whole array dereferencing is kinda awful in Perl. For example:

for my $info (@{ getInfo($some, $args) }) {
    ...
}

That's ugly. This is much better.

for my $info ( getInfo($some, $args) ) {
    ...
}

It also lends itself to mapping and grepping.

my @info = grep { ... } getInfo($some, $args);

But returning an array ref can be handy if you're going to pick out individual elements:

my $address = getInfo($some, $args)->[2];

That's simpler than:

my $address = (getInfo($some, $args))[2];

Or:

my @info = getInfo($some, $args);
my $address = $info[2];

But at that point, you should question whether @info is truly a list or a hash.

my $address = getInfo($some, $args)->{address};

What you should not do is have getInfo() return an array ref in scalar context and an array in list context. This muddles the traditional use of scalar context as array length which will surprise the user.

Finally, I will plug my own module, Method::Signatures, because it offers a compromise for passing in array references without having to use the array ref syntax.

use Method::Signatures;

method foo(\@args) {
    print "@args";      # @args is not a copy
    push @args, 42;   # this alters the caller array
}

my @nums = (1,2,3);
Class->foo(\@nums);   # prints 1 2 3
print "@nums";        # prints 1 2 3 42

This is done through the magic of Data::Alias.

Schwern
You can never assume that a function returning a list in list context will return its length in scalar context, since that only happens when the function returns an array. If the function returns a non-array list value, you'll receive the last element of the list instead of its size.
j_random_hacker
Then don't return lists! If your hammer handle gives you splinters, don't wear gloves, sand it smooth! The whole "list vs array" thing in Perl 5 is a giant, gaping bear trap right in the middle of the playground.
Schwern
I totally agree with your last sentence. I would add that most of the kids in the playground, and maybe even the playground designers, don't know about this bear trap. :)
j_random_hacker