snafu has asked for the wisdom of the Perl Monks concerning the following question:

I am in a learning stage learning about references. I think I have an old fashioned way of doing things and I have a feeling that this example code is good candidate for ''upgrading'' to a more efficient way of hitting the objective. To me, references are new and therefore I feel this could be good learning excercise for those of us that just don't quite understand references, how they work, and what their purpose is.

Merlin helped me some with a paper he wrote for Linux Magazine. However, conceptualization is different from excersization. (:

The objective of this code is to get the number of elements in @var from within multiple sub routines. Is this code optimized or should the use of references make it work more optimally?

The purpose behind this type of example is to keep from repeating lines (in each subroutine) to tell me what the number of elements are in @var. This is why I use subroutines so that I don't have to repeat the same code over and over again.

#!/usr/local/bin/perl -w use strict; &sub1; &sub2; sub sub1 { my @var; my $num; my $numElements; for $num (0..2) { $var[$num] = "blah"; } $numElements = @var; print "Number of elements in \@var of ".(caller(0))[3]." is $numEl +ements\n"; print &eval_array_size(@var),"\n"; } sub sub2 { my @var; my $num; my $numElements; for $num (0..5) { $var[$num] = "blah"; } $numElements = @var; print "Number of elements in \@var of ".(caller(0))[3]." is $numEl +ements\n"; print &eval_array_size(@var),"\n"; } sub eval_array_size { my $numElements = @_; return($numElements); }
TIA guys.

----------
- Jim

Replies are listed 'Best First'.
(jeffa) Re: Change this example to use references...
by jeffa (Bishop) on Aug 10, 2001 at 01:25 UTC
    I hate to say it, but in this case using a subroutine is overkill. You can wipe out sub eval_array_size and change the two lines that call it to this:
    print scalar @var, "\n";
    But back to your question. Let's use another example:
    #!/usr/bin/perl -w use strict; use Benchmark; timethese(100, { 'no ref' => sub { my @array = sub1() }, 'ref' => sub { my $arref = sub2() }, }); sub sub1 { my @arry = (0..100000); return @arry; } sub sub2 { my @arry = (0..100000); return \@arry; }
    and the results:
    Benchmark: timing 100 iterations of no ref, ref... no ref: 33 wallclock secs (28.19 usr + 0.07 sys = 28.26 CPU) @ 3.54 +/s (n=100) ref: 10 wallclock secs ( 9.21 usr + 0.34 sys = 9.55 CPU) @ 10.47 +/s (n=100)
    Passing arrays/hashes/objects/data structures around in Perl is much more efficient when you pass them as references, but it can get you into trouble if you aren't careful:
    sub new { my $class = shift; my $self = { list => { a => 'ls', b => 'pwd', c => 'date', }, }; bless $self, $class; return $self; } sub get_list { my ($self) = @_; return $self->{'list'}; }
    This object has an attribute called list, it is simply a hash reference with some key value pairs. If a client gets a copy of the list via get_list(), that client can modify the contents of the list. Not good. get_list() would be better as:
    sub get_list { my ($self) = @_; my %ref = %{ $self->{'list'} }; return \%ref; }
    Now, even though get_list returns a reference, it is not a reference to the original list, instead it is a reference to a copy of the original list.

    Hope this helps, references are tricky to explain. I always try to explain them by asking the asker "what does a variable really hold?" It's not a value, a variable holds the memory location where a value is stored. Therefore, a variable can hold the memory location of a value that contains another memory location of the actual value . . . and so on . . .

    perl -le '$x="jeff";$x++ for(0..4482550);print $x'

Re: Change this example to use references...
by kjherron (Pilgrim) on Aug 14, 2001 at 00:45 UTC
    If I understand the point of your question, you want to know how you can pass an array to a subroutine using references and still do array-ish things with it.

    As I'm sure you're aware, the eval_array_size() routine in your example receives the contents of @var as a list of individual arguments. There are two ways to convert this to passing @var through a reference:

    You can explicitly take a reference and pass that as a scalar value:

    print "\@var has ", eval_array_size(\@var), " elements.\n"; sub eval_array_size { my $aref = shift; return scalar @{$aref}; }
    Note I'm using "scalar" to force the sub to return the number of elements, instead of (possibly) returning the array contents.

    The second way is to prototype the function. For this to work, the function has to appear before it's called:

    sub eval_array_size (\@) { my $aref = shift; return scalar @{$aref}; } print "\@var has ", eval_array_size(@var), " elements.\n";
    Here, perl will require that the function's argument be an actual array (e.g. a list of items in parentheses wouldn't be acceptable) and will automatically reference the array for you. This second method is generally used for making functions that "act" like built-in commands.