What is exact function/purpose of * in front of _fact and how it can be equivalently written?
sub fact {
my ($n) = @_;
local *_fact = sub {
my ($n, $prod) = @_;
return $prod if $n == 0;
return _fact($n-1, $n*$prod);
};
return _fact($n, 1);
}
fact($n);
Ideally, the author of the function would have liked to use
sub fact {
my ($n) = @_;
my $_fact; $_fact = sub {
my ($n, $prod) = @_;
return $prod if $n == 0;
return $_fact->($n-1, $n*$prod);
};
return $_fact->($n, 1);
}
Unfortunately, that has a memory leak. The anon sub has a reference to $_fact
, which holds a reference to the anonymous sub. $_fact
would need to be cleared to break the reference on exit.
sub fact {
my ($n) = @_;
my $_fact;
$_fact = sub {
my ($n, $prod) = @_;
return $prod if $n == 0;
return $_fact->($n-1, $n*$prod);
};
my $rv;
my $e = eval { $rv = $_fact->($n, 1); 1 } ? undef : ($@ || 'Unknown');
$_fact = undef;
die $e if $e
return $rv;
}
But that's UGLY! One way to avoid the problem is using a Y combinator. A much simpler way to avoid the problem is to store the code reference in a package variable instead of a lexical variable (since only lexical variables are captured by subs). This is what the code you posted does. Keep in mind that
*_fact = sub { ... };
is basically a run-time version of
sub _fact { ... }
Both assign the sub to CODE slot of symbol _fact
.
That said, 5.16 introduced a better fix:
use feature qw( current_sub );
sub fact {
my ($n) = @_;
my $_fact = sub {
my ($n, $prod) = @_;
return $prod if $n == 0;
return __SUB__->($n-1, $n*$prod);
};
return $_fact->($n, 1);
}
Check typeglob aliases
Example above should be written using anonymous subroutine/closure:
sub fact {
my ($n) = @_;
my $_fact;
$_fact = sub {
my ($n, $prod) = @_;
return $prod if $n == 0;
return __SUB__->($n-1, $n*$prod);
};
return $_fact->($n, 1);
}
It appears that this is a funky attempt at creating a closure by assigning a code reference to the typeglob named _fact
and then calling it pseudo-recursively. (note: a typeglob is the container for all variables with a particular name).
A virtually equivalent (and much more standard) way to write this would be:
sub fact {
my ($n) = @_;
my $_fact;
$fact = sub { .... }; # Assigning code-ref to scalar variable.
return $_fact->($n, 1); # Note the arrow syntax to deref the code-ref
}
...but, as was kindly pointed out, that has a memory leak in it... so, I say just dump the closure altogether and write it like so:
sub fact {
my($n,$prod) = @_;
return((defined $prod) ? (($n == 0) ? $prod : fact($n-1, $n * $prod)) : fact($n,1));
}
(remember, the only thing worse than infinite recursion is... infinite recursion)
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