Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Messing with @_

by TGI (Parson)
on May 29, 2008 at 20:52 UTC ( [id://689088]=perlquestion: print w/replies, xml ) Need Help??

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

Far too often I find myself having to check a bunch of input args for definedness. It's annoying, and this annoyance is almost definitely the reason for defined-or in Perl 5.10. However, I need a solution that works in older perls.

I came up with an idea that seems to work, but it works by messing about with @_. This makes me nervous. Anyone care to tell me why this code is dangerous?

I've got two routines to munge @_. One changes the source array, called fix_undef(), which seems like bad news to me. The other, called no_sfx_fix_undef() seems to be safe.

So, as far as my limited brain can tell, the second function is safe to use unless I plan on using @_ to modify the original routine's arguments. Any other gotchas I should know about?

use strict; use warnings; my @args = ( qw/fee fie foe fum/, undef, 1..3 ); print "No SFX\n", '-'x10, "\n"; Sub_With_NO_Side_Effects( @args ); print map { "'$_'\n" } @args; print "\nSFX\n", '-'x10, "\n"; Sub_With_Side_Effects( @args ); print map { "'$_'\n" } @args; sub Sub_With_NO_Side_Effects { &no_sfx_fix_undef; print "$_: '$_[$_]'\n" foreach 0..$#_; } sub Sub_With_Side_Effects { &fix_undef; print "$_: '$_[$_]'\n" foreach 0..$#_; } # This sub modifies the original calling function's variables. BAD NE +WS sub fix_undef { defined $_ or $_ = '' foreach @_; } # This seems to be ok... sub no_sfx_fix_undef { @_ = map { defined $_ ? $_ : '' } @_; } __END__ # OUTPUT No SFX ---------- 0: 'fee' 1: 'fie' 2: 'foe' 3: 'fum' 4: '' 5: '1' 6: '2' 7: '3' Use of uninitialized value in concatenation (.) or string at C:\temp\t +est.pl line 9. 'fee' 'fie' 'foe' 'fum' '' '1' '2' '3' SFX ---------- 0: 'fee' 1: 'fie' 2: 'foe' 3: 'fum' 4: '' 5: '1' 6: '2' 7: '3' 'fee' 'fie' 'foe' 'fum' '' '1' '2' '3'


TGI says moo

Replies are listed 'Best First'.
Re: Messing with @_
by jethro (Monsignor) on May 29, 2008 at 21:39 UTC
    I needed a while to find out what happened. Nice trick.

    To make it easier for other readers: The difference is that in the No_Side_Effects version @_ gets overwritten thereby destroying the aliasing between the function arguments and @_.

    I can't see any gotchas, but that doesn't mean much.

      I know I'm going to appear a little thick with this post, but how does a sub-routine magically adopt the @_ arguments from the parent routine? I thought @_ was local to each sub-routine?

        That happens if you call the sub with an ampersand and without parentheses. This is documented in perlsub.

        When you call a function without braces i.e. &func instead of &func(), a shortcut happens.

        In the words of 'man perlsub':

        If a subroutine is called using the "&" form, the argument list is optional, and if omitted, no @_ array is set up for the subroutine: the @_ array at the time of the call is visible to subroutine instead. This is an efficiency mechanism that new users may wish to avoid.

Re: Messing with @_
by goibhniu (Hermit) on May 30, 2008 at 14:23 UTC

    I would think the answer to your question:

    Anyone care to tell me why this code is dangerous?
    would be situational. I can think of a situation where it might be dangerous, but can't say whether it's dangerous to you.

    An example of where it's dangerous is where the undef is intended in the defined interface to be meaningful. What if your args are:

    @args = ( qw/fee fie foe fum/, func_that_returns_undef_for_error(), 1. +.3 );

    Then you'd be masking errors by fixing up the undefs instead of checking for them and handling them (though I suppose converting to '' might be a way to handle them).

    Anyway, just brainstorming here. I think in the right situation you have a good idea.

    Update: I was just thinking that if you're "messing" with the value so that you can present undef in output, perhaps you're looking to write a string_maybe_undef() function?

    sub string_maybe_undef { $_ ? $_ : '' }

    then you could
    print string_maybe_undef($_).": '".string_maybe_undef($_[$_])."'\n" fo +reach 0..$#_;
    without actually messing with @_.


    #my sig used to say 'I humbly seek wisdom. '. Now it says:
    use strict;
    use warnings;
    I humbly seek wisdom.
Re: Messing with @_
by ruzam (Curate) on May 30, 2008 at 14:56 UTC
    Messing with @_ would make me nervous too. I would definitely stick with the Sub_With_NO_Side_Effects().

    On the other hand, I'm against using subs when not needed. While checking for definedness annoys me too, it's often done while assigning @_ to local variables in the sub, so it's not that much more trouble. At least not worth the bother of another sub call when a cut and paste will do.
    sub Sub_With_NO_Side_Effects { my @arg = map { defined $_ ? $_ : '' } @_; print "$_: '$arg[$_]'\n" foreach 0..$#arg; }

      That's exactly what I was doing, and then unpacking @arg--over and over again. When I do something over and over again, I usually reach for a sub.

      Where you draw the line between repeating code and writing subroutines is a matter of style. I tend to reach for a sub on the third instance. YMMV.


      TGI says moo

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://689088]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2024-04-25 15:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found