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

Okay, I've encountered one of those little code tidbits that make me want to just crawl under my desk and weep. Instead, I'm deferring to you guys in the hopes that one of you can explain this to me, since I really don't feel like taking my usual 2-3 days of extensive debugging and isolationism 'til I figure it out.

This is with perl 5.005_03.
my $data = "first line second line third line last line"; my $data2 = $data; print "Case 1:\n\n"; foreach (1..8){ my $line = undef; #lexical variable! $line = $1 if $data2 =~ s/^([^\n]*)\n//; #assign to the lexica +l if (! defined $line && $data2 !~ /\n/){ $line = $data2; $data2 = undef; }; print "SPEC: ($line)($data2)\n"; }; print "\n=======\n\nCase 2:\n\n"; foreach (1..8){ my $line = $1 if $data =~ s/^([^\n]*)\n//; #make a lexical and + assign to it if (! defined $line && $data !~ /\n/){ $line = $data; $data = undef; }; print "SPEC: ($line)($data)\n"; };

And here's the output:

Case 1: SPEC: (first line)(second line third line last line) SPEC: (second line)(third line last line) SPEC: (third line)(last line) SPEC: (last line)() SPEC: ()() SPEC: ()() SPEC: ()() SPEC: ()() ======= Case 2: SPEC: (first line)(second line third line last line) SPEC: (second line)(third line last line) SPEC: (third line)(last line) SPEC: (last line)() SPEC: (last line)() SPEC: (last line)() SPEC: (last line)() SPEC: (last line)()


The first case is the "correct" one, meaning it works the way I want to/think it should. The second case is the "incorrect" one, meaning it doesn't work how I want it to, and I can't quite figure out why. The only difference between the two is where the variable $line is lexicalized.

Any ideas?

Replies are listed 'Best First'.
RE: lexical weirdness(?)
by dchetlin (Friar) on Oct 25, 2000 at 19:57 UTC
    As the anonymous but wise monk pointed out above, this is a case of the `my $x if 0' feature. For some reading material on this, start here and look through some of the p5p threads on it.

    I doubt this feature will go anywhere for Perl 5 at least, because it doesn't seem to cause any problems and allows you to do something (create a static variable) which you can't really do any other way.

    But it does mean you should be careful when declaring lexicals.

    -dlc

      Thanks for the help guys. I'll begrudgingly admit that this does make sense, though I'm not too pleased with it.

      Regardless, just to be good and point it out, my $x if 0; is most certainly not the only way to create a static variable. This is clearer, IMHO, and more widely used. (I hope)
      { my $static; sub some_function { print "Static!", $static++; }; };

      Just wrap up the function in braces and lexically scope any "static" variables you want in the enclosing braces. Much clearer, IMHO.
        There are at least two major differences between closures and static-via-my-if-0 variables.

        Here's the first. Look closely at this code and see if you can figure out what its output will be before you run it.

        #!/usr/bin/perl -w use strict; { my $closure; sub func_w_closure { print "Closure! ", $closure++, "\n"; func_w_closure($_[0]+1) if $_[0] < 3; } } sub func_w_static { my $static if 0; print "Static! ", $static++, "\n"; func_w_static($_[0]+1) if $_[0] < 3; } func_w_closure(0); func_w_static(0);

        I'll use tilly's spoiler trick:

        [~] $ dev/test/scope.pl Closure! 0 Closure! 1 Closure! 2 Closure! 3 Static! 0 Static! 0 Static! 0 Static! 0

        In other words, the closure is the same variable through all calls, including recursive ones, while each recursive call to the func_w_static gets its own copy.

        The cool thing about that, of course, is that if you call func_w_closure again, you see that each recursive instance keeps its value!

        The other difference is that closures can be shared among functions -- if you stick another function into the lexical scope of `$static' above, some_function and your new function will refer to the same variable.

        -dlc

Re: lexical weirdness(?)
by Malkavian (Friar) on Oct 25, 2000 at 19:36 UTC
    *Cough* Weird one..
    Does this behaviour still occur if you declare my $line on it's own on the second loop?
    If not, a wild stab in the dark would be that you're somehow forcing the $line to stay in scope by using it along with the if and substitute on the global variable $data, like a closure.

    Malk
    (This is a VERY wild stab in the dark)
    *I lost my .sig. Do you have a spare?*
Re: lexical weirdness(?)
by mirod (Canon) on Oct 25, 2000 at 19:31 UTC

    It looks like the my does not reset the variable value to undef if it is initialized through a test.

    Those 2 snippets are not equivalent either:

    foreach (1..2) { my $i; print "$i\n"; $i++; }
     foreach (1..2) { my $i=0 if( 0); print "$i\n"; $i++; }

    Now is this a bug or a feature?

      my $foo if 0;
      

      This is a "feature" that has been covered on p5p... when the "if" fails, the lexical variable is not re-allocated and so stays whatever it was. I believe the consensus was not to expect this feature to stick around.

        Well, it was an accident that some decided was a feature causing "the rest of us" to put them straight that this "feature" isn't something they should expect to never be "fixed". (:

                - tye (but my friends call me "Tye")