File Coverage

blib/lib/Array/Each.pm
Criterion Covered Total %
statement 186 186 100.0
branch 56 56 100.0
condition 35 36 97.2
subroutine 41 41 100.0
pod 20 25 80.0
total 338 344 98.2


line stmt bran cond sub pod time code
1             package Array::Each;
2             $VERSION = 0.03;
3 8     8   275167 use strict;
  8         23  
  8         335  
4 8     8   44 use warnings;
  8         16  
  8         435  
5              
6             =head1 NAME
7              
8             Array::Each - iterate over one or more arrays, returning one or more
9             elements from each array followed by the array index.
10              
11             =head1 VERSION
12              
13             This document refers to version 0.03 of Array::Each,
14             released March 26, 2004.
15              
16             =head1 SYNOPSIS
17              
18             use Array::Each;
19              
20             # one array
21             my @x = qw( a b c d e );
22              
23             my $one = Array::Each->new( \@x );
24             while( my( $x, $i ) = $one->each() ) {
25             printf "%3d: %s\n", $i, $x;
26             }
27              
28             # multiple arrays
29             my @y = ( 1,2,3,4,5 );
30              
31             my $set = Array::Each->new( \@x, \@y );
32             while( my( $x, $y, $i ) = $set->each() ) {
33             printf "%3d: %s %s\n", $i, $x, $y;
34             }
35              
36             # groups of elements (note set=> parm syntax)
37             my @z = ( a=>1, b=>2, c=>3, d=>4, e=>5 );
38              
39             my $hash_like = Array::Each->new( set=>[\@z], group=>2 );
40             while( my( $key, $val ) = $hash_like->each() ) {
41             printf "%s => %s\n", $key, $val;
42             }
43              
44             =cut
45              
46             ### more POD follows the __END__
47              
48 8     8   44 use Carp;
  8         20  
  8         886  
49              
50             # object attributes subscript mappings
51 8     8   47 use constant EACH => 0; # sub ref to object's each()
  8         23  
  8         860  
52 8     8   42 use constant SET => 1; # ref to array of array_refs
  8         13  
  8         492  
53 8     8   41 use constant ITERATOR => 2; # integer, used as array index
  8         16  
  8         383  
54 8     8   58 use constant REWIND => 3; # integer, used as array index
  8         33  
  8         377  
55 8     8   40 use constant BOUND => 4; # boolean, default is true
  8         14  
  8         544  
56 8     8   43 use constant UNDEF => 5; # scalar value for non-existing elements
  8         20  
  8         418  
57 8     8   129 use constant STOP => 6; # integer, will compare to iterator
  8         14  
  8         513  
58 8     8   38 use constant GROUP => 7; # integer, number of elements per group
  8         13  
  8         360  
59 8     8   68 use constant COUNT => 8; # integer, returned instead of iterator
  8         32  
  8         393  
60 8     8   42 use constant USER => 9; # reserved for child classes(?)
  8         11  
  8         28346  
