FAST

 view release on metacpan or  search on metacpan

lib/FAST/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] : \FAST::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 'FAST::List::Gen::Thunk' ? \$$_->() : \$_} $code->(); 1}
                      or catch_done and do {
                        if (ref $@) {
                          push @list, map {ref eq 'FAST::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..$#_] : \FAST::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 'FAST::List::Gen::Thunk' ? \$$_->() : \$_} $code->(); 1}
                        or catch_done and do {
                            if (ref $@) {
                                push @last, map {ref eq 'FAST::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..$#_] : \FAST::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.812 second using v1.01-cache-2.11-cpan-71847e10f99 )