views:

236

answers:

6

I have this example:

my $numbers = [ 1, 2, 3, 4, 5, 6 ];
my $newarray = [ reverse @$numbers ];

This example contains some synthetic code to make the $numbers arrayref appropriate for the reverse function. I would like to remove that code and do something more like this:

my $newarray = reverse $numbers;

I know this doesn't work, it returns:

)8936f22x0(YARRA

Is there a better way to reverse an arrayref in Perl without changing the first line?

UPDATE:

my $mails = [ reverse @{$mail_api->GetLogSendMail({ customer_id => $id })} ];

The idea is to make the above line of code better.

+10  A: 

If you are ok to use array instead of arrayref, then try this:

my @newarray = reverse @$numbers;

Code my $newarray = reverse $numbers didn't work because reverse is called in scalar context, which make it return a string with charactes in reversed order. From reverse manual:

In scalar context, concatenates the elements of LIST and returns a string value with all characters in the opposite order.

If you declare $newarray variable somewhere above you can write it in following way:

## declare $newarray somewhere else
@$newarray = reverse @$numbers;

Update

May be you would like to create your own function:

sub reverse_by_ref {
    return [ reverse @{$_[0]} ];
}
Ivan Nevostruev
Thanks, not really what I was looking for, but it does simplify it a bit.
Peter Stuifzand
What were you looking for?
kemp
Your second suggestion will result in even more code if you write it all out.
Peter Stuifzand
@kemp A clean way to reverse an arrayref.
Peter Stuifzand
May be you need to create your own function for this (see update). As far as I know there is no build-in function to reverse array by reference.
Ivan Nevostruev
@Ivan Your function approach is probably the cleanest way to do the reverse, with the exception that it needs an extra function.
Peter Stuifzand
+2  A: 

did you looking for something like this:

#!/usr/bin/perl

$myNames = ['Jacob', 'Michael', 'Ethan', 'Andrew'];
@reversedNames = reverse(@$myNames); 

print @reversedNames;

take a look at this tutorial.

Michel Kogan
Sorry, no, I have been programming perl for about 10 years. I'm looking for programmer opinion, knowledge, and experience, not simple answers.
Peter Stuifzand
so, don't be sorry. look for your answer.
Michel Kogan
A: 

i don't know if you have control over the original object's source, but if you do, what about adding a ->reverse method to that package?

my $mails = $mail_api->GetLogSendMail({ customer_id => $id })->reverse;
Eric Strom
+7  A: 

I think the real fundamental issue here is that you're storing $numbers as an array-ref and trying to operate on them as an array. This might be common place but it is not correct. People do this to keep array's consistently in reference form but the right way is to use the native structure rather than a reference to it. In reference form scalar, reverse, pop/push/shift/unshift and all others will require explicit deference to operate on an array. This is common to all languages that permit references to arrays -- look at C's character pointers vs character arrays and things like the sizeof() operator. Really what we're doing here is making use of perl's extremely convenient anonymous array syntax to your own detriment.

It isn't a real problem but there is the minor overhead of the dereference and the almost inescapable visual element of the deference. Now onward to the /right/ answer. There is a modern solution to this and it is with autobox. Autobox provides all types references included with an object-like syntax.

use autobox;
my $arr = [ 1 .. 10 ];
$arr->reverse;

Just to follow-up, the real reason why we have anonymous array syntax is to create deep structures and avoid passing arrays (requires pushing them onto the stack), more so than to create things that should be arrays as array-refs.

  • $foo( [1..100] ) is much faster than $foo( 1..100 )
  • $arr[1] = [1,2,3] is more convenient than @temp = 1,2,3; $arr[1] = \@temp
Evan Carroll
Thanks for information about `autobox`
Ivan Nevostruev
+5  A: 

You've got several options.

Don't try to cram it all on one line:

my $mails = [ reverse @{$mail_api->GetLogSendMail({ customer_id => $id })} ];

Becomes:

my $mails = $mail_api->GetLogSendMail({ customer_id => $id });
@$mails = reverse @$mails;

If GetLogSendMail is foolish enough to return a reference to an array that you shouldn't mess with, then you'll have to modify this slightly to create a new array:

my $inviolate_mails = $mail_api->GetLogSendMail({ customer_id => $id });
my $mails;  @$mails = reverse @$inviolate_mails;

To keep everything on one line, use a subroutine as Ivan Nevostruev suggests:

sub reverse_ref \@ {
    return [ reverse @{$_[0]} ];
}

my $mails = reverse_ref $mail_api->GetLogSendMail({ customer_id => $id });

If you need to do loads of list-ops on array refs, consider making a library:

package ArrayRef::Util;
# boiler plate skipped.

sub reverse_ref \@ {
    return [ reverse @{$_[0]} ];
}

sub push_ref \@\@ {
    push @{$_[0]}, @{$_[1]};
}

# and so on

Finally, Evan Carroll's autobox suggestion helps too:

use autobox;

my $mails = [ $mail_api->GetLogSendMail({ customer_id => $id })->reverse ];

See Should I use autobox in Perl? for more info on autobox.

daotoad
A: 

Two questions:

  1. How much control do you have over whatever object $mail_api is?
  2. Do you really need $mails to be a reference rather than an actual array? Why so?

If the answer to 1 is "some" and 2 is "no", then change the GetLogSendMail method to return a list instead of an array reference. Then your code becomes a straightforward

my @mails = reverse $mail_api->GetLogSendMail({ customer_id => $id });
Dan