61              
62             # references => names mapping
63             my %_each_subs = (
64             \&each_default => '&Array::Each::each_default',
65             \&each_unbound => '&Array::Each::each_unbound',
66             \&each_group => '&Array::Each::each_group',
67             \&each_complete => '&Array::Each::each_complete',
68             );
69              
70             # mappings of named arguments
71             my %_index_for;
72             @_index_for{ qw( _each set iterator rewind bound undef stop group count user ) } =
73             ( EACH, SET, ITERATOR, REWIND, BOUND, UNDEF, STOP, GROUP, COUNT, USER );
74              
75             my @_default; # see also _set_each() for expectations about defaults
76             @_default[ EACH, SET, ITERATOR, REWIND, BOUND, UNDEF, STOP, GROUP, COUNT, USER ] =
77             ( undef, [[]], 0, 0, 1, undef, undef, undef, undef, undef );
78              
79             # attribute validations
80             my @_validate;
81             @_validate[ EACH, SET, ITERATOR, REWIND, BOUND, UNDEF, STOP, GROUP, COUNT, USER ] = (
82             sub{ !defined $_[0] or exists $_each_subs{$_[0]} or
83             croak "Invalid _each: '$_[0]'" },
84             sub{ UNIVERSAL::isa( $_[0], 'ARRAY' ) and
85             UNIVERSAL::isa( $_[0]->[0], 'ARRAY' ) or
86             croak "Invalid set: '$_[0]'" },
87             sub{ $_[0] =~ /^\d+$/ or
88             croak "Invalid iterator: '$_[0]'" },
89             sub{ $_[0] =~ /^\d+$/ or
90             croak "Invalid rewind: '$_[0]'" },
91             sub{ $_[0] =~ /[01]/ or
92             croak "Invalid bound: '$_[0]'" },
93             sub{ # Undef value can be anything
94             },
95             sub{ !defined $_[0] or $_[0] =~ /^\d+$/ or
96             croak "Invalid stop: '$_[0]'" },
97             sub{ !defined $_[0] or ($_[0] =~ /^\d+$/ and $_[0] > 0) or
98             croak "Invalid group: '$_[0]'" },
99             sub{ !defined $_[0] or $_[0] =~ /^\d+$/ or
100             croak "Invalid count: '$_[0]'" },
101             sub {}, # To be defined by child classes
102             );
103              
104             # constructor methods: new(), copy()
105             # Note, the code for new() is taken not quite in whole
106             # cloth from section 4.2 of "Object Oriented Perl"[1].
107             sub new {
108 159     159 1 83527 my $caller = shift;
109 159         247 my $caller_is_obj = ref $caller;
110 159   66     678 my $class = $caller_is_obj || $caller;
111 159         180 my %arg;
112              
113             ### if only array refs are passed to new() ...
114 159 100       323 if( ref $_[0] ) { $arg{ 'set' } = [@_] }
  6         19  
115 153         626 else { %arg = @_ }
116              
117 159         408 my $self = bless [], $class;
118 159         596 foreach my $member ( keys %_index_for ) {
119 1590         2015 my $index = $_index_for{ $member };
120 1590 100       4389 if ( exists $arg{ $member } ) {
    100          
121 633         1177 $self->[ $index ] = $arg{ $member } }
122             elsif ($caller_is_obj) {
123 60         83 $self->[ $index ] = $caller->[ $index ] }
124             else {
125 897         1783 $self->[ $index ] = $_default[ $index ] }
126 1590         3251 $_validate[ $index ]->( $self->[ $index ] );
127             }
128 159 100       858 $self->_set_each() unless defined $self->[EACH];
129 159         587 $self;
130             }
131              
132             ### do not rely on copy() being an alias of new(), see POD
133             *copy = \&new;
134              
135             # accessor methods
136 4     4   40 sub _get_each_name { $_each_subs{$_[0]->[EACH]}; }
137 2     2   427 sub _get_each_ref { $_[0]->[EACH] }
138 4     4 1 395 sub get_set { @{$_[0]->[SET]} }
  4         24  
