File Coverage

blib/lib/List/Lazy.pm
Criterion Covered Total %
statement 156 156 100.0
branch 32 36 88.8
condition 16 17 94.1
subroutine 36 36 100.0
pod 13 14 92.8
total 253 259 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.3';
5              
6              
7              
8 8     8   436564 use Moo;
  8         70899  
  8         38  
9 8     8   11912 use MooX::HandlesVia;
  8         61019  
  8         44  
10              
11 8     8   3618 use Clone qw/ clone /;
  8         15771  
  8         386  
12              
13 8     8   76 use 5.22.0;
  8         20  
14              
15 8     8   2758 use experimental 'signatures', 'postderef';
  8         21439  
  8         37  
16              
17 8     8   5035 use List::MoreUtils;
  8         85642  
  8         45  
18 8     8   6166 use Carp;
  8         14  
  8         6154  
19              
20             *list_before = *List::MoreUtils::before;
21              
22             extends 'Exporter::Tiny';
23              
24             sub _exporter_validate_opts {
25 8     8   1360 my $into = $_[1]->{into};
26 8 50       569 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   13 sub _lazy_list ($generator,$state=undef) {
  10         12  
  10         19  
  10         12  
37 10         171 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   14 sub _lazy_range ($min,$max,$step=1) {
  8         14  
  8         12  
  8         10  
  8         14  
46 8 100   26   34 my $it = ref $step ? $step : sub { $_ + $step };
  26         34  
47              
48             return scalar lazy_list {
49 33 100 100 33   84 return if defined $max and $_ > $max;
50 29         36 my $current = $_;
51 29         38 $_ = $it->();
52 29         71 return $current;
53 8         36 } $min;
54             }
55              
56              
57 8     8 1 2954 sub lazy_range :prototype($$@) { goto &_lazy_range }
58              
59             sub lazy_fixed_list {
60 12     12 1 5218 my @list = @_;
61             return List::Lazy->new(
62             _next => [ @list ],
63             is_done => 0,
64 12     12   30 generator => sub { return () },
65 12         172 );
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 4505 sub generate_next($self) {
  127         134  
  127         123  
95 127         202 local $_ = $self->state;
96              
97 127         225 my @values = $self->generator->();
98 127         220 $self->state($_);
99              
100 127 100       207 $self->is_done(1) unless @values;
101              
102 127         1719 return @values;
103             }
104              
105 126     126 1 1967 sub next($self,$num=1) {
  126         129  
  126         134  
  126         118  
106 126         131 my @returns;
107              
108 126 50 100     394 croak "next called in scalar context with \$num = $num"
      66        
109             if defined wantarray and not wantarray and $num != 1;
110              
111 126   100     362 while( @returns < $num and not $self->is_done ) {
112 196 100       4880 $self->push_next( $self->generate_next )
113             unless $self->has_next;
114              
115 196 100       8947 next unless $self->has_next;
116              
117 172 100       6100 if( ref $self->_next->[0] eq 'List::Lazy' ) {
118 9         16 my $list = $self->_next->[0];
119 9         15 push @returns, $list->next;
120 9 100       76 $self->shift_next if $list->is_done;
121             }
122             else {
123 163         2316 push @returns, $self->shift_next;
124             }
125             }
126              
127 126 100       4555 return wantarray ? @returns : $returns[0];
128             }
129              
130 8     8 1 104 sub all ($self) {
  8         13  
  8         11  
131 8         23 return $self->next(1E99);
132             }
133              
134             {
135 8     8   50 no warnings;
  8         15  
  8         6851  
136              
137 1     1 1 1496 sub reduce($self,$reducer,$reduced=undef) {
  1         2  
  1         2  
  1         2  
  1         1  
138 1 50       5 $reduced = $self->next if @_ < 3;
139              
140 1         2 while( my $next = $self->next ) {
141 9         13 local ( $::a, $::b ) = ( $reduced, $next );
142 9         12 $reduced = $reducer->();
143             }
144              
145 1         5 return $reduced;
146             }
147             }
148              
149 7     7 1 31 sub map($self,$map) {
  7         10  
  7         12  
  7         7  
150             return List::Lazy->new(
151             state => $self->_clone,
152             generator => sub {
153 25     25   46 while( my @next = $_->next ) {
154 29         46 @next = map { $map->() } @next;
  29         45  
155 29 100       131 return @next if @next;
156             }
157 2         4 return;
158             },
159 7         20 );
160             }
161              
162 1     1 1 1397 sub batch($self,$n) {
  1         2  
  1         2  
  1         1  
163             return List::Lazy->new(
164             state => [ $self->_clone, [] ],
165             generator => sub {
166 4     4   5 my $stash = $_->[1];
167              
168 4         10 while( my @next = $_->[0]->next ) {
169 5         8 push @$stash, @next;
170 5 100       11 if( @$stash >= $n ) {
171 2         5 return [ splice @$stash, 0, $n ];
172             }
173             }
174              
175 2 100       5 return @$stash ? [ splice @$stash ] : ();
176             },
177 1         3 );
178             };
179              
180 2     2 1 45 sub grep($self,$filter) {
  2         2  
  2         4  
  2         3  
181 14 100   14   22 $self->map(sub{ $filter->() ? $_ : () })
182 2         11 }
183              
184 2     2 1 1970 sub spy($self,$sub=undef) {
  2         3  
  2         3  
  2         3  
185 2   100 1   7 $sub ||= sub { carp $_ };
  1         22  
186 2     6   8 $self->map(sub{ $sub->(); $_ } );
  6         10  
  6         675  
187             }
188              
189 18     18   21 sub _clone($self,%args) {
  18         20  
  18         22  
  18         18  
190 18         400 return List::Lazy->new(
191             state => clone( $self->state ),
192             generator => $self->generator,
193             _next => [ $self->_next->@* ],
194             %args
195             );
196             }
197              
198 1     1 1 1213 sub until($self,$condition) {
  1         2  
  1         1  
  1         1  
199 1         1 my $done;
200             return List::Lazy->new(
201             state => $self->_clone,
202             generator => sub {
203 11 50   11   13 return () if $done;
204 11         15 my @next = $_->next;
205 11         42 my @filtered = list_before( sub { $condition->() }, @next );
  11         17  
206 11         42 $done = @filtered < @next;
207 11         12 return @filtered;
208             },
209 1         3 );
210             }
211              
212 4     4 1 56 sub append($self,@list) {
  4         6  
  4         5  
  4         6  
213              
214             return List::Lazy->new(
215 9         30 state => [ map { $_->_clone } $self, @list ],
216             generator => sub {
217 36     36   52 while(@$_) {
218 45   100     157 shift @$_ while @$_ and $_->[0]->is_done;
219 45 100       64 last unless @$_;
220 41         58 my @next = $_->[0]->next;
221 41 100       86 return @next if @next;
222             }
223 4         6 return ();
224             },
225 4         7 );
226              
227             }
228              
229 1     1 1 16 sub prepend( $self, @list ) {
  1         2  
  1         2  
  1         1  
230 1         3 push @list, $self;
231 1         2 $self = shift @list;
232 1         3 $self->append(@list);
233             }
234              
235             1;
236              
237             __END__