File Coverage

blib/lib/List/Enumerator/Role.pm
Criterion Covered Total %
statement 387 416 93.0
branch 126 174 72.4
condition 12 12 100.0
subroutine 90 101 89.1
pod 1 54 1.8
total 616 757 81.3


line stmt bran cond sub pod time code
1             package List::Enumerator::Role;
2 5     5   47 use strict;
  5         144  
  5         248  
3 5     5   35 use warnings;
  5         10  
  5         208  
4              
5 5     5   7055 use Exception::Class ( "StopIteration" );
  5         67809  
  5         33  
6              
7 5     5   1213 use List::Util;
  5         8  
  5         348  
8 5     5   5710 use List::MoreUtils;
  5         6752  
  5         269  
9              
10 5     5   39 use base qw/Class::Accessor::Fast/;
  5         10  
  5         223079  
11 5     5   35185 no warnings 'once';
  5         14  
  5         2730  
12              
13             __PACKAGE__->mk_accessors(qw/is_beginning/);
14              
15             # this is mix-in module
16              
17             sub new {
18 417     417 1 1286 my ($class, %opts) = @_;
19 417         1876 my $self = $class->SUPER::new(\%opts);
20 417 50       6412 $self->can("BUILD") && $self->BUILD(\%opts);
21 417         3409 $self->is_beginning(1);
22 417         3860 $self;
23             }
24              
25             sub next {
26 1129     1129 0 1564 my ($self) = @_;
27 1129         2918 $self->is_beginning(0);
28 1129         6356 $self->_next();
29             }
30              
31             sub rewind {
32 212     212 0 347 my ($self) = @_;
33 212 100       539 unless ($self->is_beginning) {
34 32         264 $self->_rewind();
35 32         97 $self->is_beginning(1);
36             }
37 212         1209 $self;
38             }
39              
40             sub select {
41 4     4 0 11 my ($self, $block) = @_;
42 4         15 $self->rewind;
43              
44             List::Enumerator::Sub->new(
45             next => sub {
46 20     20   73 local $_;
47 20         24 do {
48 38         117 $_ = $self->next;
49             } while (!$block->($_));
50 18         94 $_;
51             },
52             rewind => sub {
53 0     0   0 $self->rewind;
54             }
55 4         23 );
56             }
57             *find_all = \&select;
58              
59             sub reject {
60 1     1 0 3 my ($self, $block) = @_;
61             $self->select(sub {
62 10     10   21 !$block->($_);
63 1         9 });
64             }
65              
66             sub reduce {
67 12     12 0 49 my ($self, $result, $block) = @_;
68 12         54 $self->rewind;
69              
70 5     5   37 no strict 'refs';
  5         13  
  5         29479  
71              
72 12 100       42 if (@_ == 2) {
73 2         6 $block = $result;
74 2         5 $result = undef;
75             };
76              
77 12         37 my $caller = caller;
78 12         105 local *{$caller."::a"} = \my $a;
  12         54  
79 12         19 local *{$caller."::b"} = \my $b;
  12         48  
80              
81 12         49 my @list = $self->to_list;
82 12 100       99 unshift @list, $result if defined $result;
83              
84 12         25 $a = shift @list;
85 12         31 for (@list) {
86 67         119 $b = $_;
87 67         127 $a = $block->($a, $b);
88             };
89              
90 12         117 $a;
91             }
92             *inject = \&reduce;
93              
94             sub slice {
95 22     22 0 114 my ($self, $start, $end) = @_;
96 22         190 my @list = $self->to_list;
97 22 100       359 if (defined $end) {
98 15 100       50 return () if abs $start > @list;
99 14 100       35 $start = @list + $start if $start < 0;
100 14 100       27 $end = @list + $end if $end < 0;
101 14 100       35 $end = $#list if $end > $#list;
102 14 50       43 return () if $start > @list;
103 14 100       35 return () if $start > $end;
104              
105 13         39 my @ret = @list[$start .. $end];
106 13 100       28 if (wantarray) {
107 6 50       56 @ret ? @ret : ();
108             } else {
109 7 50       94 @ret ? List::Enumerator::Array->new(array => \@ret)
110             : List::Enumerator::Array->new(array => []);
111             }
112             } else {
113 7         51 $list[$start];
114             }
115             };
116              
117             sub find {
118 9     9 0 32 my ($self, $target) = @_;
119 9 100   9   41 my $block = ref($target) eq "CODE" ? $target : sub { $_ eq $target };
  9         34  
120 9         12 my $ret;
121             $self->each(sub {
122 20 100   20   40 if ($block->($self)) {
123 7         24 $ret = $_;
124 7         93 $self->stop;
125             }
126 9         57 });
127 9         75 $ret;
128             }
129              
130             sub first {
131 6     6 0 32 my ($self, $n) = @_;
132 6         10 my $ret;
133 6 100       18 if (defined $n) {
134 4         14 $ret = [ $self->take($n) ];
135             } else {
136 2         13 $self->rewind;
137 2         12 $ret = $self->next;
138 2         15 $self->rewind;
139             }
140 6         41 $ret;
141             }
142              
143             sub last {
144 6     6 0 24 my ($self, $n) = @_;
145 6         8 my $ret;
146 6 100       17 if (defined $n) {
147 4         7 $ret = [ @{ $self->to_a }[-$n..-1] ];
  4         14  
148             } else {
149 2         10 $ret = $self->to_a->[-1];
150             }
151             }
152              
153             sub max {
154 1     1 0 3 my ($self, $block) = @_;
155 1         5 List::Util::max $self->to_list;
156             }
157              
158             sub max_by {
159 1     1 0 2 my ($self, $block) = @_;
160 1         8 $self->sort_by($block)->last;
161             }
162              
163             sub min {
164 1     1 0 2 my ($self, $block) = @_;
165 1         5 List::Util::min $self->to_list;
166             }
167              
168             sub min_by {
169 1     1 0 6 my ($self, $block) = @_;
170 1         6 $self->sort_by($block)->first;
171             }
172              
173             sub minmax_by {
174 2     2 0 6 my ($self, $block) = @_;
175 2 100   3   9 $block = sub { $_ } unless $block;
  3         9  
176 2         9 my @ret = $self->sort_by($block)->to_list;
177 2 50       34 wantarray? ($ret[0], $ret[$#ret]) : [ $ret[0], $ret[$#ret] ];
178             }
179             *minmax = \&minmax_by;
180              
181             sub sort_by {
182 5     5 0 12 my ($self, $block) = @_;
183 17         39 List::Enumerator::Array->new(array => [
184             map {
185 17         52 $_->[0];
186             }
187             sort {
188 17         81 $a->[1] <=> $b->[1];
189             }
190             map {
191 5         17 [$_, $block->($_)];
192             }
193             $self->to_list
194             ]);
195             }
196              
197             sub sort {
198 3     3 0 48 my ($self, $block) = @_;
199 3 100       21 my @ret = $block ? sort { $block->($a, $b) } $self->to_list : sort $self->to_list;
  3         19  
200 3 50       43 wantarray? @ret : List::Enumerator::Array->new(array => \@ret);
201             }
202              
203             sub sum {
204 3     3 0 8 my ($self) = @_;
205 3     11   23 $self->reduce(0, sub { $a + $b });
  11         22  
206             }
207              
208             sub uniq {
209 2     2 0 13 my ($self) = @_;
210 2         19 my @ret = List::MoreUtils::uniq($self->to_list);
211 2 50       104 wantarray? @ret : List::Enumerator::Array->new(array => \@ret);
212             }
213              
214             sub grep {
215 2     2 0 19 my ($self, $block) = @_;
216 2         26 my @ret = grep { $block->($_) } $self->to_list;
  13         58  
217 2 50       28 wantarray? @ret : List::Enumerator::Array->new(array => \@ret);
218             }
219              
220             sub compact {
221 3     3 0 35 my ($self) = @_;
222 3         13 my @ret = grep { defined } $self->to_list;
  13         62  
223 3 50       70 wantarray? @ret : List::Enumerator::Array->new(array => \@ret);
224             }
225              
226             sub reverse {
227 2     2 0 16 my ($self) = @_;
228 2         9 my @ret = reverse $self->to_list;
229 2 50       25 wantarray? @ret : List::Enumerator::Array->new(array => \@ret);
230             }
231              
232             sub flatten {
233 4     4 0 16 my ($self, $level) = @_;
234 4         16 my $ret = _flatten($self->to_a, $level);
235 4 50       29 wantarray? @$ret : List::Enumerator::Array->new(array => $ret);
236             }
237              
238             sub _flatten {
239 10     10   33 my ($array, $level) = @_;
240 6 100       50 (defined($level) && $level <= 0) ? $array : [
241             map {
242 10 100 100     43 (ref($_) eq 'ARRAY') ? @{ _flatten($_, defined($level) ? $level - 1 : undef) } : $_;
  28 100       89  
243             }
244             @$array
245             ];
246             }
247              
248             sub length {
249 126     126 0 575 my ($self) = @_;
250 126         143 scalar @{[ $self->to_list ]};
  126         294  
251             }
252             *size = \&length;
253              
254             sub is_empty {
255 2     2 0 4 my ($self) = @_;
256 2         14 !$self->length;
257             }
258              
259             sub index_of {
260 10     10 0 46 my ($self, $target) = @_;
261 10         37 $self->rewind;
262              
263 10 100   21   53 my $block = ref($target) eq "CODE" ? $target : sub { $_ eq $target };
  21         151  
264              
265 10         17 my $ret = 0;
266 10         17 return eval {
267 10         274 while (1) {
268 31         74 my $item = $self->next;
269 28 100       176 return $ret if $block->(local $_ = $item);
270 21         55 $ret++;
271             }
272 0 0       0 }; if (Exception::Class->caught("StopIteration") ) { } else {
273 0         0 my $e = Exception::Class->caught();
274 0 0       0 ref $e ? $e->rethrow : die $e if $e;
    0          
275             }
276              
277 0         0 undef;
278             }
279             *find_index = \&index_of;
280              
281              
282             sub chain {
283 5     5 0 16 my ($self, @others) = @_;
284 5         22 $self->rewind;
285              
286 5         10 my ($elements, $current);
287 5         11 $elements = List::Enumerator::E([ map { List::Enumerator::E($_)->rewind } $self, @others ]);
  10         29  
288 5         19 $current = $elements->next;
289              
290 5         31 my @cache = ();
291 5         12 my $i = 0;
292             my $ret = List::Enumerator::Sub->new(
293             next => sub {
294 138     138   594 my $ret;
295 138 100       261 if ($i < @cache) {
296 72         151 $ret = $cache[$i];
297             } else {
298 66         79 eval {
299 66         300 $ret = $current->next;
300 57         437 push @cache, $ret;
301 66 100       8929 }; if (Exception::Class->caught("StopIteration") ) {
302 9         325 $current = $elements->next;
303 5         36 $ret = $current->next;
304 5         41 push @cache, $ret;
305             } else {
306 57         389 my $e = Exception::Class->caught();
307 57 0       309 ref $e ? $e->rethrow : die $e if $e;
    50          
308             }
309             }
310 134         153 $i++;
311 134         466 $ret;
312             },
313             rewind => sub {
314 6     6   34 $i = 0;
315             }
316 5         44 );
317              
318 5 50       43 wantarray? $ret->to_list : $ret;
319             }
320              
321             sub take {
322 27     27 0 80 my ($self, $arg) = @_;
323 27         66 $self->rewind;
324              
325 27         35 my $ret;
326 27 100       69 if (ref $arg eq "CODE") {
327             $ret = List::Enumerator::Sub->new(
328             next => sub {
329 10     10   38 local $_ = $self->next;
330 10 100       18 if ($arg->($_)) {
331 8         31 $_;
332             } else {
333 2         9 StopIteration->throw;
334             }
335             },
336             rewind => sub {
337 0     0   0 $self->rewind;
338             }
339 2         10 );
340             } else {
341 25         35 my $i;
342             $ret = List::Enumerator::Sub->new(
343             next => sub {
344 173 100   173   811 if ($i++ < $arg) {
345 148         334 $self->next;
346             } else {
347 25         98 StopIteration->throw;
348             }
349             },
350             rewind => sub {
351 0     0   0 $self->rewind;
352 0         0 $i = 0;
353             }
354 25         188 );
355             }
356 27 100       149 wantarray? $ret->to_list : $ret;
357             }
358             *take_while = \&take;
359              
360             sub drop {
361 10     10 0 17 my ($self, $arg) = @_;
362 10         26 $self->rewind;
363              
364 10         16 my $ret;
365 10 100       26 if (ref $arg eq "CODE") {
366 2         2 my $first;
367             $ret = List::Enumerator::Sub->new(
368             next => sub {
369 10     10   31 my $ret;
370 10 100       16 unless ($first) {
371 2         3 do { $first = $self->next } while ($arg->(local $_ = $first));
  10         39  
372 2         7 $ret = $first;
373             } else {
374 8         18 $ret = $self->next;
375             }
376 10         31 $ret;
377             },
378             rewind => sub {
379 0     0   0 $self->rewind;
380 0         0 $first = undef;
381             }
382 2         14 );
383             } else {
384 8         13 my $i = $arg;
385             $ret = List::Enumerator::Sub->new(
386             next => sub {
387 62     62   336 $self->next while (0 < $i--);
388 62         134 $self->next;
389             },
390             rewind => sub {
391 1     1   10 $self->rewind;
392 1         2 $i = $arg;
393             }
394 8         66 );
395             }
396 10 100       61 wantarray? $ret->to_list : $ret;
397             }
398             *drop_while = \&drop;
399              
400             sub every {
401 2     2 0 5 my ($self, $block) = @_;
402 2         17 for ($self->to_list) {
403 7 100       43 return 0 unless $block->($_);
404             }
405 1         9 return 1;
406             }
407             *all = \&every;
408              
409             sub some {
410 6     6 0 11 my ($self, $block) = @_;
411 6         20 for ($self->to_list) {
412 14 100       60 return 1 if $block->($_);
413             }
414 3         20 return 0;
415             }
416             *any = \&some;
417              
418              
419             sub none {
420 6     6 0 12 my ($self, $block) = @_;
421 6 100   11   22 $block = sub { $_ } unless $block;
  11         42  
422              
423 6         18 for ($self->to_list) {
424 17 100       77 return 0 if $block->($_);
425             }
426 2         16 return 1;
427             }
428              
429             sub one {
430 6     6 0 11 my ($self, $block) = @_;
431 6 100   12   22 $block = sub { $_ } unless $block;
  12         30  
432              
433 6         9 my $ret = 0;
434 6         16 for ($self->to_list) {
435 22 100       80 if ($block->($_)) {
436 6 100       22 if ($ret) {
437 2         13 return 0;
438             } else {
439 4         8 $ret = 1;
440             }
441             }
442             }
443 4         29 return $ret;
444             }
445              
446             sub zip {
447 11     11 0 47 my ($self, @others) = @_;
448 11         48 $self->rewind;
449              
450 18         52 my $elements = [
451             map {
452 11         22 List::Enumerator::E($_)->rewind;
453             }
454             @others
455             ];
456              
457 11         24 my @cache = ();
458             my $ret = List::Enumerator::Sub->new(
459             next => sub {
460 61     61   316 my $ret = [];
461 61         144 push @$ret, $self->next;
462 51         243 for (@$elements) {
463 90         93 my $n;
464 90         99 eval {
465 90         195 $n = $_->next;
466 90 100       3497 }; if (Exception::Class->caught("StopIteration") ) {
467 3         236 $n = undef;
468             } else {
469 87         598 my $e = Exception::Class->caught();
470 87 0       442 ref $e ? $e->rethrow : die $e if $e;
    50          
471             }
472 90         208 push @$ret, $n;
473             }
474 51         88 push @cache, $ret;
475 51         148 $ret;
476             },
477             rewind => sub {
478 0     0   0 my $i = 0;
479             $_->next_sub(sub {
480 0 0       0 if ($i < @cache) {
481 0         0 $cache[$i++];
482             } else {
483 0         0 StopIteration->throw;
484             }
485 0         0 });
486             $_->rewind_sub(sub {
487 0         0 $i = 0;
488 0         0 });
489             }
490 11         588 );
491              
492 11 100       123 wantarray? $ret->to_list : $ret;
493             }
494              
495             sub with_index {
496 1     1 0 3 my ($self, $start) = @_;
497 1         6 $self->zip(List::Enumerator::E($start)->countup);
498             }
499              
500             sub countup {
501 23     23 0 48 my ($self, $lim) = @_;
502 23   100     214 my $start = eval { $self->next } || 0;
503 23         3795 my $i = $start;
504             List::Enumerator::Sub->new(
505             next => sub {
506 200 100 100 200   1216 ($lim && $i > $lim) && StopIteration->throw;
507 192         650 $i++;
508             },
509             rewind => sub {
510 5     5   26 $i = $start;
511             }
512 23         399 );
513             }
514             *countup_to = \&countup;
515             *to = \&countup;
516              
517              
518             sub cycle {
519 5     5 0 11 my ($self) = @_;
520 5         29 $self->rewind;
521              
522 5         10 my @cache = ();
523             List::Enumerator::Sub->new(
524             next => sub {
525 22     22   99 my ($this) = @_;
526              
527 22         26 my $ret;
528 22         31 eval {
529 22         64 $ret = $self->next;
530 17         100 push @cache, $ret;
531 22 100       5443 }; if (Exception::Class->caught("StopIteration") ) {
532 5         316 my $i = -1;
533             $this->next_sub(sub {
534 41         261 $cache[++$i % @cache];
535 5         31 });
536 5         35 $ret = $this->next;
537             } else {
538 17         166 my $e = Exception::Class->caught();
539 17 0       96 ref $e ? $e->rethrow : die $e if $e;
    50          
540             }
541 22         93 $ret;
542             },
543             rewind => sub {
544 0     0   0 $self->rewind;
545 0         0 @cache = ();
546             }
547 5         65 );
548             }
549              
550             sub join {
551 3     3 0 21 my ($self, $sep) = @_;
552 3   100     19 join $sep || "", $self->to_list;
553             }
554              
555             sub group_by {
556 3     3 0 7 my ($self, $block) = @_;
557             $self->reduce({}, sub {
558 32     32   37 local $_ = $b;
559 32         56 my $r = $block->($b);
560 32   100     206 $a->{$r} ||= [];
561 32         33 push @{ $a->{$r} }, $b;
  32         50  
562 32         69 $a;
563 3         27 });
564             }
565              
566             sub partition {
567 2     2 0 5 my ($self, $block) = @_;
568             my $ret = $self->group_by(sub {
569 20 100   20   36 $block->($_) ? 1 : 0;
570 2         16 });
571              
572 2 100       27 wantarray? ($ret->{1}, $ret->{0}) : [$ret->{1}, $ret->{0}];
573             }
574              
575             sub is_include {
576 4     4 0 7 my ($self, $target) = @_;
577 4     8   23 $self->some(sub { $_ eq $target });
  8         36  
578             }
579             *include = \&is_include;
580              
581             sub map {
582 3     3 0 30 my ($self, $block) = @_;
583 3         15 $self->rewind;
584              
585             my $ret = List::Enumerator::Sub->new(
586             next => sub {
587 28     28   164 local $_ = $self->next;
588 26         87 $block->($_);
589             },
590             rewind => sub {
591 0     0   0 $self->rewind;
592             }
593 3         130 );
594 3 100       18 wantarray? $ret->to_list : $ret;
595             }
596             *collect = \&map;
597              
598             sub each {
599 57     57 0 92 my ($self, $block) = @_;
600 57         139 $self->rewind;
601              
602 57         94 eval {
603 57         817 while (1) {
604 338         732 local $_ = $self->next;
605 281 50       1028 $block->($_) if $block;
606             }
607 57 50       49042 }; if (Exception::Class->caught("StopIteration") ) { } else {
608 0         0 my $e = Exception::Class->caught();
609 0 0       0 ref $e ? $e->rethrow : die $e if $e;
    0          
610             }
611              
612 57         3647 $self;
613             }
614              
615             sub to_list {
616 51     51 0 84 my ($self) = @_;
617              
618 51         101 my @ret = ();
619             $self->each(sub {
620 261     261   412 push @ret, $_;
621 51         282 });
622              
623 51 50       732 wantarray? @ret : [ @ret ];
624             }
625              
626             sub each_index {
627 1     1 0 13 my ($self, $block) = @_;
628 1         6 $self->rewind;
629              
630 1         1 my $i = 0;
631 1         13 eval {
632 1         3 while (1) {
633 4         24 $self->next;
634 3         15 local $_ = $i++;
635 3 50       12 $block->($_) if $block;
636             }
637 1 50       1034 }; if (Exception::Class->caught("StopIteration") ) { } else {
638 0         0 my $e = Exception::Class->caught();
639 0 0       0 ref $e ? $e->rethrow : die $e if $e;
    0          
640             }
641              
642 1 50       26 wantarray? $self->to_list : $self;
643             }
644              
645             sub each_slice {
646 3     3 0 6 my ($self, $n, $block) = @_;
647 3         10 $self->rewind;
648              
649             my $ret = List::Enumerator::Sub->new(
650             next => sub {
651 12     12   45 my $arg = [];
652 12         17 my $i = $n - 1;
653 12         23 push @$arg, $self->next;
654 10         40 while ($i--) {
655 17         28 eval {
656 17         50 push @$arg, $self->next;
657 17 100       2429 }; if (Exception::Class->caught("StopIteration") ) { } else {
658 14         86 my $e = Exception::Class->caught();
659 14 0       81 ref $e ? $e->rethrow : die $e if $e;
    50          
660             }
661             }
662 10         195 $arg;
663             },
664             rewind => sub {
665 0     0   0 $self->rewind;
666             }
667 3         22 );
668 3 100       9 if ($block) {
669 2         7 $ret->each($block);
670             }
671 3 50       38 wantarray? $ret->to_list : $ret;
672             }
673              
674             sub each_cons {
675 3     3 0 7 my ($self, $n, $block) = @_;
676 3         7 $self->rewind;
677              
678 3         4 my @memo = ();
679             my $ret = List::Enumerator::Sub->new(
680             next => sub {
681 12 100   12   44 if (@memo < $n) {
682 3         2 my $i = $n;
683 3         13 push @memo, $self->next while $i--;
684             } else {
685 9         10 shift @memo;
686 9         17 push @memo, $self->next;
687             }
688 10         45 [ @memo ];
689             },
690             rewind => sub {
691 0     0   0 $self->rewind;
692 0         0 @memo = ();
693             }
694 3         21 );
695 3 100       9 if ($block) {
696 2         6 $ret->each($block);
697             }
698 3 50       32 wantarray? $ret->to_list : $ret;
699             }
700              
701             sub choice {
702 114     114 0 149 my ($self) = @_;
703 114         251 $self->[int(rand($self->length))];
704             }
705             *sample = \&choice;
706              
707             sub shuffle {
708 2     2 0 9 my ($self) = @_;
709 2         10 my @shuffled = List::Util::shuffle($self->to_list);
710 2 100       47 wantarray? @shuffled : List::Enumerator::Array->new(array => \@shuffled);
711             }
712              
713             sub transpose {
714 3     3 0 6 my ($self) = @_;
715 3         10 my ($first, @rest) = $self->to_list;
716              
717 3 100       24 if (defined $first) {
718 2 100       18 die "not a matrix" unless ref($first) eq "ARRAY";
719 1         5 List::Enumerator::Array->new(array => $first)->zip(@rest);
720             } else {
721 1         6 List::Enumerator::Array->new(array => [])->to_list;
722             }
723             }
724              
725             sub to_a {
726 25     25 0 67 my ($self) = @_;
727 25         96 [ $self->to_list ];
728             }
729              
730             sub expand {
731 3     3 0 25 my ($self) = @_;
732 3         25 List::Enumerator::Array->new(array => $self->to_a);
733             }
734             *dup = \&expand;
735              
736             sub dump {
737 1     1 0 2 my ($self) = @_;
738 1         15 require Data::Dumper;
739 1         8 Data::Dumper->new([ $self->to_a ])->Purity(1)->Terse(1)->Dump;
740             }
741              
742              
743             sub _next {
744 0     0   0 die "Not implemented.";
745             }
746              
747             sub _rewind {
748 0     0   0 die "Not implemented.";
749             }
750              
751             sub stop {
752 58     58 0 93 my ($self) = @_;
753 58         334 StopIteration->throw;
754             }
755              
756             1;
757             __END__
758              
759              
760