Group
Extension

Module-Generic/lib/Module/Generic/Iterator.pod

=encoding utf8

=head1 NAME

Module::Generic::Iterator - An Array Iterator Object Class

=head1 SYNOPSIS

    my $i = Module::Generic::Iterator->new( [qw( Joe John Mary )] );
    # or also:
    my $a = Module::Generic::Array->new( [qw( Joe John Mary )] );
    my $i = $a->iterator;
    while( $i->has_next )
    {
        my $elem = $i->next;
        my $value = $elem->value;
        # Get the next element relative to our element
        printf( "Next value is: %s at offset %d\n", $elem->next, $elem->next->pos  );
    }

    # Navigate to a specific element
    my $elem = $iter->find( "Jack" );
    print( "Found Jack at position: ", $elem->pos, "\n" );

    # Use element navigation
    my $first = $iter->first;
    my $next = $first->next; # Returns "Jack" as an element
    print( "Next after John: ", $next->value, "\n" );

=head1 VERSION

    v1.2.3

=head1 DESCRIPTION

L<Module::Generic::Iterator> provides a generic iterator interface for traversing a list of elements. It supports navigation through the list with methods like L</next>, L</prev>, L</first>, and L</last>, and allows finding specific elements with L</find>. Each element in the iterator is wrapped in a L<Module::Generic::Iterator::Element> object, which provides contextual navigation (e.g., L</next>, L</prev>) relative to the parent iterator.

This class is designed to be lightweight and flexible, working with any array of values, including scalars, references, or objects.

=head1 CONSTRUCTORS

=head2 new

Creates a new iterator object. It takes an optional array reference or an L<Module::Generic::Array> object of elements to iterate over, and an optional hash or hash reference of parameters, and returns the newly instantiated object:

    my $iter = Module::Generic::Iterator->new( [1, 2, 3], debug => 3 );

Supported parameters:

=over 4

=item * C<debug>

An integer to enable debugging. See L<Module::Generic/debug>.

=back

Returns a new L<Module::Generic::Iterator> object.

=head1 METHODS

=head2 elements

Returns the underlying array of elements as a L<Module::Generic::Array> object. Each element is a L<Module::Generic::Iterator::Element> object:

    my $elems = $iter->elements;
    print( $elems->length, "\n" ); # Number of elements

=head2 eof

Returns true if the iterator is at the end of the list (i.e., the current position is the last element or beyond). Optionally takes an element to check its position:

    if( $iter->eof )
    {
        print( "End of iterator\n" );
    }

=head2 find

Finds an element by value and returns its corresponding L<Module::Generic::Iterator::Element> object. Returns C<undef> in scalar context, or an empty list in list context if the element is not found:

    my $elem = $iter->find( "Jack" );
    print( $elem->value, "\n" ) if( $elem ); # "Jack"

For reference values, comparison is done using L<Scalar::Util/refaddr>. For scalar values, comparison is done using string equality (C<eq>).

=head2 first

Moves the iterator to the first element and returns it as a L<Module::Generic::Iterator::Element> object:

    my $first = $iter->first;
    print( $first->value, "\n" ); # First element

=head2 has_next

Returns true if there is a next element in the iterator:

    while( $iter->has_next )
    {
        my $elem = $iter->next;
        print( $elem->value, "\n" );
    }

=head2 has_prev

Returns true if there is a previous element in the iterator:

    $iter->last;
    if( $iter->has_prev )
    {
        my $prev = $iter->prev;
        print( $prev->value, "\n" );
    }

=head2 last

Moves the iterator to the last element and returns it as a L<Module::Generic::Iterator::Element> object:

    my $last = $iter->last;
    print( $last->value, "\n" ); # Last element

=head2 length

Returns the number of elements, starting from 1, as a L<Module::Generic::Number> object.

    print( $iter->length, "\n" ); # e.g., 5

=head2 next

Moves the iterator to the next element and returns it as a L<Module::Generic::Iterator::Element> object. Optionally takes an element to start from:

    while( my $elem = $iter->next )
    {
        print( $elem->value, "\n" );
    }

Returns C<undef> in scalar context, or an empty list in list context, if there are no more elements.

=head2 pos

An lvalue method to get or set the current position in the iterator. Returns the position as an integer:

    $iter->pos = 2; # Move to the third element
    my $pos = $iter->pos; # Returns the current position
    print( $iter->pos, "\n" ); # 2

Warns if the position is not an integer.


=head2 prev

Moves the iterator to the previous element and returns it as a L<Module::Generic::Iterator::Element> object. Optionally takes an element to start from:

    $iter->last;
    my $prev = $iter->prev;
    print( $prev->value, "\n" );

Returns C<undef> in scalar context, or an empty list in list context, if there are no previous elements.

=head2 reset

Resets the iterator position to the beginning (position 0):

    $iter->last;
    $iter->reset;
    print( $iter->pos, "\n" ); # 0

Returns the iterator object.

=head2 _find_pos

Provided with an item, this returns its position in the array or undef if it is not in the array.

=head1 SERIALISATION

=for Pod::Coverage FREEZE

=for Pod::Coverage STORABLE_freeze

=for Pod::Coverage STORABLE_thaw

=for Pod::Coverage THAW

=for Pod::Coverage TO_JSON

Serialisation by L<CBOR|CBOR::XS>, L<Sereal> and L<Storable::Improved> (or the legacy L<Storable>) is supported by this package. To that effect, the following subroutines are implemented: C<FREEZE>, C<THAW>, C<STORABLE_freeze> and C<STORABLE_thaw>

=head1 THREAD-SAFETY

L<Module::Generic::Iterator> is thread-safe for all operations, as it operates on per-object state and does not modify shared resources at runtime.

Key considerations for thread-safety:

=over 4

=item * B<Shared Variables>

There are no shared variables that are modified at runtime. The global C<$DEBUG> variable (inherited from L<Module::Generic>) is typically set before threads are created, and it is the user's responsibility to ensure thread-safety if modified at runtime:

    use threads;
    local $Module::Generic::Iterator::DEBUG = 0; # Set before threads
    my @threads = map
    {
        threads->create(sub
        {
            my $iter = Module::Generic::Iterator->new( [1, 2, 3] );
            $iter->next; # Thread-safe
        });
    } 1..5;
    $_->join for( @threads );

=item * B<Object State>

Iterator data (e.g., L</elements>, L</pos>, L<Module::Generic::Iterator::Element/value>) is stored per-object, ensuring thread isolation:

    use threads;
    my @threads = map
    {
        threads->create(sub
        {
            my $iter = Module::Generic::Iterator->new( [1, 2, 3] );
            while( my $elem = $iter->next )
            {
                print( $elem->value, "\n" ); # Thread-safe
            }
        });
    } 1..5;
    $_->join for( @threads );

=item * B<Serialisation>

Serialisation methods (L</FREEZE>, L</THAW>) operate on per-object state, making them thread-safe.

=back

For debugging in threaded environments (depending on your Operating System):

    ls -l /proc/$$/fd  # List open file descriptors

=head1 SEE ALSO

L<Module::Generic::Iterator::Element>, L<Module::Generic::Array>

=head1 AUTHOR

Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>

=head1 COPYRIGHT & LICENSE

Copyright (c) 2000-2024 DEGUEST Pte. Ltd.

You can use, copy, modify and redistribute this package and associated
files under the same terms as Perl itself.

=cut



Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.