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

Fellow monks,
In another one of my steps to learning Perl the right way, I'm trying to get my head around subroutines, namely passing data back and forth between. I did learn (by reading the Q&A) that data gets passed as a list, e.g., @_). But part of my problem seems to lie in what happens once in the subroutine. This works (right out of the Camel book):
use strict; use Data::Dumper; my $name = "baggins"; &uppercase( $name ); print Dumper( $name ); sub uppercase { for (@_) { tr/a-z/A-Z/; } }
but this ones does not:
use strict; use Data::Dumper; my $name = "baggins"; &uppercase( $name ); print Dumper( $name ); sub uppercase { for (@_) { uc($_); } }
Is it the $_ in the uc($_)? I'm sure it's insanely simple, but I'm not seeing it. Thanks!

—Brad
"A little yeast leavens the whole dough."

Replies are listed 'Best First'.
Re: How to handle passed data in subroutines
by Roger (Parson) on Nov 20, 2003 at 03:22 UTC
    Hi Brad, uc is a function, it does not modify $_. If you want to modify $_, try, $_ = uc; # note that I have omitted the implicit $_ from the uc function.

    You can find the documentation of uc by typing perldoc -f uc, it gives you a detailed description of the function.

Re: How to handle passed data in subroutines
by !1 (Hermit) on Nov 20, 2003 at 03:27 UTC

    There's a bit of magic happening in the background. First thing you need to know is that if you work directly with the @_ array, you are altering the values passed to the subroutine directly. When you use for, the variable assigned to (in this case, $_) is actually an alias in the list the for is iterating over. uc returns a new value and does not work on $_. To fix it, it's only a matter of:

    $_ = uc($_);

    Of course, since we're working with the variable $_, we can just use $_ = uc; instead and still get the desired results. I hope this helps you with your problem.

Re: How to handle passed data in subroutines
by pg (Canon) on Nov 20, 2003 at 03:23 UTC

    Try my first piece of demo. It prints "abc", not "ABC", because the return from uc() is simply discarded, and the value of $str is not changed at all. That's exactly what was missed from your understandng.

    use strict; my $str = "abc"; uc($str); print $str;

    The second demo gives you "ABC", not "abc", because we assigned the result returned from uc(), which is "ABC", back to $str:

    use strict; my $str = "abc"; $str = uc($str); print $str;

    BTW, you normally should pass array ref, not array, to your subs, for performance reason. Also, if you pass multiple arrays, or array mixed with other stuffs, you can easily lose track of the boundary of your array.

      BTW, you normally should pass array ref, not array, to your subs, for performance reason.

      I consider this misguided advice.

      I think that people normally should pass whatever is clearest, and consider performance a distinctly secondary concern. This chapter is timeless and applicable across languages.

      Sure, this isn't always true. Occasionally you really do need to squeeze performance and straightforward good architecture choices, a bit of tuning bottlenecks, etc won't get you there. But in those problems, Perl is probably the wrong language to use in the first place.

      OTOH I agree with you when you say, Also, if you pass multiple arrays, or array mixed with other stuffs, you can easily lose track of the boundary of your array. However whether I think that this means that you should pass things into functions by reference depends on what the function does.

Re: How to handle passed data in subroutines
by bradcathey (Prior) on Nov 20, 2003 at 13:13 UTC
    Thanks all! I have used uc before, and in the $foo = uc($foo) context, but thought that I could make it work like the tr deal. Anyway, the comment about it being a function will be most helpful in the future.

    —Brad
    "A little yeast leavens the whole dough."