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

I caused a bit of a problem for myself in some code when I did this:

use strict; # forgot this in the original ;( outer_loop { my $headerStr; ... # some code that may or may not initialize $headerStr my @headerElements = split /\s+/, $headerStr if $headerStr; if(! @headerElements) { # Create content for use later. $headerElements[0] = 'some value'; $headerElements[1] = 'some value'; $headerElements[2] = 'some value'; } $headerElements[2] =~ s/[<>,]//g; ... # other checks and tests of the array content follow. }

Of course, there were problems ;), since my @headerElements only processes if $headerStr is set.

When it's not set, the lexical scoping of the array does not happen. So the assignments to the array need to autovivify the array. I assume this is what happens, since there is no complaint from Perl and something does appear in the array.

I also assume the scope of this autovivified array is global, since data is getting carried over from one iteration of the outer loop to the next, when it wouldn't if the array were lexical.

I can't find any reference in the Perl docs I've searched to support the global scope conjecture. Can anyone point me to anything about this?

Thanks.

Replies are listed 'Best First'.
Re: scope of an autovivified variable?
by davido (Cardinal) on May 11, 2011 at 19:55 UTC

    From perlsyn:

    NOTE: The behaviour of a my statement modified with a statement modifier conditional or loop construct (e.g. my $x if ... ) is undefined. The value of the my variable may be undef, any previously assigned value, or possibly anything else. Don't rely on it. Future versions of perl might do something different from the version of perl you try it out on. Here be dragons.

    I think that's clear enough. And perlsyn is far from obscure documentation.

    Update:

    Perl doesn't natively complain to you if you use such a construct. However, there's a tool that I have in my arsenal which is so simple to use that I've managed to make it a habit as I work on scripts. Start with Perl::Critic. Next, create a Perl script based on the synopsis of Perl::Critic's documentation, as follows:

    use Perl::Critic; my $file = shift; my $critic = Perl::Critic->new(); my @violations = $critic->critique( $file ); print @violations;

    Now, name that script 'criticize', and put it in your favorite toolbox bin directory. Finally, as you're composing scripts, periodically pass it through the complaint department like this:

    criticize mytest.pl

    And the output for the specific 'violation' we're discussing:

    Variable declared in conditional statement at line 9, column 1. Declar +e variables outside of the condition.

    Dave

      Thank you.

      I don't think I said anything about obscure, I just said I couldn't find anything about it.

      That's because I was focused on 'autovivification' and was searching for references to that word, not for use of 'my' in a conditional construct.

        I wasn't suggesting that it was easy to search for. In fact, the entire issue ought to be more searchable; it's a question that gets asked here often. I don't know of a perlfaq that covers it, but it's one of those things that comes up often enough it should be part of the FAQ.

        I probably should have been more specific. What I meant was that perlsyn is one of the top ten 'must read' documents for someone using Perl. perlintro, perlsyn, perlop, perlvar, perlsub, perldata, perlre... ok, that's top seven. ;)

        "Why is my( $variable ) = 'something' if $condition; almost certainly an error?" seems like a very good candidate entry for perlfaq7, though. ;)


        Dave

Re: scope of an autovivified variable?
by ikegami (Patriarch) on May 11, 2011 at 20:03 UTC

    I also assume the scope of this autovivified array is global,

    It's not autovivified. It is explicitly created by my. Note that my does this at compile-time.

    my @headerElements = split /\s+/, $headerStr if $headerStr;

    «my ... if ...;» is not valid code. One cannot use a variable declared using my without first executing that my. This example is specifically documented in perlsyn ("NOTE: The behaviour ...").

    The documentation is a bit suboptimal, since it doesn't mention «... and my ...;», «... ? my ... : ...;», etc,

    I also assume the scope of this autovivified array is global

    No, it's lexically scoped. Scope refers to where a variable is visible, accessible. That's why state variables are said to be lexically scoped too.

    data is getting carried over from one iteration of the outer loop to the next, when it wouldn't if the array were lexical.

    You are mistaken in your belief that lexical variables get deallocated at the end of their scope.

    In the following, in the second call of f(), you can see effects the first call of f() had on $x:

    $ perl -MDevel::Peek -e'sub f { my $x; Dump($x); $x="abc"; } f;f;' SV = NULL(0x0) at 0x93fcee0 REFCNT = 1 FLAGS = (PADMY) SV = PV(0x93eb040) at 0x93fcee0 REFCNT = 1 FLAGS = (PADMY) PV = 0x93f8e00 "abc"\0 CUR = 3 LEN = 4

    Update: Fleshed out some details.

      OK, but I'll have to take your word regarding Devel::Peek and it's output. I have no idea what it means (for now).

      What I do know is this:

      #!/usr/local/bin/perl -w use strict; use Data::Dumper; sub f { my $x; print Dumper($x); $x="abc"; } f; f;

      And the output is:

      $VAR1 = undef; $VAR1 = undef;

      Whether the storage space is deallocated or not seems to be irrelevant with respect to general use. I can't get back to the original variable the second time around. Or at least, that's what it looks like. Perhaps some additional discussion about what you mean would help my feeble understanding.

        Perhaps some additional discussion about what you mean would help my feeble understanding.

        You said the "data is getting carried over from one iteration of the outer loop to the next, when it wouldn't if the array were lexical."

        That's not true, because the array is lexical. Whatever you think is happening doesn't change the scope of the variable.

        I can't get back to the original variable the second time around.

        You say "variable" when you mean "value".

        Updated based on better understanding of the question.

Re: scope of an autovivified variable?
by TomDLux (Vicar) on May 12, 2011 at 23:10 UTC
    my $var = 42 if 0;

    used to be the idiom for achieving a state variable, that is, one that lived on from one invocation of a subroutine or block to another. it usually leads to more trouble than benefit, and anyway, since 5.10 there is the state keyword.

    A simple way to achieve the result you want is to reverse the order of alternatives for @headerElements. Instead of handling the exception of $headerStr not having a value, why not assign @headerElements a deafult value, and then possibly override that with values derived from $headerStr?

    my @headerElements = ( 'value 1', 'value 2', 'value 3' ); @headerElements = split /\s+/, $headerStr if $headerStr;

    Another way is to preserve the order you had, but use a ternary operator ... an inline if-block:

    my @headerElements = ( $headerStr ? split /\s+/, $headerStr : ( 'value 1', 'value 2', 'value 3' ) );

    You could also do it using a logical-or test operator to detect whether the result of the split is a true or false value. This could result in complications if the left value is valid but logically false, for example, a zero or an empty string. But in this case it seems safe enough. Note that you have to parenthesize the arguments to split(), to specify the end of the arg list.

    my @headerElements = split( /\s+/, $headerStr ) || ( 'value 1', 'value + 2', 'value 3' );

    As Occam said: Entia non sunt multiplicanda praeter necessitatem.