http://qs1969.pair.com?node_id=40924

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

Hello all Monks!

I used to browse through all the codes you provide as a solution to the others questions. I realized that lot of you used to redeclare variables in a while loop, for example:

while (my $var) { #do something with $a }

The lead software engineer/guru here told me that it is a very bad behaviour, so better to avoid. As well there is a lot of guru here too, please tell me what is the truth. Is it dangerous, or I can apply this style without any PITA (pain in the ass)? :)

-- tune

Replies are listed 'Best First'.
RE (tilly) 1: redeclaring variables with 'my'
by tilly (Archbishop) on Nov 10, 2000 at 17:20 UTC
    Tell the lead software engineer that Perl gurus recommend making variables available with the smallest possible declared scope. And lexical is preferred over dynamic scoping. The above code works fine in Perl from version 5.004 onwards and gives you maximal protection against accidentally using the same variable twice for two different things.

    If you need to work with 5.003, then avoid. Otherwise I would recommend this style in general.

    Although doing it in a while loop is not normal for me. Usually I do things like:

    foreach my $thing (@list) { #Something interesting }
      "Perl gurus recommend making variables available with the smallest possible declared scope"

      Why is that? Is it for efficiency reasons or mainly for organization and readability purposes?

      I tend to predeclare all my variables at the top of the file so they are listed in one place. I find it much easier to keep track of my variables that way. I guess I got into that habbit during my Pascal and C days.

      --Glenn

        As davorg said, because it is a good software engineering practice.

        Pick up a good book on programming (eg Code Complete) and read up on the concepts of loose coupling vs tight coupling. Tightly scoped variables go hand in hand with loose coupling between different sections of code which goes hand in hand with code that is easier to maintain, adapt, and modify.

        These principles have been known for decades, but far too many people don't know about them. Also a lot of people have (sorry to pick on you) misconceptions such as beliefs that you need to make it easy to keep track of all variables you use anywhere.

        That is actually the opposite of what you should want.

        You want to make it absolutely useless to keep track of as many variables as possible by giving them private scopes where they cannot accidentally have anything non-obvious happen to them. Guaranteeing no accidents with a tight scope is safer and easier to maintain than manually remembering which variables got used somewhere and so should not be accidentally stomped on.

        However when you are done there are a small list of variables that you will need to give larger scopes to. (For instance package globals that you are willing to export.) It is still a good style to put those at the top. But now you get a second win. By winnowing down the list of global variables to the minimum necessary, you have made the list of important things to remember much smaller. Therefore you have just made it easier to keep track of things that you needed to track!

        So the recommended style then works out to at the top of your file use strict to catch mistakes, use vars to declare in an obvious place everything that you need to track in your code, and then protect every little variable you need to use by making them lexicals with tiny scopes. The protection from scoping then protects those far more securely than you could do manually, and you will find it easier to track and manually protect the few that are left over.

        Given the choice when hiring a Perl programmer, I would prefer to hire someone who understood good software engineering principles but didn't know Perl than I would to hire someone who knew a lot of Perl but thought good software engineering was bunk. I am confident that I can teach the first person to program in Perl. I don't know how to keep the second from being a hazard to the company's code-base.

        "Why is that? Is it for efficiency reasons or mainly for organization and readability purposes?

        Largely because it makes it less likely that you'll stomp all over another variable with the same name.

        --
        <http://www.dave.org.uk>

        "Perl makes the fun jobs fun
        and the boring jobs bearable" - me

Re: redeclaring variables with 'my'
by Fastolfe (Vicar) on Nov 10, 2000 at 19:19 UTC
    Usually I tend to back out when my own knowledge seems to differ when the majority, but just for the record, my understanding agrees with this guy's guru's. These two while loops are not equivalent (and not for the reasons you think):
    # within while (my $x = $count) { $count++; last if $count >= 5000000; } # without my $x; while ($x = $count) { $count++; last if $count >= 5000000; } Benchmark: timing 10 iterations of within, without... within: 40 wallclock secs (39.78 usr + 0.04 sys = 39.82 CPU) @ 0 +.25/s (n=10) without: 35 wallclock secs (35.09 usr + 0.00 sys = 35.09 CPU) @ 0 +.28/s (n=10)
    Now, I'm no expert on Perl internals, but it certainly seems like $x is being re-allocated for every loop. At a minimum, $x falls out of scope when the loop ends, which leads me to believe it's falling out of scope for each iteration and being re-allocated. Note that it isn't being redeclared, just reallocated. I'd be interested if anyone has any other explanation...
      There is some run-time action upon entering and exiting its scope, so there is indeed some overhead. It does not need to allocate and deallocate space, but Perl does need to manipulate reference counts and check that the old space allocated is for a variable not in use and so can be reassigned. (Just in case you saved a reference and kept a closure!) But even within this minimal loop the overhead is minor.

      So yes, there is a small amount of overhead associated with the safer mechanism. However when you try conciously switching styles between projects you will quickly find that the safer style results in big wins in terms of reliability, development, and debugging. Besides which, aggressively modularizing your code etc gives you opportunities to later find and fix large algorithm mistakes. (Leading to better performance wins than optimizing the heck out of your code up front can do.)

      The tiny performance difference is a really, really bad excuse for keeping a bad habit. For more on this topic, there is a sample section from Code Complete online.

        I agree completely, and have been reluctant to move away from the while (my $var ...) style for that reason (except in cases where I'm expecting to iterate very tightly many thousands of times). A 15% win isn't much when we're talking about, like, picoseconds here. Heh.
      It's not re-allocating memory each time through the loop. You can test that by doing this:
      my $count = 1; while (my $x = $count) { print \$x, "\n"; last if ++$count > 5; }
      If you run that, you should get something like:
      SCALAR(0x80e4aa8) SCALAR(0x80e4aa8) SCALAR(0x80e4aa8) SCALAR(0x80e4aa8) SCALAR(0x80e4aa8)
      Each time through the loop $x is at the same memory location. So it's not re-allocating memory.

      As tilly mentions in his post, Perl does have to do *some* extra things--but re-allocating memory isn't one of them. :)

        If it were tightly de-allocating and re-allocating, is it not unreasonable to expect that they might end up at the same address? Not that I disagree with you; I'm hardly the expert. I rather think that what you say is actually the case.

        But yah, I think tilly's hit it on the head.

      You are correct Fastolfe, the re-allocation happens in the loop. However, this is the behavior that is GOOD. The cost of the allocation is a great deal less than this
      my $x='ok'; # scads of code my $x=300; while ($x--) { do_something_with($x); } # scads of code... print "$final" if ($x eq 'ok'); #kaboom

      In the end, when you do a "-w" and it complains that '$x won't stay shared' or something at the second my, your lesser perl wizard is likely to take the "my" off since the variable is already declared. Ouch.

      In the end, you suffer the cost of extra work on the backside to protect yourself from the future.

      --
      $you = new YOU;
      honk() if $you->love(perl)

Re: redeclaring variables with 'my'
by mirod (Canon) on Nov 10, 2000 at 17:16 UTC

    I think you slightly mis-stated the common construct, it should be:

    while( my $var= func( @args)) { # do something }

    Otherwise the my $var returns undef which might not be what you want (the loop would not be executed).

    As far as I know this is perfectly kosher. $var is declared, its scope is the inner block and I would bet (Perl guts Gurus can confirm this) that the whole thing is optimized so the variable is actually built (ie allocated) only once.

    I'd be interested in knowing what are your local guru arguments.

Re: redeclaring variables with 'my'
by ChOas (Curate) on Nov 10, 2000 at 17:09 UTC
    Why does he say it is bad behaviour ?
    okay, I have to admit that some compilers
    'n stuff are not that smart and start allocing
    and de-allocing variables right aways when out of
    scope (not so handy when you're redeclaring IN
    a loop that runs a million times or so)

    But, initialising a new var in the while of the loop is not
    that bad I think, it's just local to the loop, declared
    once, and out of scope out of your loop... works for me..

    By the way... while (my $Var) is NOT initialising,
    $Var is allocated as undef, then processed by the while,
    verified as not true, and the code in the while will
    never run

    But then again... what do I know....

RE: redeclaring variables with 'my' (use vars qw($var); and my $var ?)
by ybiC (Prior) on Nov 10, 2000 at 18:10 UTC
    Might the lead software engineer been railing against using my $var in a loop when $var already declared with use vars qw($var)? Resulting in two scalars of same name, one of which isn't even used.

    I did the same on occasion, until corrected here at PM.
        cheers,
        Don
        striving for Perl Adept

Re: redeclaring variables with 'my'
by tune (Curate) on Nov 10, 2000 at 17:23 UTC
    Oh yes i meant filling up the variable combined with the my statement, like:

    while (my $var = <FILE>) {} # or similar
    The local guru used to speak about redeclaring and confusion

    -- tune

      Tell him it is NOT redeclaring, it is only allocated
      once, at the 2..nth time in the while the 1st declared
      variable is used...

      It is just a local, like this:
      ... { my $var=<FILE>; while ($Var) { ... ... $var=<FILE>; }; } # $Var out of scope ...

      Try that on him ;))

      GrtZ!,
        ChOas
Re: redeclaring variables with 'my'
by roberto (Acolyte) on Nov 10, 2000 at 17:39 UTC
    my $var get declared only once!