Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Conditional initialisation

by Akhasha (Scribe)
on May 20, 2004 at 02:17 UTC ( [id://354824]=perlquestion: print w/replies, xml ) Need Help??

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

Today I got something of a slap in the face. Well, at least it made me blush and there is a sting to thinking back on where I might have used a construct like this in a subroutine:

my @list = @_ if @_ > 1;

For the last few months I've been coding away and occasionally using this or something like it. I thought it would always initialise a new lexical and that the assignment was what the condition applied to.

However it seems that in the above case for example, if @_ has 0 or 1 elements, then @list has the value it was assigned the last time the function was called and the condition held true. I just spent the last hour or so going back over my code and breaking this kind of construct into two lines:

my @list; @list = @_ if @_ > 1;

Firstly I just want to caution my fellow monks to be on the lookout for it, as it works the first time and afterward can turn around to bite the naive.

Secondly I'm wondering where this behaviour is clearly explained in the perl docs. I thought that because @list is a lexical, the value could not be there to be picked up on a second invokation of the function. Any pointers or observations on this would be appreciated. Thanks!

Replies are listed 'Best First'.
Re: Conditional initialisation
by broquaint (Abbot) on May 20, 2004 at 02:35 UTC
    This behaviour is somewhat of a bug, in that it's unexpected behaviour, but it's not going away anytime soon. It is caused by perl not clearing it's lexical pads on exit, for efficiency reasons. See. the classic Unusual Closure Behaviour for an exhausitive discussion on the subject.
    HTH

    _________
    broquaint

      Many thanks, that node is precisely what I was after.
Re: Conditional initialisation
by Somni (Friar) on May 20, 2004 at 02:51 UTC

    The closest thing I can find in the documentation is in perlsub:

    A "my" has both a compile-time and a run-time effect. At compile time, the compiler takes notice of it. The principal usefulness of this is to quiet "use strict 'vars'", but it is also essential for generation of closures as detailed in perlref. Actual initialization is delayed until run time, though, so it gets executed at the appropriate time, such as each time through a loop, for example.

    The section doesn't make it entirely obvious what's going on, and there are certainly no examples, but it is the explanation for why you're seeing what you are.

    The canonical example is use as a static variable: sub foo { my $foo if 0; $foo++ }. perl sees the declaration at compile-time, so space is allocated for it, and it passes strict. But at run-time the variable is never initialized (my $foo; being equivalent to my $foo = undef;), so the value is left as it was.

    This misfeature has been discussed several times on the perl5-porters mailing list. The last time I saw it there was resistance to documenting it in any form lest it be read as an official sanction for the syntax. It is, after all, a method of declaring a static variable, which can be useful.

    In case anyone is curious, the proper way to declare a static is to use a closure:

    { my $foo; sub foo { $foo++ } } ... foo(); foo(); ...
      The canonical example is use as a static variable: sub foo { my $foo if 0; $foo++ }
      Note that in Perl 5.10.0 the use of my $foo if 0 will generate a compile-time warning
Re: Conditional initialisation
by graff (Chancellor) on May 20, 2004 at 04:52 UTC
    Wow. Thanks. The first two replies cleared up a few related uncertainties that were lurking in the background for me.

    But the particular problem you cited is not actually something I would have run into myself, because I've never used that sort of idiom in a subroutine. So I'm curious... what's the motivation for doing this:

    my @list; @list = @_ if ( @_ > 0 ); # (using the "safe" form) # when no args have been passed, @list is empty (undef) at this point. +..
    as opposed to doing this (which is what I always do, and I thought everybody always did it pretty much this way):
    my @list = @_; # when no args have been passed, @list is empty (undef) at this point. +..
    If there's a difference between those two, I'm having trouble seeing it. (Well, okay, with the former, one can easily fall into the trap that you described, but besides that, what's the difference?)

      Ah, well the last time I used this construct it was for a function that could accept either a unix timestamp as a scalar, or a time vector as returned by localtime() or gmtime().

      So I was checking the length of @_ to see if it was greater than 1 (not 0), like this:

      sub unix_to_datetime { my @time_vec; @time_vec = @_ if @_>1; unless (@time_vec) { my $time = shift; @time_vec = (defined $time ? localtime($time) : localtime); } return strftime('%Y-%m-%d %H:%M:%S', @time_vec); }
        Wouldn't the following work just the same?
        sub foo { my @time_vec = @_ > 1 ? @_ : defined $_[0] ? localtime($_[0]) : localtime; return strftime('%Y-%m-%d %H:%M:%S', @time_vec); }

        ------
        We are the carpenters and bricklayers of the Information Age.

        Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

        I shouldn't have to say this, but any code, unless otherwise stated, is untested

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others studying the Monastery: (5)
As of 2024-04-19 00:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found