139 10     10 1 3644 sub get_iterator { $_[0]->[ITERATOR] }
140 4     4 1 360 sub get_rewind { $_[0]->[REWIND] }
141 4     4 1 399 sub get_bound { $_[0]->[BOUND] }
142 5     5 1 369 sub get_undef { $_[0]->[UNDEF] }
143 4     4 1 372 sub get_stop { $_[0]->[STOP] }
144 4     4 1 353 sub get_group { $_[0]->[GROUP] }
145 4     4 1 357 sub get_count { $_[0]->[COUNT] }
146 2     2 0 10 sub get_user { $_[0]->[USER] }
147              
148             sub _set_each {
149 174     174   416 my $self = shift;
150 174 100       349 if( defined $_[0] ) {
151 2         4 $self->[EACH] = $_[0];
152 2         8 $_validate[EACH]->( $self->[EACH] );
153 1         3 return $self->[EACH];
154             }
155              
156             # are these set to defaults?
157 172         277 my $r = ( $self->[REWIND] == $_default[REWIND] );
158 172         252 my $b = ( $self->[BOUND] == $_default[BOUND] );
159 172         236 my $s = ( !defined $self->[STOP] ); # default is undef
160 172         418 my $g = ( !defined $self->[GROUP] ); # default is undef
161 172         220 my $c = ( !defined $self->[COUNT] ); # default is undef
162 172 100 100     1085 CASE: {
      100        
      100        
      100        
163             # all are defaults
164 172         182 ($r && $b && $s && $g && $c) and
165             $self->[EACH] = \&each_default, last CASE;
166             # all except bound
167 155 100 100     837 ($r && $s && $g && $c) and
      100        
      100        
168             $self->[EACH] = \&each_unbound, last CASE;
169             # all except group
170 148 100 100     620 ($r && $b && $s && $c) and
      100        
      100        
171             $self->[EACH] = \&each_group, last CASE;
172             # else
173 140         2331 $self->[EACH] = \&each_complete;
174             }
175 172         332 $self->[EACH];
176             }
177              
178             sub set_set {
179 4     4 1 1368 my $self = shift;
180 4         13 $self->[SET] = [@_];
181 4         15 $_validate[SET]->( $self->[SET] );
182 3         4 @{$self->[SET]};
  3         12  
183             }
184             sub set_iterator {
185 248     248 1 5336 my $self = shift;
186 248         326 $self->[ITERATOR] = $_[0];
187 248         447 $_validate[ITERATOR]->( $self->[ITERATOR] );
188 247         902 $self->[ITERATOR];
189             }
190             sub set_rewind {
191 5     5 1 786 my $self = shift;
192 5         12 $self->[REWIND] = $_[0];
193 5         16 $_validate[REWIND]->( $self->[REWIND] );
194 4         10 $self->_set_each;
195 4         9 $self->[REWIND];
196             }
197             sub set_bound {
198 2     2 1 870 my $self = shift;
199 2         7 $self->[BOUND] = $_[0];
200 2         8 $_validate[BOUND]->( $self->[BOUND] );
201 1         3 $self->_set_each;
202 1         3 $self->[BOUND];
203             }
204             sub set_undef {
205 2     2 1 766 my $self = shift;
206 2         7 $self->[UNDEF] = $_[0];
207 2         9 $_validate[UNDEF]->( $self->[UNDEF] );
208 2         5 $self->[UNDEF];
209             }
210             sub set_stop {
211 8     8 1 2590 my $self = shift;
212 8         21 $self->[STOP] = $_[0];
213 8         22 $_validate[STOP]->( $self->[STOP] );
214 7         18 $self->_set_each;
215 7         17 $self->[STOP];
216             }
217             sub set_group {
218 6     6 1 2573 my $self = shift;
219 6         17 $self->[GROUP] = $_[0];
220 6         22 $_validate[GROUP]->( $self->[GROUP] );
221 5         14 $self->_set_each;
222 5         11 $self->[GROUP];
223             }
224             sub set_count {
225 2     2 1 898 my $self = shift;
226 2         8 $self->[COUNT] = $_[0];
227 2         9 $_validate[COUNT]->( $self->[COUNT] );
228 1         4 $self->_set_each;
229 1         3 $self->[COUNT];
230             }
231              
232             # note: no set_user() defined in this class
233              
234             # utility methods
235             sub rewind {
236 238     238 1 808 my $self = shift;
237 238 100       663 $self->set_iterator(
238             defined $_[0] ? $_[0] : $self->[REWIND] );
239             }
240             sub incr_iterator {
241 1143     1143 1 1227 my $self = shift;
242 1143 100       3161 return $self->[ITERATOR]++ unless $self->[GROUP];
243 437         564 my $i = $self->[ITERATOR];
244 437         571 $self->[ITERATOR] += $self->[GROUP];
245 437         736 $i;
246             }
247              
248             # each methods
249 1323     1323 1 9402 sub each { &{$_[0]->[EACH]} } # call this object's each()
  1323         2876  
