What's the best Perl practice for returning hashes from functions?

后端 未结 7 1952
走了就别回头了
走了就别回头了 2021-02-04 07:31

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 fun

相关标签:
7条回答
  • 2021-02-04 08:10

    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;
    }
    
    0 讨论(0)
  • I originally posted this to another question, and then someone pointed to this as a "related post", so I'll post it here to for my take on the subject, assuming people will encounter it in the future.

    I'm going to contradict the Accepted Answer and say that I prefer to have my data returned as a plain hash (well, as an even-sized list which is likely to be interpreted as a hash). I work in an environment where we tend to do things like the following code snippet, and it's much easier to combine and sort and slice and dice when you don't have to dereference every other line. (It's also nice to know that someone can't damage your hashref because you passed the entire thing by value -- though someone pointed out that if your hash contains more than simple scalars it's not so simple.)

    my %filtered_config_slice = 
       hashgrep { $a !~ /^apparent_/ && defined $b } (
       map { $_->build_config_slice(%some_params, some_other => 'param') } 
       ($self->partial_config_strategies, $other_config_strategy)
    );
    

    This approximates something that my code might do: building a configuration for an object based on various configuration strategy objects (some of which the object knows about inherently, plus some extra guy) and then filters out some of them as irrelevant.

    (Yes, we have nice tools like hashgrep and hashmap and lkeys that do useful things to hashes. $a and $b get set to the key and the value of each item in the list, respectively). (Yes, we have people who can program at this level. Hiring is obnoxious, but we have a quality product.)

    If you don't intend to do anything resembling functional programming like this, or if you need more performance (have you profiled?) then sure, use hashrefs.

    0 讨论(0)
  • 2021-02-04 08:21

    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.

    0 讨论(0)
  • 2021-02-04 08:23

    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.

    0 讨论(0)
  • 2021-02-04 08:30

    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.

    0 讨论(0)
  • 2021-02-04 08:30

    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};
    
    0 讨论(0)
提交回复
热议问题