FAST
view release on metacpan or search on metacpan
lib/FAST/List/Gen/Cookbook.pm view on Meta::CPAN
($x, $y) = ($y, take($x) + $y)
}
}
say for @{fibonacci 15};
=head2 variable length generators
to implement C< grep > (as C< filter >) or C< while > (as C< While >) on a
generator means that the generator no longer knows its exact size at all times.
care has been taken to make sure that this doesn't bite you too much.
my $pow = While {$_ < 20} gen {$_**2};
say for @$pow; # checks size on every iteration, works fine
say while <$pow>; # also works
say $pow->all; # ok too
each prints:
0
1
4
9
16
but, if instead of C< say for @$pow > you had written C< map {say} @$pow >, perl
will try to expand C< @$pow > in list context, and it will not know when to
stop, since it only checks at the beginning. the solution, in short, is to only
dereference variable length generators in slice C< @$gen[0 .. 10] > or iterator
C< ... for @$gen; > context, and never in list context.
in general, it makes more sense (and is faster) to build your constraint into
the calling code:
my $pow = gen {$_**2};
for (@$gen) {
last if $_ > 20;
say;
}
=head2 recursive generators
the fibonacci sequence can be generated from the following definition:
f[0] = 0;
f[1] = 1;
f[n] = f[n-1] + f[n-2];
here are a few ways to write that definition as a generator:
my $fib; $fib = cache gen {$_ < 2 ? $_ : $$fib[$_ - 1] + $$fib[$_ - 2]};
my $fib = gen {$_ < 2 ? $_ : self($_ - 1) + self($_ - 2)}
->cache
->recursive;
my $fib; $fib = gen {$fib->($_ - 1) + $fib->($_ - 2)}
->overlay( 0 => 0, 1 => 1 )
->cache;
my $fib; $fib = gen {$$fib[$_ - 1] + $$fib[$_ - 2]}->cache->overlay;
@$fib[0, 1] = (0, 1);
bringing all those techniques together:
my $fib = gen {self($_ - 1) + self($_ - 2)}
->overlay( 0 => 0, 1 => 1 )
->cache
->recursive;
the C< cache > function is used in each example because the recursive definition
of the fibonacci sequence would generate an exponentially increasing number of
calls to itself as the list grows longer. C< cache > prevents any index from
being calculated more than once.
=head3 more ways to write the fibonacci sequence
my $fib = <0, 1, *+*...>; >>
my $fib = <0, 1, {$^a + $^b}...>; >>
my $fib = ([0, 1] + iterate {sum self($_, $_ + 1)})->rec; >>
my $fib = ([0, 1] + iterate {sum fib($_, $_ + 1)})->rec('fib'); >>
my $fib = (iterate {$_ < 2 ? $_ : sum self($_ - 1, $_ - 2)})->rec; >>
my $fib; $fib = cache gen {$_ < 2 ? $_ : sum $fib->($_ - 1, $_ - 2)}; >>
=head3 a few ways to write the factorial sequence
my $fac = <[*..] 1, 1..>;
my $fac = <1, 1..>->scan('*');
my $fac = 1 + repeat(1)->scan('+')->scan('*');
my $fac = <1, **_...>;
=head2 stream generators
here is an example of a sieve of eratosthenes implemented with generators:
my $primes = do {
my $src = <2..>;
iterate {
my ($x, $xs) = $src->x_xs;
$src = $xs->grep_stream(sub {$_ % $x});
$x
}
};
in this example, the list is filtered with C< grep_stream/filter_stream > since
the algorithm only reads the source once, and reads it in order. a regular
C< filter/grep > call could be used, but it would unnecessarily use up a lot
of memory since each call would have to build up a new random-access cache.
the inefficiency addressed above could also be fixed by modifying the filtering
function itself:
my $primes = do {
my @p;
<2..>->grep(sub {
my $i = $_;
$i % $_ or return for @p;
push @p, $i;
})
( run in 4.384 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )