Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

uninitialised variable

by opaltoot (Novice)
on Dec 30, 2016 at 14:13 UTC ( [id://1178655]=perlquestion: print w/replies, xml ) Need Help??

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

This recently caught me out because it seems to work the first call, but not the second. What's going on?
print test(), "\n"; print test(), "\n"; sub test { my $test = 'a' if shift; $test .= 'b'; }
Prints
b bb

Replies are listed 'Best First'.
Re: uninitialised variable
by stevieb (Canon) on Dec 30, 2016 at 14:30 UTC

    Declaring a variable in a conditional statement has undefined behaviour, and should not be used. From 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."

    update: The code would be better written along the following lines:

    sub test { my $var = shift; my $test; $test = 'a' if defined $var; $test .= 'b'; }
Re: uninitialised variable
by talexb (Chancellor) on Dec 30, 2016 at 14:52 UTC
      my $test = 'a' if shift;

    This line clearly is doing too much at once. It can be broken down (as has already been explained) to

    • my $test; and
    • $test = 'a' if shift;
    This may be you fooling around with Perl's syntax, or it may be a simplification of a real-world problem, but I'm hoping it's just fooling around.

    Better would be

    sub test { my $arg = shift; my $test = ''; defined $arg and $test = 'a'; $test .= 'b'; return $test; }
    This is much longer, but I've removed the post-fix statement (personal preference); I have explicitly initialized $test; and it's now clear that the steps are 1) initialize test; 2) set test to 'a' if there was an argument; 3) append 'b' to test; and 4) explicitly return the test value.

    And the best part .. when you run it, you now get

    b b
    as I expect you wanted.

    My comments:

    • I'm not a fan of post-fix -- I have to read it more than once to be sure I'm getting the right meaning.
    • I don't mind writing stuff out so it takes a little longer to read, if the meaning or intent of the code is clearer.
    • Yes, the explicit return is unnecessary in this context, but this then relies on the concatenation operation being the last line of code. Add something unrelated as the last statement, and suddenly the routine's returning an unexpected result.
    • Code clarity matters. If you have to go back to some of your own code after six months and it takes time for you to decipher what's going on .. that's bad. Just make it clear, for your own sake, and for anyone else who has to read your code.

    Alex / talexb / Toronto

    Thanks PJ. We owe you so much. Groklaw -- RIP -- 2003 to 2013.

Re: uninitialised variable (my: Run Time vs Compile Time)
by LanX (Saint) on Dec 30, 2016 at 14:28 UTC

    my has a dual nature:

    • a declaration at compile-time and
    • an initialisation at run-time to the assigned value (or undef if missing)

    so in your code

    • at compile-time every variable $test in the scope of the function (post-fix if has no own scope) is bound to a storage in the "functions-pad"
    • at run-time the variable is not reset

    Hence you just disabled the initialisation and the old value survives.

    Rule of thumb: using my within a conditional branches without own scope is bad style and vulnerable to creating unwanted side effects!

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

Re: uninitialised variable
by haukex (Archbishop) on Dec 30, 2016 at 18:00 UTC

    Hi opaltoot,

    As the others have already said (such as the piece of documentation from perlsyn that stevieb quoted), don't put statement modifiers on my. There's also this related piece of doc, from perldiag (try running perl -wMdiagnostics -e 'my $x if 0'):

    Deprecated use of my() in false conditional

    (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. You can achieve a similar static effect by declaring the variable in a separate block outside the function, eg

    sub f { my $x if 0; return $x++ }

    becomes

    { my $x; sub f { return $x++ } }

    Beginning with perl 5.10.0, you can also use state variables to have lexicals that are initialized only once (see feature):

    sub f { state $x; return $x++ }

    Hope this helps,
    -- Hauke D

Re: uninitialised variable
by hippo (Bishop) on Dec 30, 2016 at 16:05 UTC

    You have some very good replies already. I would only add that perlcritic picks this up:

    $ perlcritic 1178655.pl Code before strictures are enabled at line 1, column 1. See page 429 +of PBP. (Severity: 5) Variable declared in conditional statement at line 5, column 3. Decla +re variables outside of the condition. (Severity: 5)

    If you have some odd-looking code like this which does not behave how you expect, perlcritic is a decent (but not infallible) tool to use in order to obtain some hints as to possible problems.

Re: uninitialised variable
by kcott (Archbishop) on Jan 01, 2017 at 03:05 UTC

    G'day opaltoot,

    Given you're attempting to return one of two constant values ('ab' or 'b') from &test, I don't see the need for $test in that subroutine at all.

    Because of the problems with the code, and the fact that you only show calls with no arguments, your intent is unclear. Having said that, I think your old friend the ternary operator can help.

    If you want to test whether there were any arguments:

    $ perl -E 'sub t { @_ ? "ab" : "b" } say for t(), t(), t(0), t(1)' b b ab ab

    If you want to test whether the first argument was TRUE:

    $ perl -E 'sub t { $_[0] ? "ab" : "b" } say for t(), t(), t(0), t(1)' b b b ab

    — Ken

Re: uninitialised variable
by 1nickt (Canon) on Dec 30, 2016 at 14:29 UTC

    Edit: that's what I get for answering without testing :-P Below is incorrect, see the first reply above.

    You made a closure?


    The way forward always starts with a minimal test.

      I don't see how. A closure is a function that returns another function, which has access to variables in its outer scope. I don't see that here.

        > A closure is a function that returns another function,

        No a closure is (in normal speak) only a function which is accessing variables outside from it's own body at declaration time.

        The surrounding is not necessarily "another function" and the closure function is not necessarily "returned".

        Technically the combination

        • function plus
        • "enclosed" variables

        is called closure!

        see Closure_(computer_programming)

        Most people ignore these variables as implementation detail and keep concentrating on the "closure function" calling it simply the closure.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

        update

        Here an example of getter and setters implemented as closures which don't fall under your definition.

        { my $a; sub get_a { return $a } sub set_a { $a = shift } }

        Update:

        more examples here perlfaq7#What's-a-closure%3f

Log In?
Username:
Password:

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

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

    No recent polls found