[Brisbane-pm] Passing references into Arrays

Jacinta Richardson jarich at perltraining.com.au
Fri Feb 23 21:21:13 PST 2007


Martin Jacobs wrote:

> Perl Best Practices says 'Use a hash of named arguments for any
> subroutine that has more than three parameters'. OK, that's PERRMOSS. I
> have varied the advice, though, in that I have set up a hash of
> references to the variables (because some of the arrays will be very
> big).

I think you're making this too hard.  You should be able to do the following:

do_something_cool( count      => 2,
                   list       => \@some_list,
                   defaults   => \%defaults,
                   otherthing => 10,
                   somestring => 'dfasdfasdfsdf',
                   scalar     => $something,
);

sub do_something_cool {
	my %args = @_;

	my $count = $args{count} || 0;
	my $list  = $args{list};       # just using the scalar avoids copying
        my $hash  = $args{defaults};
        ....

	foreach my $value (@$list) {
		...
	}
}

You do not need to create a hash of references per se.  What I have done above
will still ensure that only scalars will be passed around.  Unless your simple
scalars contain very large strings in them, or you _really_ need to change their
contents, I'd suggest that you don't pass them as references, but instead pass
them by value.

> If the keys are to make sense,
> then they should have long names, like the names of the values, but then
> there's the danger of mixing up the key with the value.

Use good layout and => to help reduce this confusion.  Glancing at my code above
you should be able to tell instantly which are keys and which are values.

> Version 1 works well, but the syntax is cluttered because each operation
> has to derefence each variable at every step.

It's worse than cluttered, it hurts my eyes!

> Version 2 attempts to get round this by dereferencing everything at the
> start of the sub. Does this mean it copies the variables in the sub?

Yes these dereferences are doing copying.  Ideally you pass references and then
use the references - dereferencing only where you need to: as in my foreach loop
above.

> Oddly, and I don't know why, if you hash out line 31, the sub applies
> the operation to the referent outside the sub, @k

You code does this:

       my @k      =  @{$arg_ref->{arg3}};
       $k[0][0]  =  "$k[1][1]"."$k[1][2]";
       @{$arg_ref->{arg3}} = @k;

Let's look at what this does.  On the first time we dereference $arg_ref->{arg3}
to get an array.  If we previously had had:
	$arg_ref->{arg3} = ['a' .. 'e'];
then we would now have:
	@k = ('a'..'e');
However, we previously had:
	$arg_ref->{arg3} = [ref1, ref2, ref3...]
so we now have:
	@k = (ref1, ref2, ref3, ...]

The second line then changes the data structure pointed at by that first
reference.  So instead of saying:
	$k[0] = x
which would have changed only @k, what we are instead doing is the same as:
	ref1->[0] = x
So we're changing the data structure pointed to from @k[0]

Thus whether or not we re-assign @k to @{$arg_ref->{arg3}} the under-lying data
structure has been changed.  The third line just involves a little more copying.
 If you want true copying so that you can change parts of a data structure
without repercussions, look at Storable's "clone" method.

I hope that helps.


Now.... some comments on your included code:

> #!/usr/local/bin/perl
> use warnings;
> use strict;
> use Time::Local;
> 
> #The following is provided as a 'template' for the conventions used in PERRMOSS.
> #It is based on Perl Best Practices by Damian Conway, in particular Chapter 9.
> #Chapter 9 can be found at www.oreilly.com/catalog/perlbp/chapter/ch09.pdf.
> 
> &Some_block_of_code;

Subroutines really are best called with parens and no &.

	Some_block_of_code();

> sub Some_sub {
...
>        #Then, do some operations
>        $$i        =  $$i + 100;
>        print 'Testprint line 22     $$i                =   '."$$i\n";

I know this works, but that's not necessarily a good reason for doing it.

>        $$j[0]     =  $$j[1]*$$j[2];

Much better written as:
	$j->[0]     = $j->[1] * $j->[2];


>        my $arguments    =  Some_sub ({
>                                       arg1   =>    \$i, 
>                                       arg2   =>    \@j,
>        		        	      arg3   =>    \@k,
>        			              arg4   =>    \%l, 
>                                      });

There are two good reasons for passing references to a subroutine:
	1. The data structures are large and thus you don't want to copy them
	2. The subroutine needs to be able to edit those data structures.

If you have a variable that doesn't fall into either of the above, don't pass
that variable as a reference.  It is a very good idea to make sure that any
subroutine which changes its arguments has a name that reflects that.  You don't
want to call a subroutine called:

	print_data

only to find that it completely corrupts the data structures sent to it.

I've attached a slightly modified version of your code to show another
alternative.  In it I return the change to $i (because I don't like references
to scalars), but if you really need to use the reference to scalar then of
course you should.

	J

-- 
   ("`-''-/").___..--''"`-._          |  Jacinta Richardson         |
    `6_ 6  )   `-.  (     ).`-.__.`)  |  Perl Training Australia    |
    (_Y_.)'  ._   )  `._ `. ``-..-'   |      +61 3 9354 6001        |
  _..`--'_..-_/  /--'_.' ,'           | contact at perltraining.com.au |
 (il),-''  (li),'  ((!.-'             |   www.perltraining.com.au   |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test1.pl
Type: text/x-perl
Size: 2611 bytes
Desc: not available
Url : http://mail.pm.org/pipermail/brisbane-pm/attachments/20070224/02ebed98/attachment-0001.bin 


More information about the Brisbane-pm mailing list