Local value of variable saving

So, I just kept track of the error that can be demonstrated in this trivial routine:

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 with 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 is not a syntax error due to the operation of the comma operator. I understand that @items not set to () because the right side of or not reached. My question is, how does a variable declared with my inside a subroutine save data between subroutine calls? It seems that my somehow turning into our .

+5
source share
3 answers

B::Deparse is invaluable in such exercises:

 $ perl -MO=Deparse 31191808.pl sub foo { die 'Missing bar', my(@items) = () unless my $bar = shift @_; push @items, $bar; return @items; } 

which makes this option my $var if 0 trick / error / curiosity. Its effect is to create a lexical but static variable that will not be reinitialized every time foo is called.

+5
source

You found comma-operator

From perldoc perlop:

Binary "," is a comma operator. In a scalar context, it evaluates the left argument, discards that value, then evaluates its right argument and returns that value.

So, this is actually considered one statement:

 my $bar = shift or die "Missing bar", my @items = (); 

Perl evaluates the LHS and discards the result, since this assignment, which does not actually cancel anything, is still assigned $bar , then it evaluates the RHS and returns that value. It is important to note that this means that @items initialized as a static lexical variable in your sub-populator, but remains static in all foo() calls. Similar to how state works.

At this point in the routine, you assigned 1 to $bar . Next line:

 push @items, $bar; 

Perl clicks $bar on the static lexical variable @items . The following statement returns a list of one item 1 .

Subsequent calls to foo continue to add elements to the @items array and return these elements. This is why you see more than 1 from subroutine calls.

+3
source

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, by conditionally declaring a variable, it assigns a value when this condition is true . If you changed if 0 to if $_[0] == 3 , you would get a completely different sequence of numbers. This is actually an old bug in Perl that can no longer be fixed because a lot of code depends on it, but if you are lucky you can see this warning: "Deprecated use of my () in a false conditional"

+3
source

All Articles