List-Gen
view release on metacpan or search on metacpan
lib/List/Gen.pm view on Meta::CPAN
$pos++, return $$last if $i == 0 and $from;
local *_;
while ($i >= $pos and $pos < $size) {
*_ = $from ? $last
: $source ? \$source->(undef, $pos)
: \(0+$pos);
$last = \$code->();
$pos++;
}
$pos < $size ? $$last : ()
},
fsize => sub {$size},
index => sub {\$pos},
from => sub {
croak "can not call ->from on started iterator"
if $pos or $from++;
$last = @_ > 1 ? \$_[1] : \List::Gen::Iterate::Default->new;
},
} => $class
},
purge => sub {croak 'can not purge iterative generator'};
=item iterate_multi C< {CODE} [LIMIT] >
the same as C<iterate>, except CODE can return a list of any size. inside CODE,
C<$_> is set to the position in the returned generator where the block's
returned list will be placed.
the returned generator from C< iterate_multi > can be modified with C<push>,
C<pop>, C<shift>, C<unshift>, and C<splice> like a normal array. it is up to
you to ensure that the iterative algorithm will still work after modifying the
array.
the C<< ->from(...) >> method can be called on the returned generator. see
C< iterate > for the rules and effects of this.
=cut
sub iterate_multi (&;$) {
goto &iterate_multi_stream if $STREAM;
tiegen Iterate_Multi => @_, 9**9**9
} BEGIN {*iterateM = *iterate_multi}
mutable_gen Iterate_Multi => sub {
my ($class, $code, $size) = @_;
my ($iter, $when_done ) = (0, sub {});
my ($from, @list, @tails, $source, $mutable);
if (isagen $size) {
my $src = tied @$size;
$source = $src->can('FETCH');
$size = $src->fsize;
$mutable = $src->mutable;
$src->tail_size($size) if $mutable;
}
curse {
FETCH => sub {
my $i = $_[1];
while ($i > $#list) {
$iter++ >= $size
and croak "too many iterations requested: ".
"$iter. index $i out of bounds [0 .. @{[$size - 1]}]";
local *_ = $from ? $list[-1] :
$source ? \$source->(undef, scalar @list) :
\scalar @list;
eval {push @list, map {ref eq 'List::Gen::Thunk' ? \$$_->() : \$_} $code->(); 1}
or catch_done and do {
if (ref $@) {
push @list, map {ref eq 'List::Gen::Thunk' ? \$$_->() : \$_} @{$@};
$size = @list;
$$_ = $size for @tails;
$when_done->();
return ${$list[$i < $#list ? $i : $#list]};
} else {
$iter--;
$size = @list;
$$_ = $size for @tails;
$when_done->();
return
}
}
}
if ($size < @list) {
$size = @list;
$$_ = $size for @tails;
}
elsif ($mutable) {
$$_ = $size for @tails;
}
${$list[$i]}
},
fsize => sub {$size},
cached => sub {\@list},
set_size => sub {
$size = int $_[1];
$$_ = $size for @tails;
$when_done->() if $size == @list
},
_resize => sub {
$size += $_[1] if $size < 9**9**9;
$$_ = $size for @tails;
$iter += $_[1];
},
_when_done => sub :lvalue {$when_done},
from => sub {
croak "can not call ->from on started iterator"
if @list or $from++;
push @list, @_ > 1 ? \@_[1..$#_] : \List::Gen::Iterate::Default->new;
},
tail_size => sub {
push @tails, \$_[1]; weaken $tails[-1];
},
} => $class
},
purge => sub {Carp::croak 'can not purge iterative generator'},
load => sub {push @{$_[0]->cached}, \@_[1..$#_]},
PUSH => sub {
my $self = shift;
$self->_resize(0+@_);
push @{$self->cached}, \(@_)
},
UNSHIFT => sub {
my $self = shift;
$self->_resize(0+@_);
unshift @{$self->cached}, \(@_)
},
POP => sub {
my $self = shift;
return unless $self->fsize > 0;
$self->_resize(-1);
${pop @{$self->cached}}
},
SHIFT => sub {
my $self = shift;
return unless $self->fsize > 0;
$self->_resize(-1);
${shift @{$self->cached}}
},
SPLICE => sub {
my $self = shift;
my $list = $self->cached;
my $size = $self->fsize;
my @ret =
@_ == 0 ? splice @$list :
@_ == 1 ? splice @$list, shift :
@_ == 2 ? splice @$list, shift, shift :
splice @$list, shift, shift, \(@_) ;
$self->_resize(@$list - $size);
map {$$_} @ret
};
=item iterate_multi_stream C< {CODE} [LIMIT] >
C< iterate_multi_stream > is a version of C< iterate_multi > that does not cache
the generated values. because of this, access to the returned generator must be
monotonically increasing (such as repeated calls to C<< $gen->next >>).
keyword modification of a stream iterator (with C<push>, C<shift>, ...) is not
supported.
=cut
sub iterate_multi_stream (&;$) {
tiegen Iterate_Multi_Stream => @_, 9**9**9
}
BEGIN {*iterateMS = *iterate_multi_stream}
mutable_gen Iterate_Multi_Stream => sub {
my ($class, $code, $size) = @_;
my ($pos, $when_done ) = (0, sub {});
my ($from, @last, @tails, $source, $mutable);
if (isagen $size) {
$source = tied(@$size)->can('FETCH');
$mutable = $size->is_mutable;
$size = $size->size;
}
curse {
FETCH => sub {
my $i = $_[1];
$i < $pos and croak "non-monotone access of iterate multi stream, idx($i) < pos($pos)";
while ($i >= $pos) {
$pos >= $size and croak "too many iterations requested: ".
"$pos. index $i out of bounds [0 .. @{[$size - 1]}]";
if ($i == $pos and @last) {
$pos++;
last
}
if (@last) {
shift @last;
$pos++;
next;
}
local *_ = $from ? $from :
$source ? \$source->(undef, $pos) :
\$pos;
eval {push @last, map {ref eq 'List::Gen::Thunk' ? \$$_->() : \$_} $code->(); 1}
or catch_done and do {
if (ref $@) {
push @last, map {ref eq 'List::Gen::Thunk' ? \$$_->() : \$_} @{$@};
$size = $pos;
$$_ = $size for @tails;
$when_done->();
return ${shift @last};
} else {
$size = $pos;
$$_ = $size for @tails;
$when_done->();
return
}
};
$from = $last[-1] if $from;
$pos++
}
if ($mutable) {
$$_ = $size for @tails
}
${shift @last};
},
fsize => sub {$size},
index => sub {\$pos},
set_size => sub {
$size = int $_[1];
$$_ = $size for @tails;
$when_done->();
},
_when_done => sub :lvalue {$when_done},
from => sub {
croak "can not call ->from on started iterator"
if @last or $from;
push @last, @_ > 1 ? \@_[1..$#_] : \List::Gen::Iterate::Default->new;
$from = $last[-1];
},
tail_size => sub {
push @tails, \$_[1]; weaken $tails[-1];
},
} => $class
},
purge => sub {Carp::croak 'can not purge iterative generator'};
=item gather C< {CODE} [LIMIT] >
( run in 0.756 second using v1.01-cache-2.11-cpan-71847e10f99 )