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

Consider the following program that intends to decide if a day is a working day:

#!/usr/bin/perl -l use strict; use warnings; my $dow = 2; while ($dow < 8) { my $workday = 0 if not $dow == 6 and not $dow == 7; $workday = 1 if $dow == 6; ### print "$dow $workday"; $dow++; }

The logic is simple: weekends are not workdays, unless it's a special Saturday marked by the government as an extra workday (line marked by ###, condition simplified for sake of minimal example).

I get the following output:

2 0 3 0 4 0 5 0 6 1 7 1

To use a technical expression, what the hell is going on here? It looks as if the value of 1 somehow "leaked through" to the next iteration of the while loop (however stupid this sounds). Is this a bug? Is it a known bug?

I get this with Perl 5.20.3 (Fedora 22's default 64 bit system Perl)

Replies are listed 'Best First'.
Re: Paranormal leakage of previous value from conditionally set lexical variable
by Anonymous Monk on Mar 09, 2016 at 19:42 UTC

    perlsyn:

    NOTE: The behaviour of a my, state, or our modified with a statement modifier conditional or loop construct (for example, 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.
    $ perl -Mdiagnostics -wMstrict -le 'my $x if 0' Deprecated use of my() in false conditional at -e line 1 (#1) (D deprecated) You used a declaration similar to my $x if 0. There has been a long-standing bug in Perl that causes a lexical variable not to be cleared at scope exit when its declaration includes a false conditional. Some people have exploited this bug to achieve a kind of static variable. Since we intend to fix this bug, we don't want people relying on this behavior. ...

      (OP here)

      Thanks, so at least it is known and documented.

      I don't like the fact that as opposed to your code, I don't get any warning, the code just silently fails, and that this behavior is in Perl since at least 5.8, and according to the diagnostic message it is a known bug that they meant to fix but didn't.

        This won't help in your case, but fwiw, Perl::Critic catches this.

Re: Paranormal leakage of previous value from conditionally set lexical variable
by toolic (Bishop) on Mar 09, 2016 at 19:25 UTC
    I can duplicate your results on Perl 5.16.3.

    What do you expect your output to be? I expect to get the following warning when dow=7 since you don't set workday to a defined value:

    Use of uninitialized value $workday in concatenation (.) or string

    Tip #6 from the Basic debugging checklist: B::Deparse, but it doesn't reveal much.

    Interesting to change 8 to 9:

    while ($dow < 9) { ... 2 0 3 0 4 0 5 0 6 1 7 1 8 0

      8 is not 6 or 7; therefore the if sets $workday to 0.

      But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

Re: Paranormal leakage of previous value from conditionally set lexical variable
by 1nickt (Canon) on Mar 09, 2016 at 19:25 UTC

    Don't see why, but it seems to be a problem of precedence. Try:

    my $workday = 0 if not $dow == 6 && not $dow == 7;

    The way forward always starts with a minimal test.