250              
251             sub each_default { # enough attributes are default
252 91     91 0 101 my $self = shift;
253 91         126 my $i = $self->[ITERATOR]++; # inlined incr_iterator
254 155         405 $self->[ITERATOR] = $self->[REWIND], # inlined rewind
255 91 100       82 return if grep {$i >= @$_} @{$self->[SET]};
  91         133  
256 73         77 ( (map $_->[$i], @{$self->[SET]}), $i );
  73         403  
257             }
258              
259             sub each_unbound { # bound not true
260 55     55 0 67 my $self = shift;
261 55         88 my $i = $self->[ITERATOR]++; # inlined incr_iterator
262 110         281 $self->[ITERATOR] = $self->[REWIND], # inlined rewind
263 55 100       54 return unless grep {$i < @$_} @{$self->[SET]};
  55         89  
264 46 100       55 ( (map {$i<@$_ ? $_->[$i] : $self->[UNDEF]} @{$self->[SET]}), $i );
  92         305  
  46         65  
265             }
266              
267             sub each_group { # group is defined
268 36     36 0 46 my $self = shift;
269 36         42 my $group = $self->[GROUP];
270 36         48 my $i = $self->[ITERATOR]; # inlined
271 36         47 $self->[ITERATOR] += $group; # incr_iterator
272 49         164 $self->[ITERATOR] = $self->[REWIND], # inlined rewind
273 36 100       36 return if grep {$i >= @$_} @{$self->[SET]};
  36         60  
274 28         33 my @ret;
275 28         30 foreach my $aref ( @{$self->[SET]} ) {
  28         52  
276 97 100       255 push @ret,
277 37         69 map {$_<@$aref ? $aref->[$_] : $self->[UNDEF]}
278             ($i..$i+$group-1) }
279 28         112 ( @ret, $i );
280             }
281              
282             sub each_complete { # enough attributes aren't default
283 1141     1141 0 1273 my $self = shift;
284 1141         1880 my $i = $self->incr_iterator(); # increment for next time, use current
285 1141         1639 my $set = $self->[SET];
286 1141         1309 my $stop = $self->[STOP];
287 1141         1204 my $undef = $self->[UNDEF];
288 1141         1184 my $group = $self->[GROUP];
289 1141         1089 my $c;
290              
291             # if bound to the shortest array, stop there (or at stop) ...
292 1141 100       1853 if( $self->[BOUND] ) {
293 431 100       637 if( defined $stop ) {
294 240 100 100     676 $self->rewind(), return if $i > $stop || grep {$i >= @$_} @$set }
  420         1338  
295             else {
296 191 100       248 $self->rewind(), return if grep {$i >= @$_} @$set }
  365         948  
297 320 100       863 $c = defined $self->[COUNT] ? $self->[COUNT]++ : $i;
298 320 100       1803 return ( (map $_->[$i], @$set), $c ) unless defined $group;
299             }
300              
301             # else not bound to the shortest array, so (maybe) go farther ...
302             else {
303 710 100       1036 if( defined $stop ) { # may go past longest array, too
304 328 100       704 $self->rewind(), return if $i > $stop }
305             else {
306 382 100       536 $self->rewind(), return unless grep {$i < @$_} @$set }
  777         1976  
307 586 100       1131 $c = defined $self->[COUNT] ? $self->[COUNT]++ : $i;
308 586 100       1203 return ( (map {$i<@$_ ? $_->[$i] : $undef} @$set), $c )
  778 100       2861  
309             unless defined $group;
310             }
311              
312             # or return groups of elements, i.e., $group is defined
313 318         392 my @ret;
314 318         468 foreach my $aref ( @$set ) {
315 622 100       1091 push @ret, map {$_<@$aref ? $aref->[$_] : $undef} ($i..$i+$group-1) }
  1451         3704  
316 318         1590 ( @ret, $c );
317             }
318              
319             1; # true
320              
321             __END__