File Coverage

blib/lib/List/MapMulti.pm
Criterion Covered Total %
statement 117 119 98.3
branch 17 22 77.2
condition n/a
subroutine 26 27 96.3
pod 2 2 100.0
total 162 170 95.2


line stmt bran cond sub pod time code
1             package List::MapMulti;
2              
3 5     5   129383 use 5.006;
  5         19  
  5         203  
4 5     5   30 use strict;
  5         10  
  5         193  
5 5     5   40 use warnings;
  5         20  
  5         167  
6 5     5   24 no warnings qw/once void/;
  5         10  
  5         473  
7              
8             BEGIN
9             {
10 5     5   10 $List::MapMulti::AUTHORITY = 'cpan:TOBYINK';
11 5         7 $List::MapMulti::VERSION = '0.003';
12            
13             # use this module if it's installed.
14             # don't panic if it's unavailable.
15 5         10 eval {
16 5         14539 require autovivification;
17 5         5454 autovivification->unimport('warn');
18             };
19             }
20              
21 5     5   259 use Carp qw/carp croak/;
  5         10  
  5         598  
22              
23             our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS);
24 5     5   28 use base qw/Exporter/;
  5         10  
  5         923  
25             BEGIN {
26 5     5   16 @EXPORT = qw/mapm/;
27 5         18 @EXPORT_OK = (@EXPORT, qw/map_multi iterator_multi/);
28 5         1021 %EXPORT_TAGS = (
29             'all' => \@EXPORT_OK,
30             'standard' => \@EXPORT,
31             'default' => \@EXPORT,
32             'nothing' => [],
33             );
34             }
35              
36             sub iterator_multi
37             {
38 9     9 1 190 join(q{::}, __PACKAGE__, 'Iterator')->new(@_);
39             }
40              
41             sub map_multi (&@)
42             {
43 8     8 1 14840 my ($code, @arrays) = @_;
44 8         18 my @results;
45            
46 8 50       31 if (@arrays)
47             {
48 8         27 my $iter = iterator_multi(@arrays);
49            
50 8         11 local $_ = $iter;
51              
52             # Localise $a, $b
53 8         13 my ( $caller_a, $caller_b ) = do {
54 8         15 my $pkg = caller;
55 5     5   29 no strict 'refs';
  5         66  
  5         462  
56 8         10 \*{$pkg.'::a'}, \*{$pkg.'::b'};
  8         632  
  8         42  
57             };
58            
59 8         150 while (my @values = $iter->())
60             {
61 5     5   32 no strict 'refs';
  5         9  
  5         1451  
62 676         1438 (*$caller_a, *$caller_b) = \( @values[0, 1] );
63 676         1531 push @results, $code->(@values);
64             }
65             }
66            
67 8 50       276 wantarray ? @results : scalar(@results);
68             }
69              
70             sub mapm (&@); *mapm = \&map_multi;
71              
72             package List::MapMulti::Iterator;
73              
74 5     5   27 use strict;
  5         8  
  5         139  
75 5     5   23 use warnings;
  5         9  
  5         189  
76 5     5   23 no warnings qw/once void/;
  5         14  
  5         196  
77              
78 5     5   29 use Carp qw/carp croak/;
  5         7  
  5         683  
79              
80             use overload
81 732     732   5244 '&{}' => sub { my $self = shift; sub { $self->next } },
  732         2100  
  732         1280  
82 0     0   0 '@{}' => sub { my $self = shift; [ $self->current ] },
  0         0  
83 5     5   9194 ;
  5         6430  
  5         54  
84              
85             BEGIN
86             {
87 5     5   514 $List::MapMulti::Iterator::AUTHORITY = 'cpan:TOBYINK';
88 5         11 $List::MapMulti::Iterator::VERSION = '0.003';
89            
90 5         33 autovivification->unimport('warn');
91             }
92              
93             sub new
94             {
95 10     10   154 my ($class, @arrays) = @_;
96            
97 10         42 _array_check(\@arrays);
98            
99             my $self = bless {
100             arrays => \@arrays,
101 24         52 lengths => [ map { ;scalar @$_ } @arrays ],
102 10         27 next_indices => [ map { ;0 } @arrays ],
  24         106  
103             current_indices => undef,
104             last => 0,
105             }, $class;
106            
107 10         27 for my $arr (@arrays)
108             {
109 24 100       62 $self->{'last'}++ unless scalar @$arr;
110             }
111            
112 10         28 return $self;
113             }
114              
115             sub _array_check
116             {
117 10     10   19 my ($arrays) = @_;
118 10         92 my $callsub = [caller(1)]->[3];
119              
120 10 50       1574 if (warnings::enabled('misc'))
121             {
122 10 50       43 carp "no arrayrefs were passed to $callsub"
123             unless @$arrays;
124             }
125            
126 24         78 croak "non-arrayref passed to $callsub"
127 10 50       24 if grep { ref ne 'ARRAY' } @$arrays;
128             }
129              
130             sub _increment_indices
131             {
132 723     723   825 my ($indices, $lengths) = @_;
133 723         843 my $inc = $#$indices;
134            
135 723         724 while (1)
136             {
137 1042 100       1763 if ($inc < 0)
138             {
139 8         36 @$indices = ();
140 8         27 return;
141             }
142            
143 1034         1062 $indices->[$inc]++;
144 1034 100       1696 if ($indices->[$inc] >= $lengths->[$inc])
145             {
146 319         340 $indices->[$inc] = 0;
147 319         338 $inc--;
148             }
149             else
150             {
151 715         1649 return $indices;
152             }
153             }
154             }
155              
156             sub next
157             {
158 732     732   724 my $self = shift;
159            
160 732 100       1564 return if $self->{last};
161            
162 723         1284 $self->{current_indices} = [ $self->next_indices ];
163            
164 1966         4808 my @values = map
165 723         1377 { $self->{arrays}[$_][$self->{current_indices}[$_]] }
166 723         1121 0 .. $#{$self->{arrays}};
167            
168 723         1764 $self->{last} = !_increment_indices($self->{next_indices}, $self->{lengths});
169            
170 723         2687 return @values;
171             }
172              
173             sub next_indices
174             {
175 724     724   691 my $self = shift;
176            
177 724 100       1227 if (@_)
178             {
179 1         2 $self->{next_indices} = [@_[ 0 .. $#{$self->{arrays}} ]];
  1         5  
180             }
181            
182 724         636 @{ $self->{next_indices} };
  724         2156  
183             }
184              
185             sub current
186             {
187 2     2   12 my $self = shift;
188            
189 2 100       5 if (@_)
190             {
191 1         4 my @ix = $self->current_indices;
192 1         4 for my $i (0 .. $#_)
193             {
194 2         7 $self->{arrays}[$i][ $ix[$i] ] = $_[$i];
195             }
196             }
197            
198 4         18 my @values = map
199 2         7 { $self->{arrays}[$_][$self->{current_indices}[$_]] }
200 2         3 0 .. $#{$self->{arrays}};
201             }
202              
203             sub current_indices
204             {
205 2     2   8 @{ (shift)->{current_indices} };
  2         10  
206             }
207              
208             __PACKAGE__
209             __END__