FunctionalPerl
view release on metacpan or search on metacpan
intro/more_tailcalls view on Meta::CPAN
# main> opt_even 500000
# $VAR1 = 1;
# Now it runs with little (and constant) memory usage.
# ------------------------------------------------------------------
# Note: there's also trampolining as a potential solution:
use FP::Trampoline;
use Chj::time_this;
func tramp_odd($n) {
if ($n == 0) {
0
} else {
T { tramp_even($n - 1) }
}
}
func tramp_even($n) {
if ($n == 0) {
1
} else {
T { tramp_odd($n - 1) }
}
}
TEST {
time_this { trampoline tramp_even 60000 } "T"
}
1;
# or
func tramp2_odd($n) {
if ($n == 0) {
0
} else {
TC \&tramp2_even, $n - 1
}
}
func tramp2_even($n) {
if ($n == 0) {
1
} else {
TC \&tramp2_odd, $n - 1
}
}
TEST {
time_this { trampoline tramp2_even 60000 } "TC"
}
1;
# ------------------------------------------------------------------
# Also note: all of the above example are defining functions as
# package globals, which makes it trivial to call themselves
# recursively. If you need to define them as lexicals, then you need
# to heed the advice given in [[README]] with regards to self
# calls (recursive function definitions), either by way of weaken:
use Scalar::Util 'weaken';
use FP::Stream 'Weakened'; # XX should probably move to non-lazyness
# related place
func weakened_even($n) {
my ($odd, $even);
$odd = func($n) {
if ($n == 0) {
0
} else {
tail &$even($n - 1)
}
};
$even = func($n) {
if ($n == 0) {
1
} else {
tail &$odd($n - 1)
}
};
# do *not* make this a tail call or $even will become undef on
# bleadperl at some point (not so on v5.14.2):
# (XXX Perl issue, or what are the rules here?)
Weakened($even)->($n)
}
TEST {
($^V->{version}[1] > 20)
? weakened_even 60000
: warn "skipping test on older perl"
}
1;
# XXX this actually fails both on v5.14.2 and bleadperl for undefined
# subroutine call. Submit bug report?
# or by using the n-ary fixpoint combinator:
use FP::fix;
func fix_even($n) {
my ($odd, $even) = fixn(
func($odd, $even, $n)
{
if ($n == 0) {
0
} else {
tail &$even($n - 1)
}
},
func($odd, $even, $n)
{
if ($n == 0) {
1
} else {
tail &$odd($n - 1)
}
}
);
tail &$even($n)
}
TEST { fix_even 60000 }
1;
# ------------------------------------------------------------------
# run tests if called as part of the test suite, or
# enter the repl for your experiments, see (0) in `basics`
perhaps_run_tests "main" or do {
require FP::Repl;
FP::Repl::repl();
};
( run in 1.127 second using v1.01-cache-2.11-cpan-6aa56a78535 )