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';
}
| [reply] [d/l] |
Re: uninitialised variable
by talexb (Chancellor) on Dec 30, 2016 at 14:52 UTC
|
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.
| [reply] [d/l] [select] |
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!
| [reply] |
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
| [reply] [d/l] [select] |
Re: uninitialised variable
by hippo (Bishop) on Dec 30, 2016 at 16:05 UTC
|
$ 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. | [reply] [d/l] |
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
| [reply] [d/l] [select] |
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.
| [reply] |
|
| [reply] |
|
> 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.
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 | [reply] [d/l] |