Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Using $_ as a temp var, especially in functions

by BUU (Prior)
on Oct 23, 2002 at 02:40 UTC ( #207264=perlmeditation: print w/replies, xml ) Need Help??

Typically when i code a function, i often times need a 'temporary variable', to preform a few operations on, or simple for the sake of neatness and stuff. Looking at perlvar, one might assume that $_ would be a perfect variable to use for this. The problem then comes in is that $_ is global, and if your function modifies it, that could cause a very, very nasty bug, espcially combined with things like map{} and for{}. So what is your wisdom on temporary variables, and $_ in specific.

(I realize i could just do local($_); but i might as well just do my$foo then)

And while i'm meditating on semi-random things, does anyone else feel the need for another special var that would contain eithr the value, or a ref to, the last variable you used? Think of the fun you could have with it..
  • Comment on Using $_ as a temp var, especially in functions

Replies are listed 'Best First'.
Re: Using $_ as a temp var, especially in functions
by Zaxo (Archbishop) on Oct 23, 2002 at 02:57 UTC

    Use it, localized, if it makes sense as a pronoun. If it is the primary data source and functions that default to it as argument are needed, use it.

    Localizing it is not the same as creating a lexical. By localizing, you avoid damaging the wider scope with an innocent-looking sub call.

    After Compline,
    Zaxo

Re: Using $_ as a temp var, especially in functions
by thelenm (Vicar) on Oct 23, 2002 at 03:00 UTC
    If I need a temporary variable, I don't have a problem with declaring my $foo (but usually call it something a little more meaningful). Actually, I tend to declare most variables with my anyway, unless for some reason their scope really is global. It makes the code more modular and less likely to have unintended side effects, like modifying the global $_ that you mention.

    I'd recommend just biting the bullet and typing "local" if you must use $_ as a temporary variable. That's what local is for, after all. :-)

    -- Mike

    --
    just,my${.02}

Re: Using $_ as a temp var, especially in functions
by greenFox (Vicar) on Oct 23, 2002 at 03:53 UTC

    I would avoid using $_ as a temporary variable, even when localised. Not because it will break your code as it is but because the next coder (or even you some time later) who comes along won't be expecting it and could get bitten.

    If the var is so transient that $_ is an OK name for it then, as you say, "my $x;" is shorter to type than "local $_;" :) I would still use a more meaningful name though eg. $temp_foo_counter or $partial_interest_calc etc. If it is distinctly named it is much less likely to cause problems later on.

    Update:I read the original node as suggesting using $_ as a generic temporary variable and not necessarily to take advantage of Perl's built in use of $_ as the default target. I have used $_ as converter described and didn't mean to suggest you shouldn't use it for that, though I still advocate taking care :)

    --
    Life is a tale told by an idiot -- full of sound and fury, signifying nothing. William Shakespeare, Macbeth

      Localizing the default pattern-matching space ($_) can make subroutines that perform several pattern-matching or other string operations against the same variable a lot quicker to write and easier to read and maintain.

      I'd rather write:

      local $_ = shift; s/\A[ \t]+//; s/[ \t]+\z//; tr/a-zA-Z0-9//cd; return unless length; ...
      than:
      my $x = shift; $x =~ s/\A[ \t]+//; $x =~ s/[ \t]+\z//; $x =~ tr/a-zA-Z0-9//cd; return unless length $x; ...

      Am I being lazy? Yeah, that's part of it, but it's exactly that sort of idiom (if that even qualifies as idiomatic) that I expect to see in Perl code.

        I would agree that this is, in fact, a very Perly thing to do. Also, it is quite clear what the author intends to do w/ this function, and the localization of $_. I would be afraid of moving too far beyond this little stretch of a pronoun if only to prevent leading the maintainer of the code ( usually me! ) VERY far astray.
