File Coverage

blib/lib/List/Lazy.pm
Criterion Covered Total %
statement 153 153 100.0
branch 32 36 88.8
condition 16 17 94.1
subroutine 35 35 100.0
pod 13 14 92.8
total 249 255 97.6


line stmt bran cond sub pod time code
1             package List::Lazy;
2             our $AUTHORITY = 'cpan:YANICK';
3             # ABSTRACT: Generate lists lazily
4             $List::Lazy::VERSION = '0.3.2';
5              
6              
7              
8 8     8   539242 use Moo;
  8         88906  
  8         43  
9 8     8   15127 use MooX::HandlesVia;
  8         75528  
  8         49  
10              
11 8     8   4290 use Clone qw/ clone /;
  8         19667  
  8         447  
12              
13 8     8   95 use 5.22.0;
  8         64  
14              
15 8     8   3523 use experimental 'signatures', 'postderef';
  8         27694  
  8         47  
16              
17 8     8   6386 use List::MoreUtils;
  8         99077  
  8         47  
18 8     8   7521 use Carp;
  8         18  
  8         15674  
19              
20             *list_before = *List::MoreUtils::before;
21              
22             extends 'Exporter::Tiny';
23              
24             sub _exporter_validate_opts {
25 8     8   1503 my $into = $_[1]->{into};
26 8 50       679 eval qq{
27             *${into}::a = *::a;
28             *${into}::b = *::b;
29             1;
30             } or die $@;
31             }
32              
33              
34             our @EXPORT_OK = qw/ lazy_list lazy_range lazy_fixed_list /;
35              
36 10     10   15 sub _lazy_list ($generator,$state=undef) {
  10         16  
  10         15  
  10         14  
37 10         214 return List::Lazy->new(
38             generator => $generator,
39             state => $state,
40             );
41             }
42              
43 10     10 1 27 sub lazy_list :prototype(&@) { goto &_lazy_list }
44              
45 8     8   32 sub _lazy_range ($min,$max,$step=1) {
  8         15  
  8         10  
  8         12  
  8         13  
46 8 100   26   34 my $it = ref $step ? $step : sub { $_ + $step };
  26         43  
47              
48             return scalar lazy_list {
49 33 100 100 33   104 return if defined $max and $_ > $max;
50 29         39 my $current = $_;
51 29         49 $_ = $it->();
52 29         89 return $current;
53 8         45 } $min;
54             }
55              
56              
57 8     8 1 3334 sub lazy_range :prototype($$@) { goto &_lazy_range }
58              
59             sub lazy_fixed_list {
60 12     12 1 6479 my @list = @_;
61             return List::Lazy->new(
62             _next => [ @list ],
63             is_done => 0,
64 12     12   26 generator => sub { return () },
65 12         268 );
66             }
67              
68             has generator => (
69             is => 'ro',
70             required => 1,
71             );
72              
73             has state => (
74             is => 'rw'
75             );
76              
77             has _next => (
78             is => 'rw',
79             handles_via => 'Array',
80             handles => {
81             has_next => 'count',
82             shift_next => 'shift',
83             push_next => 'push',
84             _all_next => 'elements',
85             },
86             default => sub { [] },
87             );
88              
89             has is_done => (
90             is => 'rw',
91             default => sub { 0 },
92             );
93              
94 127     127 0 5856 sub generate_next($self) {
  127         167  
  127         148  
95 127         230 local $_ = $self->state;
96              
97 127         247 my @values = $self->generator->();
98 127         262 $self->state($_);
99              
100 127 100       308 $self->is_done(1) unless @values;
101              
102 127         2353 return @values;
103             }
104              
105 126     126 1 1978 sub next($self,$num=1) {
  126         172  
  126         170  
  126         144  
106 126         162 my @returns;
107              
108 126 50 100     468 croak "next called in scalar context with \$num = $num"
      66        
109             if defined wantarray and not wantarray and $num != 1;
110              
111 126   100     469 while( @returns < $num and not $self->is_done ) {
112 196 100       6136 $self->push_next( $self->generate_next )
113             unless $self->has_next;
114              
115 196 100       11518 next unless $self->has_next;
116              
117 172 100       7706 if( ref $self->_next->[0] eq 'List::Lazy' ) {
118 9         15 my $list = $self->_next->[0];
119 9         26 push @returns, $list->next;
120 9 100       81 $self->shift_next if $list->is_done;
121             }
122             else {
123 163         2551 push @returns, $self->shift_next;
124             }
125             }
126              
127 126 100       5912 return wantarray ? @returns : $returns[0];
128             }
129              
130 8     8 1 86 sub all ($self) {
  8         18  
  8         12  
131 8         24 return $self->next(1E99);
132             }
133              
134 1     1 1 1752 sub reduce($self,$reducer,$reduced=undef) {
  1         3  
  1         2  
  1         2  
  1         2  
135 1 50       9 $reduced = $self->next if @_ < 3;
136              
137 1         4 while( my $next = $self->next ) {
138 9         17 local ( $::a, $::b ) = ( $reduced, $next );
139 9         18 $reduced = $reducer->();
140             }
141              
142 1         7 return $reduced;
143             }
144              
145 7     7 1 36 sub map($self,$map) {
  7         10  
  7         14  
  7         9  
146             return List::Lazy->new(
147             state => $self->_clone,
148             generator => sub {
149 25     25   68 while( my @next = $_->next ) {
150 29         50 @next = map { $map->() } @next;
  29         56  
151 29 100       168 return @next if @next;
152             }
153 2         6 return;
154             },
155 7         27 );
156             }
157              
158 1     1 1 1788 sub batch($self,$n) {
  1         3  
  1         1  
  1         2  
159             return List::Lazy->new(
160             state => [ $self->_clone, [] ],
161             generator => sub {
162 4     4   7 my $stash = $_->[1];
163              
164 4         21 while( my @next = $_->[0]->next ) {
165 5         6 push @$stash, @next;
166 5 100       15 if( @$stash >= $n ) {
167 2         8 return [ splice @$stash, 0, $n ];
168             }
169             }
170              
171 2 100       7 return @$stash ? [ splice @$stash ] : ();
172             },
173 1         7 );
174             };
175              
176 2     2 1 23 sub grep($self,$filter) {
  2         4  
  2         3  
  2         3  
177 14 100   14   27 $self->map(sub{ $filter->() ? $_ : () })
178 2         13 }
179              
180 2     2 1 2434 sub spy($self,$sub=undef) {
  2         4  
  2         3  
  2         5  
181 2   100 1   13 $sub ||= sub { carp $_ };
  1         21  
182 2     6   10 $self->map(sub{ $sub->(); $_ } );
  6         27  
  6         960  
183             }
184              
185 18     18   27 sub _clone($self,%args) {
  18         27  
  18         28  
  18         22  
186 18         512 return List::Lazy->new(
187             state => clone( $self->state ),
188             generator => $self->generator,
189             _next => [ $self->_next->@* ],
190             %args
191             );
192             }
193              
194 1     1 1 1713 sub until($self,$condition) {
  1         2  
  1         2  
  1         2  
195 1         2 my $done;
196             return List::Lazy->new(
197             state => $self->_clone,
198             generator => sub {
199 11 50   11   22 return () if $done;
200 11         19 my @next = $_->next;
201 11         49 my @filtered = list_before( sub { $condition->() }, @next );
  11         22  
202 11         55 $done = @filtered < @next;
203 11         22 return @filtered;
204             },
205 1         5 );
206             }
207              
208 4     4 1 73 sub append($self,@list) {
  4         7  
  4         7  
  4         6  
209              
210             return List::Lazy->new(
211 9         40 state => [ map { $_->_clone } $self, @list ],
212             generator => sub {
213 36     36   72 while(@$_) {
214 45   100     198 shift @$_ while @$_ and $_->[0]->is_done;
215 45 100       95 last unless @$_;
216 41         73 my @next = $_->[0]->next;
217 41 100       109 return @next if @next;
218             }
219 4         6 return ();
220             },
221 4         6 );
222              
223             }
224              
225 1     1 1 21 sub prepend( $self, @list ) {
  1         2  
  1         3  
  1         2  
226 1         2 push @list, $self;
227 1         2 $self = shift @list;
228 1         3 $self->append(@list);
229             }
230              
231             1;
232              
233             __END__