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

This is partly a question about style, maintainability, etc. and partly one about my understanding(or most probably lack there of) of scope .

Background
I am creating a sub that will take 2-3 scalars as input, munge them (sparing the originals)and return separate results to the caller in some fashion. There are many ways to go and I am looking for some advice/pro's/cons.
I wrote a silly test script at the end to illustrate options I see.

I am looking at:

1.Pass the arguments and return the results:

sub Munger1 { my ($p1, $p2) = @_; #... my $munge1 = $p1.' or '.$p2; my $munge2 = $p2.' or '.$p1; return ($munge1, $munge2); } my ($a1, $a2) = Munger1($p1, $p2);
To me this is real clear regarding not only the subs args but what it returns when looking at the call or the sub. However, since the returned strings were scoped to the sub, I would guess there is copying going on in the assignment upon return. Anyone know if this is true?

2.Pass the arguments and explicit references to args to return the results in.

sub Munger2 { my ($p1, $p2, $m1, $m2) = @_; #... $$m1 = $p1.' or '.$p2; $$m2 = $p2.' or '.$p1; } Munger2($p1, $p2, \$a3, \$a4);
This seems good too. The caller passes in where it wants the results placed. The fact that explicit refs are made clues the reader in on what's going to happen. Also, this would seem to avoid copying the results as in option 1 (assuming I am correct there).

3.Pass the arguments and "Aliased" scalar refs (if using scalars of course).

sub Munger3 { my ($p1, $p2) = @_; #... $_[2] = $p1.' or '.$p2; $_[3] = $p2.' or '.$p1; } Munger2($p1, $p2, $a5, $a6);
This is just a variation on option 2 above but it "silently" passes the "aliased" refs. The reader probably won't realize initially that $a5 and $a6 will be modified. Arggg. Also, I have read some other posts which mention this as being undesirable for a number of reasons except in certain situations. I think I'll just stay clear for now ;-)

4.Pass the arguments and return references to the results.

sub Munger4 { my ($p1, $p2) = @_; #... my $munge1 = $p1.' or '.$p2; my $munge2 = $p2.' or '.$p1; return (\$munge1, \$munge2); } my ($ar7, $ar8) = Munger4($p1, $p2);
This looks pretty good, as long as the it's made clear that the return vars are refs, presumably via good names. However, this one puzzles me bit. When the sub returns the references, the scalars it created and returned references to go out of scope, don't they? If so, then why does this seem to work? Isn't the data gone?
Or is it waiting to blow up down the road, say after a garbage collection?
I dug a bit and only found something on returning by reference and how to do it but that was all. Are there problems/caveats using this approach?

Thanks

Simple test code below:

#!/usr/bin/perl -w use strict; sub Munger1 { # Munger returns 2 scalars my ($p1, $p2) = @_; #... my $munge1 = $p1.' or '.$p2; my $munge2 = $p2.' or '.$p1; return ($munge1, $munge2); } sub Munger2 { # Munger2 returns results via passed +in references my ($p1, $p2, $m1, $m2) = @_; #... $$m1 = $p1.' or '.$p2; $$m2 = $p2.' or '.$p1; } sub Munger3 { # Munger3 returns results via "aliase +d"??? references my ($p1, $p2) = @_; #... $_[2] = $p1.' or '.$p2; $_[3] = $p2.' or '.$p1; } sub Munger4 { # Munger4 returns 2 scalar references my ($p1, $p2) = @_; #... my $munge1 = $p1.' or '.$p2; my $munge2 = $p2.' or '.$p1; return (\$munge1, \$munge2); } my $p1 = "This"; my $p2 = "That"; my ($a1, $a2) = Munger1($p1, $p2); print "Munger 1: A1=$a1 A2=$a2\n"; my ($a3, $a4) = ('a3' , 'a4'); Munger2($p1, $p2, \$a3, \$a4); print "Munger 2: A3=$a3 A4=$a4\n"; my ($a5, $a6) = ('a5' , 'a6'); Munger3($p1, $p2, $a5, $a6); print "Munger 3: A5=$a5 A6=$a6\n"; my ($aref7, $aref8) = Munger4($p1, $p2); print "Munger 4: A7=$$aref7 A8=$$aref8\n";

janitored by ybiC: balanced <readmore> tags around code chunks

Replies are listed 'Best First'.
Re: Subs: Pass references or return them?
by davido (Cardinal) on Sep 03, 2003 at 20:53 UTC
    It is ok (and even necessary in many cases) to return a reference. Even though the scope within the sub dies, the variable (or expression) to which you passed the reference is still holding onto the reference, and its data. Perl is smart enough not to destroy the referenced data until all references to it fall out of scope or are reassigned. Think of it this way; the reference inside of the sub fell out of scope, but the data to which it pointed doesn't care about scope as much as it cares about being pointed to by someone, or relevant to someone.

    I read where a monk earlier today on this site compared references to cards from a library card catalogue. The card tells where to get the book. Continuing with that analogy, simply make the assumption that the librarian doesn't dispose of a book until all of the library catalogue cards pointing to it have been tossed out first.

    Dave

    "If I had my life to do over again, I'd be a plumber." -- Albert Einstein

      Thanks, that helps and it actually makes sense to me. The analogy is a good one I think.
Re: Subs: Pass references or return them?
by antirice (Priest) on Sep 03, 2003 at 22:03 UTC

    Ok. You've asked when you should use them so I'll attempt to answer your question with a very long post.

    Passing references of scalars to a sub is usually pretty silly since you can create references to them in your subroutine (this is due to the way perl builds @_). i.e.

    Now you're probably wondering when you should pass references to a subroutine. If you wish to pass either a hash or an array with anything else (scalar, hash, array, etc) then you should usually pass it as a reference. You actually don't need to use references in the case where you wish to send a few scalars and an array or hash (as long as the scalars come before the array or hash) or in the case where you know how long each will be (although this is *VERY* easy to break). For instance:

    When should you return references? Again, if you're dealing with multiple return types, the same rules apply.

    Please note that readability is also enormously important. Even though I've presented ways of deconstructing @_ in a subroutine does not mean it's a good idea to use them. Passing references is the standard way of passing arrays and hashes to a subroutine. Please check out perlref and perlreftut as well as other material listed in perltoc for more information.

    Sorry for the rant. Hope this helps.

    antirice    
    The first rule of Perl club is - use Perl
    The
    ith rule of Perl club is - follow rule i - 1 for i > 1

      Thank you for taking the time to put that post together, I really do appreciate the detail (aka rant) and it helps.

      I have looked at the perldoc stuff and I get good information there. However, being new to perl (a couple weeks-ish) I still get lost in the details trying to find somewhat of a "specific" answer to a somewhat "general" question, if that makes any sense. ;-)

      Anyhow, I usually can find the answers to "mechanical" type questions on my own. You know, "How do you do that?" etc. Where I find it more difficult is finding answers to "Why" or "When" I should (or should not) do something a particular way... the questions often best answered by experince.

      That's where I have, in particular, found this board extremely helpful and remarkably responsive. No sucking up here, that's just the way I see it!

      Thanks

Re: Subs: Pass references or return them?
by tcf22 (Priest) on Sep 03, 2003 at 20:58 UTC
    I personally prefer the 1st method of passing back actual values. I think that this is much simplier than the other(not that the others are all that complicated). The other methods will work also, because perl won't reap the data until all references to it are gone.
Re: Subs: Pass references or return them?
by hmerrill (Friar) on Sep 04, 2003 at 13:37 UTC
    A similar 'passing parameters & references' question was posted yesterday - here is the node:

    Node 288726