Re: Using $_ as a temp var, especially in functions
by blokhead (Monsignor) on Oct 23, 2002 at 06:21 UTC
    You may want to consider an idiom like the following:
    for ($user_input) { s/foo/bar/g; tr/A-Z/a-z/g; print unless /^#/; }
    Although I rarely find the need to use $_ as a temporary variable, this idiom makes sense for me. I agree with converter that when doing lots of m//, tr///, s///, and builtins that default to $_, it's a good timesaver.

    Pros: Any pros of using local($_). Seeing a for() loop (that doesn't have for my $i ()) automatically signals a localized $_ in my brain, so I can interpret the body correctly. It works better for me than just saying local($_) at the top. The indented structure of the for loop is a great visual sign that there are scoping changes going on. Also, since $_ is aliased to $user_input, you are actually performing the same operations as if you'd written everything out like $user_input =~ s/foo/bar/;, etc. This is also a con ;)

    Cons: Using a "looping" flow-control structure that will never loop might be confusing. It's like seeing one of these: do { foo; } while (0);, which might (understandably) freak you out. You may also expect the loop to not affect the value of $user_input, since it only modifies $_. Wrong, $user_input gets changed in this code.

    blokhead

Re: Using $_ as a temp var, especially in functions
by erikharrison (Deacon) on Oct 23, 2002 at 04:26 UTC

    $_ is really not what you want. Whenever you'd say "it" in English then use $_, because that's what it is there for. Makes things clean, and it's what I, as the maintainer and reader of your code, expect.

    A variable which keeps track of the last variable used has also been mused about in the past. I think it was even implemented in an experiment(this from when I was a member of p5p). The symantics are hard to tie down, and it only leads to obfuscation - it doesn't make one liners clearer, and makes larger programs into a twist of backreferences, all alike.

    Cheers,
    Erik

    Light a man a fire, he's warm for a day. Catch a man on fire, and he's warm for the rest of his life. - Terry Pratchet

Re: Using $_ as a temp var, especially in functions
by grinder (Bishop) on Oct 23, 2002 at 09:36 UTC
    The problem then comes in is that $_ is global, and if your function modifies it, that could cause a very, very nasty bug

    I would tend to turn that statement around and say that if your code relies on $_ at a higher level, and clobbering it at a lower level results in chaos, then you shouldn't be using $_ at the higher level. There you should be using a lexical.

    It's for this reason that I consider File::Find to be slightly brain-damaged, for it admonishes you to not modify the $_ you are given otherwise all hell breaks loose. The burden of correctness should be on the module, not the client using it. At least for older versions of perl, c.f. v5.005_03

    File::Find assumes that you don't alter the $_ variable. If you do then make sure you return it to its original value before exiting your function.

    Here, for instance, if you want to pass information to a callback, you should be using a function they could call instead of relying on $_.


    print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'

      I would tend to turn that statement around and say that if your code relies on $_ at a higher level, and clobbering it at a lower level results in chaos, then you shouldn't be using $_ at the higher level.

      It is not that easy. Clobering $_ at a lower level can easily break map, for/foreach and grep constructs at a higher level as shown at this topic: while(<>) { ... } considered harmful.

      In short: would you rewrite code like

      my @result = map some_sub($_), qw(a b c d);

      to

      my @result = map { my $x = $_; local $_; some_sub($x) } qw(a b c d);

      to satisfy your requirement?

      --
      Ilya Martynov, ilya@iponweb.net
      CTO IPonWEB (UK) Ltd
      Quality Perl Programming and Unix Support UK managed @ offshore prices - http://www.iponweb.net
      Personal website - http://martynov.org

        If I had the chance, I would rewrite some_sub to work on lists, which would allow me to say

        my @result = some_sub(qw(a b c d));

        But I will grant you that that is a somewhat facetious answer, as I understand your question to mean an arbitrarily complicated map code block and not something as simple.

        But then were the map code block arbitrarily complicated, I would no longer be using map, but instead I would be using for, which would make the code look like:

        my @result; for my $thing(qw(a b c d)) { # complex code block that munges $thing followed by push @result, $thing; }

        On the Principle of Least Surprise, by and large I tend to put unsurprising code in maps and greps. That rules out function calls and other invisible things that might play around with $_. for blocks do not suffer from these problems because you can name your own topicalizer (or whatever it's called) with the for my $topic construct. If you stick to this, you can't be bitten by the types of problems you describe.

        <update> if you have code running around secretly overloading the + operator, then clobbering $_ is likely the least of your problems. Just because you can write code that dicks around with $_, doesn't mean you should. We'll just have to agree to disagree. Your point is valid and I find no fault with it, I just see things differently.</update>


        print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
Re: Using $_ as a temp var, especially in functions
by BrowserUk (Pope) on Oct 23, 2002 at 12:05 UTC

    Personally, if an intermediate var is used is in more than one line of code (or possibly two), it no longer warrents the nomenclature "temporary". At some point in the future, someone (possibly you:) is going to come along and insert some code between some of the lines where the 'temporary var' is used and that seperates it from its temporaryness (Is that a word?).

    In any other language I've used, my advice would be to just go ahead and use a locally-scoped (lexical in Perl terminology ie. my $tempSomething). However, the propensity (and utility) of Perl's built-ins to use $_ leads to a slightly ambiguous decision, in that I do use $_ where that makes sense because I am going to apply one or more built-in features which default to that.

    However, I wouldn't use the for ($thingToAlias) { ... }; as I personally find the one-time-loop construct distracting. In preference I would use a bare block.

    { ## Updated: The next line was (erroneously) local $_ = $thingToAlias, local *_ = \$thingToAlias; ##which [Aristotle] pointed out *doesn't* alias m/regex1/; s/regex2/modification/; #other stuff }

    To me (who is still experimenting and evolving my Perl Style), this has all the advantages of the 1-time for with none of it's disadvantages. It will be interesting to see if anyone has any arguments against this.


    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
      The biggest argument against it that I can see is that the for will probably be translated to a given in Perl 6, while your local probably wouldn't.

        Interesting point. I hadn't considered Perl 6, I'm trying hard not to get excited about until it's a little closer to fruition.

        When you say "the for will probably be translated to a given", do you mean by an automated process? Is there an intention to provide a p5top5.pl or binary? I thought that the route was to have a p5 'mode' rather than conversion?

        Given all the different ways the scalar value in the for loop could be provided

        for ($scalar) {...} for (funcReturningScalar()) {..} for ($hash{of}{some}{arbitrar}{size}{returning}{scalar}) {...} for($$hash{returning}{refToScalar}) { ... } # Many more

        Unless this p5top6 processor is going to act at the bytecode level, its going to have to be a fairly sophisticated piece of code to catch all the possible variations where the topicaliser of the for returns a scalar and not a list? Would it be that much more work to have it check to see if the first line inside a bareblock was local $_ = expression; which by definition must be assigning a scalar?

        Thinking about this further, I'm not sure I understand though. As given is a case-type construct, converting a one-time for to a given would result in some strange code

        for ($scalar) { # do stuff; }

        Becoming

        given ($scalar) { when <<always>> { # do stuff; } }

        I'm not quite clear how you would express the always? Though I assume given will have an equivalent of C's default: clause, so I guess it could become

        given ($scalar) { default { # do stuff; } }

        But I'm not sure that's much better.


        Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!

      There is a very significant difference: your second snippet isn't aliasing anything, it's making a local copy.

      I tend to use for as a topicalizer for single expressions, as a statement modifier. It would be cumbersome to bloat those into a naked block with a local, and will often not even do what I want since I'm actually intending to change the original variable. A silly example would be s/nit// for $style;

      For temporary variables, which was what this thread really was about, I use local. Not only does a for block not do what I want anyway (except (close your eyes) in the perverted for(my $temp) { ... } form (now open them again)) because it aliases a variable rather than give me a fresh, empty one, but it also adds another indentation level, which in my stylistic preference is to be avoided at all cost.

      for and local are really orthogonal to each other. Neither can do what the other does, and so debating which one is preferred is moot.

      Makeshifts last the longest.

        second snippet isn't aliasing anything

        Good point. It would have to be

        $_= 'value before localising'; my $thingToAlias = 'the quick brown fox'; { local *_= \$thingToAlias; s/brown//; } print $thingToAlias; print $_;

        Giving

        C:\test>test the quick fox value before localising C:\test>

        A silly example would be s/nit// for $style;

        I can't see any advantage in this over $style =~ s/nit//;?

        I appreciate this was just a trivial example, but I can't see where this would be useful as a statement modifier? (No doubt you have a great example up your sleeve:^)

        For temporary variables, which was what this thread really was about, I use local. Not only does a for block not do what I want anyway (except (close your eyes) in the perverted for(my $temp) { ... } form (now open them again)) because it aliases a variable rather than give me a fresh, empty one, but it also adds another indentation level, which in my stylistic preference is to be avoided at all cost.

        for and local are really orthogonal to each other. Neither can do what the other does, and so debating which one is preferred is moot.

        Agreed. They are unrelated, but I was responding to Re: Using $_ as a temp var, especially in functions in the second part of my post rather than the OP.

        I first saw the 1-time for when you showed it to me in a thread about a week ago (which I can't now find?) but I was uncomfortable with the idiom of using a loop for a straight through peice of code. I'm similarly troubled by the use of redo in a bare block or if statement, prefering to see a loop construct where things are going to loop.

        I did spend a while after you first showed me the 1-time for looking for a suitable alternative. And came up with the local *_ = \$variable; (Which in turn was stolen from the Cheap idioms thread.), but promptly forgot the detail as I haven't had a need to use it yet. That's also why I was interested in the arguments against it.

        Who knows, maybe in time I'll get used to these idioms as I have others and go with the flow (sic), but as I said, my devlopment of a personal style, and my investigations of what's possible in perl is ongoing.


        Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Re: Using $_ as a temp var, especially in functions
by bprew (Monk) on Oct 24, 2002 at 03:59 UTC

    From what I understand from a Perl 6 meeting a couple of weeks ago, it will have a "topic", which allows you to specify a value for the temporary value as its passed in... (ie

    for @list -> $foo; # $foo can now be used in place of $_ for the rest +of the block

    Also, $foo defaults to read-only, so you have to specificly specify read-write mode.

    PS I'm sure I've made a mockery of Allison's speech, so please forgive me. Also, the minutes of our meeting should be up soon at Portland Perlmongers.

    Topics and topicalizers are also explained in more detail in exegesis 4.
    --
    Ben
    "Naked I came from my mother's womb, and naked I will depart."

      Which is what in Perl5 lingo is for my $foo (@list) { ... } except the read-only bit of course. ;-) Perl6 will offer additional abilities on top of this.

      Makeshifts last the longest.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://207264]
Approved by toma
Front-paged by ignatz
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2021-04-18 21:49 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?