| 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; | ||||||