File Coverage

blib/lib/Array/Iterator.pm
Criterion Covered Total %
statement 75 77 97.4
branch 42 44 95.4
condition 4 6 66.6
subroutine 20 21 95.2
pod 8 13 61.5
total 149 161 92.5


line stmt bran cond sub pod time code
1             package Array::Iterator;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2021-08-09'; # DATE
5             our $DIST = 'Array-Iterator'; # DIST
6             our $VERSION = '0.130'; # VERSION
7              
8 5     5   148545 use strict;
  5         26  
  5         150  
9 5     5   25 use warnings;
  5         10  
  5         5439  
10              
11             ### constructor
12              
13             sub new {
14 17     17 1 5279 my ($_class, @array) = @_;
15 17 100       69 (@array)
16             || die "Insufficient Arguments : you must provide something to iterate over";
17 16   33     80 my $class = ref($_class) || $_class;
18 16         25 my $_array;
19 16 100       46 if (scalar @array == 1) {
20 7 100       31 if (ref $array[0] eq "ARRAY") {
    50          
21 3         7 $_array = $array[0];
22             }
23             elsif (ref $array[0] eq "HASH") {
24             die "Incorrect type : HASH reference must contain the key __array__"
25 4 100       29 unless exists $array[0]->{__array__};
26             die "Incorrect type : __array__ value must be an ARRAY reference"
27 2 100       19 unless ref $array[0]->{__array__} eq 'ARRAY';
28 1         3 $_array = $array[0]->{__array__};
29             }
30             }
31             else {
32 9         30 $_array = \@array;
33             }
34 13         55 my $iterator = {
35             _current_index => 0,
36             _length => 0,
37             _iteratee => [],
38             _iterated => 0,
39             };
40 13         28 bless($iterator, $class);
41 13         20 $iterator->_init(scalar(@{$_array}), $_array);
  13         54  
42 13         42 return $iterator;
43             }
44              
45             ### methods
46              
47             # private methods
48              
49             sub _init {
50 15     15   739 my ($self, $length, $iteratee) = @_;
51 15 100 100     87 (defined($length) && defined($iteratee))
52             || die "Insufficient Arguments : you must provide an length and an iteratee";
53 13         34 $self->{_current_index} = 0;
54 13         23 $self->{_length} = $length;
55 13         79 $self->{_iteratee} = $iteratee;
56             }
57              
58             # protected method
59              
60             # this can be used in a subclass to access the value
61              
62             # we need to alter this so its an lvalue
63             sub _current_index : lvalue {
64 110 100   110   654 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
65             || die "Illegal Operation : This method can only be called by a subclass";
66             $_[0]->{_current_index}
67 109         1074 }
68              
69             # this we should never need to alter
70             # so we dont make it a lvalue
71             sub _iteratee {
72 42 100   42   564 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
73             || die "Illegal Operation : This method can only be called by a subclass";
74             $_[0]->{_iteratee}
75 41         294 }
76              
77             # we move this from a private method
78             # to a protected one, and check our access
79             # as well
80             sub _getItem {
81 136 100   136   848 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
82             || die "Illegal Operation : This method can only be called by a subclass";
83 135         859 my ($self, $iteratee, $index) = @_;
84 135         492 return $iteratee->[$index];
85             }
86              
87 0     0   0 sub _get_item { my $self = shift; $self->_getItem(@_) }
  0         0  
88              
89             # we need to alter this so its an lvalue
90             sub _iterated : lvalue {
91 42 50   42   123 (UNIVERSAL::isa((caller)[0], __PACKAGE__))
92             || die "Illegal Operation : This method can only be called by a subclass";
93             $_[0]->{_iterated}
94 42         304 }
95              
96             # public methods
97              
98             # this defines the interface
99             # an iterator object will have
100              
101             sub iterated {
102 2     2 0 5685 my ($self) = @_;
103 2         11 return $self->{_iterated};
104             }
105              
106             sub has_next {
107 57     57 1 480 my ($self, $n) = @_;
108              
109 57 100       136 if(not defined $n) { $n = 1 }
  49 100       72  
    100          
110 1         9 elsif(not $n) { die "has_next(0) doesn't make sense, did you mean current()?" }
111 1         10 elsif($n < 0) { die "has_next() with negative argument doesn't make sense, perhaps you should use a BiDirectional iterator" }
112              
113 55         98 my $idx = $self->{_current_index} + ($n - 1);
114              
115 55 100       180 return ($idx < $self->{_length}) ? 1 : 0;
116             }
117              
118 47     47 0 7102 sub hasNext { my $self = shift; $self->has_next(@_) }
  47         116  
119              
120             sub next {
121 37     37 1 5220 my ($self) = @_;
122             ($self->{_current_index} < $self->{_length})
123 37 100       97 || die "Out Of Bounds : no more elements";
124 36         52 $self->{_iterated} = 1;
125 36         83 return $self->_getItem($self->{_iteratee}, $self->{_current_index}++);
126             }
127              
128             sub get_next {
129 20     20 1 37 my ($self) = @_;
130 20         29 $self->{_iterated} = 1;
131 20 100       93 return undef unless ($self->{_current_index} < $self->{_length});
132 15         42 return $self->_getItem($self->{_iteratee}, $self->{_current_index}++);
133             }
134              
135 46     46 0 7659 sub getNext { my $self = shift; $self->get_next(@_) }
  46         96  
136              
137             sub peek {
138 31     31 1 777 my ($self, $n) = @_;
139              
140 31 100       92 if(not defined $n) { $n = 1 }
  22 100       34  
    100          
141 1         9 elsif(not $n) { die "peek(0) doesn't make sense, did you mean get_next()?" }
142 1         9 elsif($n < 0) { die "peek() with negative argument doesn't make sense, perhaps you should use a BiDirectional iterator" }
143              
144 29         57 my $idx = $self->{_current_index} + ($n - 1);
145              
146 29 100       82 return undef unless ($idx < $self->{_length});
147 24         54 return $self->_getItem($self->{_iteratee}, $idx);
148             }
149              
150             sub current {
151 19     19 1 37 my ($self) = @_;
152 19         50 return $self->_getItem($self->{_iteratee}, $self->currentIndex());
153             }
154              
155             sub current_index {
156 57     57 1 102 my ($self) = @_;
157 57 100       214 return ($self->{_current_index} != 0) ? $self->{_current_index} - 1 : 0;
158             }
159              
160 57     57 0 4331 sub currentIndex { my $self = shift; $self->current_index(@_) }
  57         108  
161              
162             sub get_length {
163 32     32 1 54 my ($self) = @_;
164 32         96 return $self->{_length};
165             }
166              
167 32     32 0 106 sub getLength { my $self = shift; $self->get_length(@_) }
  32         53  
168              
169             1;
170             # ABSTRACT: A simple class for iterating over Perl arrays
171              
172             __END__