So, I just tracked down a bug which can be demonstrated in this trivial subroutine:
sub foo {
my $bar = shift or die "Missing bar", # <--- not a semicolon
my @items = ();
push @items, $bar;
return @items;
}
Obviously the mistake is that the first line of the subroutine ends in a comma. This had some rather unusual consequences, as can be seen:
say foo(1); # 1
say foo(1); # 11
say foo(1); # 111
say foo(1); # 1111
Now, I understand that this isn't a syntax error because of how the comma operator works. I understand that @items
is not being set to ()
because the right side of the or
isn't being reached. My question is, how can a variable declared with my
inside of a subroutine allow data to persist between subroutine calls? It seems as if the my
is turning into an our
somehow.
Local variables are stored in memory area of the corresponding function. Scope of a variable is a program part, in which a variable can be referred to. Variables declared inside a block (at the internal level), have the block as their scope.
The reason for the limited scope of local variables is that local variables are stored in the stack, which is dynamic in nature and automatically cleans up the data stored within it. But by making the variable static with "static" keyword, we can retain the value of local variable.
The lifetime of a variable is the time during which the variable stays in memory and is therefore accessible during program execution. The variables that are local to a method are created the moment the method is activated (exactly as formal parameters) and are destroyed when the activation of the method terminates.
There is no default value for local variables, so local variables should be declared and an initial value should be assigned before the first use.
B::Deparse
is invaluable in exercises like this:
$ perl -MO=Deparse 31191808.pl
sub foo {
die 'Missing bar', my(@items) = () unless my $bar = shift @_;
push @items, $bar;
return @items;
}
which makes this a variant of the my $var if 0
trick/bug/curiosity. Its effect is to create a lexical but static variable, which will not be reinitialized each time foo
is called.
What you are doing is very similar to this snippet :
use v5.14; # Implies strict
sub foo {
my @something= () if 0;
push @something, shift;
say @something;
}
foo($_) for 1..5;
The output will be :
1
12
123
1234
12345
In Perl, conditionally declaring a variable makes it only assign a value whenever that condition is true. If you changed the if 0
to if $_[0] == 3
, you'd get a completely different sequence of numbers. This is actually an old bug in Perl that cannot be fixed anymore because a lot of code might depend on it, but if you're lucky you might see this warning: "Deprecated use of my() in false conditional"
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With