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