What's the best Perl practice for returning ha

2019-03-12 18:03发布

I am mulling over a best practice for passing hash references for return data to/from functions.

On the one hand, it seems intuitive to pass only input values to a function and have only return output variables. However, passing hashes in Perl can only be done by reference, so it is a bit messy and would seem more of an opportunity to make a mistake.

The other way is to pass a reference in the input variables, but then it has to be dealt with in the function, and it may not be clear what is an input and what is a return variable.

What is a best practice regarding this?

Return references to an array and a hash, and then dereference it.

($ref_array,$ref_hash) = $this->getData('input');
@array = @{$ref_array};
%hash = %{$ref_hash};

Pass in references (@array, %hash) to the function that will hold the output data.

$this->getData('input', \@array, \%hash);

7条回答
聊天终结者
2楼-- · 2019-03-12 18:43

Trying to create copies by saying

my %hash = %{$ref_hash};

is even more dangerous than using the hashref. This is because it only creates a shallow copy. This will lead you to thinking it is okay to modify the hash, but if it contains references they will modify the original data structure. I find it better to just pass references and be careful, but if you really want to make sure you have a copy of the reference passed in you can say:

use Storable qw/dclone/;

my %hash = %{dclone $ref_hash};
查看更多
【Aperson】
3楼-- · 2019-03-12 18:43

The first one is better:

my ($ref_array,$ref_hash) = $this->getData('input');

The reasons are:

  • in the second case, getData() needs to check the data structures to make sure they are empty
  • you have freedom to return undef as a special value
  • it looks more Perl-idiomatic.

Note: the lines

@array = @{$ref_array};
%hash = %{$ref_hash};

are questionable, since you shallow-copy the whole data structures here. You can use references everywhere where you need array/hash, using -> operator for convenience.

查看更多
闹够了就滚
4楼-- · 2019-03-12 18:56

My personal preference for sub interfaces:

  1. If the routine has 0-3 arguments, they may be passed in list form: foo( 'a', 12, [1,2,3] );
  2. Otherwise pass a list of name value pairs. foo( one => 'a', two => 12, three => [1,2,3] );
  3. If the routine has or may have more than one argument seriously consider using name/value pairs.

Passing in references increases the risk of inadvertent data modification.

On returns I generally prefer to return a list of results rather than an array or hash reference.

I return hash or array refs when it will make a noticeable improvement in speed or memory consumption (ie BIG structures), or when a complex data structure is involved.

Returning references when not needed deprives one of the ability to take advantage of Perl's nice list handling features and exposes one to the dangers of inadvertent modification of data.

In particular, I find it useful to assign a list of results into an array and return the array, which provides the contextual return behaviors of an array to my subs.

For the case of passing in two hashes I would do something like:

my $foo = foo( hash1 => \%hash1, hash2 => \%hash2 ); # gets number of items returned
my @foo = foo( hash1 => \%hash1, hash2 => \%hash2 ); # gets items returned

sub foo {
   my %arg = @_;

   # do stuff

   return @results;
}
查看更多
Animai°情兽
5楼-- · 2019-03-12 18:58

Uh... "passing hashes can only be done by reference"?

sub foo(%) {
    my %hash = @_;
    do_stuff_with(%hash);
}

my %hash = (a => 1, b => 2);
foo(%hash);

What am I missing?

I would say that if the issue is that you need to have multiple outputs from a function, it's better as a general practice to output a data structure, probably a hash, that holds everything you need to send out rather than taking modifiable references as arguments.

查看更多
我只想做你的唯一
6楼-- · 2019-03-12 19:02

If it's getting complicated enough that both the callsite and the called function are paying for it (because you have to think/write more every time you use it), why not just use an object?

my $results = $this->getData('input');

$results->key_value_thingies;
$results->listy_thingies;

If making an object is "too complicated" then start using Moose so that it no longer is.

查看更多
萌系小妹纸
7楼-- · 2019-03-12 19:05

Just return the reference. There is no need to dereference the whole hash like you are doing in your examples:

my $result = some_function_that_returns_a_hashref;
say "Foo is ", $result->{foo};
say $_, " => ", $result->{$_} for keys %$result;

etc.

I have never seen anyone pass in empty references to hold the result. This is Perl, not C.

查看更多
登录 后发表回答