Detect recursion using state variables
As you might know Perl version 5.10 has introduce a keyword called state that allows the creation of so-called state variables. These are very similar to the static variables of C. You declare them insied a function. They are scoped to the function so you cannot reach them from the outside, but they never get destroyed and they are initialized only once.
Dave Horsfall suggested to use this feature to detect unwanted recursion. Or calling the same function twice.
The idea is to add a small construct to every function:
confess 'recursion' if state $recursed++;
The code will look like this:
examples/detect_recursion.pl
use strict; use warnings; use 5.010; use Carp qw(confess); sub a { #say 'enter a'; confess 'recursion' if state $recursed++; b(); } sub b { #say 'enter b'; confess 'recursion' if state $recursed++; c(); } sub c { #say 'enter c'; confess 'recursion' if state $recursed++; a(); } a();
Then if we run the script we get the following exception:
recursion at examples/detect_recursion.pl line 9. main::a() called at detect_recursion.pl line 21 main::c() called at detect_recursion.pl line 15 main::b() called at detect_recursion.pl line 10 main::a() called at detect_recursion.pl line 25
That is, when a() called again deep down the call stack, the state variable already has a value and this Perl will call confess that will raise an exception with a nice stack-trace.
False alarms
While this idea can be used in some of the cases, in other it might generate false alarms.
In this case we call the same function twice. One after the other, but not in a recursion.
examples/detect_recursion_falsly.pl
use strict; use warnings; use 5.010; use Carp qw(confess); sub a { confess 'recursion' if state $recursed++; } a(); a();
recursion at detect_recursion_falsly.pl line 8. main::a() called at detect_recursion_falsly.pl line 12
Comments
If all of the exits from a function do a
$recursed--
then I *think* the false alarm goes away.
sub a { #say 'enter a'; confess 'recursion' if state $recursed++; b(); $recursed--; } sub b { #say 'enter b'; confess 'recursion' if state $recursed++; c(); $recursed--; }
sub c { #say 'enter c'; confess 'recursion' if state $recursed++; a(); $recursed--; }
Also, I thought that declaring and modifying the state variable in the
state
declaration was outlawed recently. Of was that just for
my
?
Published on 2021-04-16