| Attribute-Generator documentation | Contained in the Attribute-Generator distribution. |
Attribute::Generator - Python like generator powered by Coro
use Attribute::Generator;
sub fizzbuzz :Generator {
my($i, $end) = @_;
do {
yield (($i % 3 ? '':'Fizz').($i % 5 ? '':'Buzz') || $i)
} while $i++ < $end;
}
my $generator = fizzbuzz(1, 100);
while(defined (my $val = $generator->next())) {
print "$val\n";
}
while(<$generator>) {
print "$_\n";
}
Attribute::Generator realizes Python like generators using the power of Coro
module. This module provides :Generator CODE attribute which declares
generator subroutines, and exports yield function which is like yield in
Python.
This CODE attribute declares generator. When generator subroutines are called, it returns an iterator object that has next() method.
Advances generator until next yield called.
Send a value to the generator. In generator subroutine, sent value can be received as return value of yield(): e.g.
sub foo:Generator {
my $i = 0;
while() {
if(defined yield $i++) {
$i=0;
}
}
}
This generator, yields 0, 1, 2, 3.. , can be reset by calling $gen->send(1).
Returns the generator itself.
Note: Unlike Python, send() does *NOT* advances iterator.
When you call yield in generator, current status of the generator are frozen and EXPR is returned to the caller of $generator->next().
Note that calling yield() outside of :Generator subroutines are strictly prohibited.
Rintaro Ishizaki <rintaro@cpan.org>
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
| Attribute-Generator documentation | Contained in the Attribute-Generator distribution. |
package Attribute::Generator; use strict; use warnings; our $VERSION = '0.02'; use Attribute::Handlers; use Coro::State 4.91; use base qw(Exporter); our @EXPORT = qw(yield); sub UNIVERSAL::Generator : ATTR(CODE) { my($package, $symbol, $refent) = @_; no warnings 'redefine'; *{$symbol} = sub { new Attribute::Generator::State $refent, @_ }; } our @stack = (Coro::State->new()); # Generator stack; sub yield { $stack[-1]{_sent} = \@_; pop(@stack)->transfer($stack[-1]); my $sent = delete $stack[-1]{_sent} or return; # from send() wantarray ? @$sent : $sent->[0]; } { package Attribute::Generator::State; use base qw(Coro::State); use overload ( '@{}' => '__list__', '<>' => 'next', ); sub _run_generator { eval { &{+shift}; #execute the code }; $stack[-2]->throw($@) if $@; while() { pop(@stack)->transfer($stack[-1]); delete $stack[-1]{_sent}; # clear send()ed. } } sub new { shift->SUPER::new(\&_run_generator, @_) } sub next { my($self) = @_; push @stack, $self; $stack[-2]->transfer($self); # resume my $ret = delete $self->{_sent} or return; wantarray ? @$ret : $ret->[0]; } sub send { shift->{_sent} = \@_; } sub __list__ { my($self) = @_; my @ret; while(my @tmp = $self->next) { push @ret, @tmp; } \@ret; } } 1; __END__