Array-Iterator
view release on metacpan or search on metacpan
lib/Array/Iterator.pm view on Meta::CPAN
my $i = Array::Iterator->new({ __array__ => \@array });
=cut
sub new {
my ($_class, @array) = @_;
(@array) || die 'Insufficient Arguments: you must provide something to iterate over';
my $class = ref($_class) || $_class;
my $_array;
if (scalar @array == 1) {
if (ref $array[0] eq 'ARRAY') {
$_array = $array[0];
} elsif (ref $array[0] eq 'HASH') {
die 'Incorrect type: HASH reference must contain the key __array__'
unless exists $array[0]->{__array__};
die 'Incorrect type: __array__ value must be an ARRAY reference'
unless ref $array[0]->{__array__} eq 'ARRAY';
$_array = $array[0]->{__array__};
} else {
# One element array
$_array = \@array;
}
} else {
$_array = \@array;
}
my $iterator = {
_current_index => 0,
_length => 0,
_iteratee => [],
_iterated => 0, # -1 when going backwards, +1 when going forwards
};
bless($iterator, $class);
return $iterator->_init(scalar(@{$_array}), $_array);
}
sub _init {
my ($self, $length, $iteratee) = @_;
(defined($length) && defined($iteratee)) || die 'Insufficient Arguments: you must provide an length and an iteratee';
$self->{_current_index} = 0;
$self->{_length} = $length;
# $self->{_iteratee} = $iteratee;
# Store a private copy to prevent modifications
$self->{_iteratee} = [@{$iteratee}];
return $self;
}
=head2 _current_index
An lvalue-ed subroutine that allows access to the iterator's internal pointer.
This can be used in a subclass to access the value.
=cut
# We need to alter this so it's an lvalue
sub _current_index : lvalue {
(UNIVERSAL::isa((caller)[0], __PACKAGE__))
|| die 'Illegal Operation: This method can only be called by a subclass';
$_[0]->{_current_index}
}
=head2 _iteratee
This returns the item being iterated over, in our case an array.
=cut
# This we should never need to alter so we don't make it a lvalue
sub _iteratee {
(UNIVERSAL::isa((caller)[0], __PACKAGE__))
|| die 'Illegal Operation: This method can only be called by a subclass';
$_[0]->{_iteratee}
}
# we move this from a private method
# to a protected one, and check our access
# as well
sub _getItem {
(UNIVERSAL::isa((caller)[0], __PACKAGE__)) || die 'Illegal Operation: This method can only be called by a subclass';
my ($self, $iteratee, $index) = @_;
return $iteratee->[$index];
}
=head2 _get_item ($iteratee, $index)
This method is used by all other routines to access items. Given the iteratee
and an index, it will return the item being stored in the C<$iteratee> at the index
of C<$index>.
=cut
sub _get_item { my $self = shift; $self->_getItem(@_) }
# we need to alter this so it's an lvalue
sub _iterated : lvalue {
(UNIVERSAL::isa((caller)[0], __PACKAGE__))
|| die 'Illegal Operation: This method can only be called by a subclass';
$_[0]->{_iterated}
}
=head2 iterated
Access to the _iterated status, for subclasses
=cut
sub iterated {
my $self = shift;
return $self->{_iterated};
}
=head2 has_next([$n])
This method returns a boolean. True (1) if there are still more elements in
the iterator, false (0) if there are not.
Takes an optional positive integer (E<gt> 0) that specifies the position you
want to check. This allows you to check if there an element at an arbitrary position.
Think of it as an ordinal number you want to check:
$i->has_next(2); # 2nd next element
$i->has_next(10); # 10th next element
Note that C<has_next(1)> is the same as C<has_next()>.
Throws an exception if C<$n> E<lt>= 0.
=cut
sub has_next {
my ($self, $n) = @_;
if(not defined $n) {
$n = 1
} elsif(not $n) {
die "has_next(0) doesn't make sense, did you mean current()?"
} elsif($n < 0) {
die "has_next() with negative argument doesn't make sense, perhaps you should use a BiDirectional iterator"
}
my $idx = $self->{_current_index} + ($n - 1);
return ($idx < $self->{_length}) ? 1 : 0;
}
=head2 hasNext
Alternative name for has_next
=cut
sub hasNext { my $self = shift; $self->has_next(@_) }
=head2 next
( run in 0.635 second using v1.01-cache-2.11-cpan-5b529ec07f3 )