[sf-perl] passing array and hash references

Quinn Weaver qw at sf.pm.org
Thu Sep 28 23:10:06 PDT 2006


On Thu, Sep 28, 2006 at 12:08:55PM -0700, Rich Morin wrote:
> Clues, comments, suggestions?

> [...]

Rich, I have a novel solution to your problem.  It combines the
minimal code changes of the dereferencing approach with the semantics
of references.  Also, it complies with warnings and strictures.

It accomplishes this using aliases, via the CPAN module Data::Alias:

#!/usr/bin/perl

use warnings;
use strict;

use Data::Alias;
use Data::Dumper;

{
    my @array = qw( 1 2 3 );
    my %hash  = (
                    a => 4,
                    b => 5,
                    c => 6,
                );

    print "Values before mutation:\n";
    print Data::Dumper->Dump( [ \@array ], "array" );
    print Data::Dumper->Dump( [ \%hash ],  "hash" );
    print "\n";

    t_alias (\@array, \%hash);

    print "Values after mutation:\n";
    print Data::Dumper->Dump( [ \@array ], [ "array" ]);
    print Data::Dumper->Dump( [ \%hash ],  [ "hash"  ]);
    print "\n";
}

sub t_alias {
    my ($array_ref, $hash_ref) = @_;

    alias my @array = @$array_ref; # Doesn't copy; just aliases.
    alias my %hash  = %$hash_ref;  # ditto

    $array[2] = 'a_dr';      # Modifies original data, not a local copy!
    $hash{c}  = 'h_dr';      # ditto
}

Compare this to your code:

> # t_dr - test use of dereferencing
> #
> # It's possible to bring in references, then dereference them
> # and assign the result to arrays or hashes.  This complies
> # with "use strict", but it has two practical limitations.  If
> # the data structure is large, the overhead of copying it may
> # be unacceptable.

Not a problem if you use aliases. :)

> Worse, changes made to the copy will not
> # affect the callers' data, so the changed code might not act
> # in the same manner as the original code did.

Also not a problem if you use aliases. :)

> #
> sub t_dr {
> 
>     my ($r_a, $r_h) = @_;
> 
>     my (@array, %hash);
> 
>     @array   = @$r_a;
>     %hash    = %$r_h;
> 
>     print "\nt_dr:\n";
>     print "  array:  ", $array[42],  "\n";
>     print "  hash:   ", $hash{'42'}, "\n";
> 
>     $array[42]  =  'a_dr';      # Modifies local data.
>     $hash{'42'} =  'h_dr';      # ditto
> }

I think this solution hits the sweet spot.  It requires that you
modify only the subroutine call

    t_alias (\@array, \%hash); # Use references, not globs.

and the first few lines of the sub:

    sub t_alias {
        my ($array_ref, $hash_ref) = @_;

        alias my @array = @$array_ref; # Doesn't copy; just aliases.
        alias my %hash  = %$hash_ref;  # ditto

From there on out, your sub can use @array and %hash just as if they
had been passed in as globs, including side-effect semantics.  There's
no need to insert arrows for dereferencing or otherwise modify the sub
body.

Does this make sense?

--
qw (Quinn Weaver); #President, San Francisco Perl Mongers
=for information, visit http://sf.pm.org/weblog =cut


More information about the SanFrancisco-pm mailing list