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   130880 use 5.006;
  5         20  
  5         219  
4 5     5   28 use strict;
  5         9  
  5         361  
5 5     5   28 use warnings;
  5         35  
  5         172  
6 5     5   22 no warnings qw/once void/;
  5         9  
  5         470  
7              
8             BEGIN
9             {
10 5     5   11 $List::MapMulti::AUTHORITY = 'cpan:TOBYINK';
11 5         14 $List::MapMulti::VERSION = '0.004';
12            
13             # use this module if it's installed.
14             # don't panic if it's unavailable.
15 5         14 eval {
16 5         4046 require autovivification;
17 5         5492 autovivification->unimport('warn');
18             };
19             }
20              
21 5     5   306 use Carp qw/carp croak/;
  5         9  
  5         583  
22              
23             our (@EXPORT, @EXPORT_OK, %EXPORT_TAGS);
24 5     5   28 use base qw/Exporter/;
  5         11  
  5         1055  
25             BEGIN {
26 5     5   25 @EXPORT = qw/mapm/;
27 5         13 @EXPORT_OK = (@EXPORT, qw/map_multi iterator_multi/);
28 5         865 %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 402 join(q{::}, __PACKAGE__, 'Iterator')->new(@_);
39             }
40              
41             sub map_multi (&@)
42             {
43 8     8 1 8712 my ($code, @arrays) = @_;
44 8         14 my @results;
45            
46 8 50       23 if (@arrays)
47             {
48 8         26 my $iter = iterator_multi(@arrays);
49            
50 8         14 local $_ = $iter;
51              
52             # Localise $a, $b
53 8         12 my ( $caller_a, $caller_b ) = do {
54 8         11 my $pkg = caller;
55 5     5   31 no strict 'refs';
  5         56  
  5         495  
56 8         610 \*{$pkg.'::a'}, \*{$pkg.'::b'};
  8         30  
  8         25  
57             };
58            
59 8         143 while (my @values = $iter->())
60             {
61 5     5   25 no strict 'refs';
  5         9  
  5         898  
62 676         1388 (*$caller_a, *$caller_b) = \( @values[0, 1] );
63 676         1452 push @results, $code->(@values);
64             }
65             }
66            
67 8 50       235 wantarray ? @results : scalar(@results);
68             }
69              
70             sub mapm (&@); *mapm = \&map_multi;
71              
72             package List::MapMulti::Iterator;
73              
74 5     5   25 use strict;
  5         8  
  5         146  
75 5     5   24 use warnings;
  5         8  
  5         194  
76 5     5   31 no warnings qw/once void/;
  5         16  
  5         224  
77              
78 5     5   32 use Carp qw/carp croak/;
  5         9  
  5         954  
79              
80             use overload
81 732     732   5541 '&{}' => sub { my $self = shift; sub { $self->next } },
  732         2077  
  732         1256  
82 0     0   0 '@{}' => sub { my $self = shift; [ $self->current ] },
  0         0  
83 5     5   9382 ;
  5         7289  
  5         57  
84              
85             BEGIN
86             {
87 5     5   549 $List::MapMulti::Iterator::AUTHORITY = 'cpan:TOBYINK';
88 5         10 $List::MapMulti::Iterator::VERSION = '0.004';
89            
90 5         33 autovivification->unimport('warn');
91             }
92              
93             sub new
94             {
95 10     10   150 my ($class, @arrays) = @_;
96            
97 10         35 _array_check(\@arrays);
98            
99             my $self = bless {
100             arrays => \@arrays,
101 24         51 lengths => [ map { ;scalar @$_ } @arrays ],
102 10         26 next_indices => [ map { ;0 } @arrays ],
  24         92  
103             current_indices => undef,
104             last => 0,
105             }, $class;
106            
107 10         23 for my $arr (@arrays)
108             {
109 24 100       63 $self->{'last'}++ unless scalar @$arr;
110             }
111            
112 10         28 return $self;
113             }
114              
115             sub _array_check
116             {
117 10     10   16 my ($arrays) = @_;
118 10         83 my $callsub = [caller(1)]->[3];
119              
120 10 50       1272 if (warnings::enabled('misc'))
121             {
122 10 50       40 carp "no arrayrefs were passed to $callsub"
123             unless @$arrays;
124             }
125            
126 24         80 croak "non-arrayref passed to $callsub"
127 10 50       27 if grep { ref ne 'ARRAY' } @$arrays;
128             }
129              
130             sub _increment_indices
131             {
132 723     723   787 my ($indices, $lengths) = @_;
133 723         779 my $inc = $#$indices;
134            
135 723         671 while (1)
136             {
137 1042 100       1679 if ($inc < 0)
138             {
139 8         13 @$indices = ();
140 8         32 return;
141             }
142            
143 1034         962 $indices->[$inc]++;
144 1034 100       1535 if ($indices->[$inc] >= $lengths->[$inc])
145             {
146 319         298 $indices->[$inc] = 0;
147 319         303 $inc--;
148             }
149             else
150             {
151 715         1525 return $indices;
152             }
153             }
154             }
155              
156             sub next
157             {
158 732     732   694 my $self = shift;
159            
160 732 100       1454 return if $self->{last};
161            
162 723         1669 $self->{current_indices} = [ $self->next_indices ];
163            
164 1966         4372 my @values = map
165 723         1290 { $self->{arrays}[$_][$self->{current_indices}[$_]] }
166 723         1088 0 .. $#{$self->{arrays}};
167            
168 723         1626 $self->{last} = !_increment_indices($self->{next_indices}, $self->{lengths});
169            
170 723         2446 return @values;
171             }
172              
173             sub next_indices
174             {
175 724     724   687 my $self = shift;
176            
177 724 100       1202 if (@_)
178             {
179 1         2 $self->{next_indices} = [@_[ 0 .. $#{$self->{arrays}} ]];
  1         5  
180             }
181            
182 724         601 @{ $self->{next_indices} };
  724         2058  
183             }
184              
185             sub current
186             {
187 2     2   14 my $self = shift;
188            
189 2 100       7 if (@_)
190             {
191 1         3 my @ix = $self->current_indices;
192 1         4 for my $i (0 .. $#_)
193             {
194 2         8 $self->{arrays}[$i][ $ix[$i] ] = $_[$i];
195             }
196             }
197            
198 4         22 my @values = map
199 2         6 { $self->{arrays}[$_][$self->{current_indices}[$_]] }
200 2         3 0 .. $#{$self->{arrays}};
201             }
202              
203             sub current_indices
204             {
205 2     2   11 @{ (shift)->{current_indices} };
  2         12  
206             }
207              
208             __PACKAGE__
209             __END__