File Coverage

blib/lib/Math/Cartesian/Product.pm
Criterion Covered Total %
statement 59 59 100.0
branch 29 30 96.6
condition n/a
subroutine 8 8 100.0
pod 0 1 0.0
total 96 98 97.9


line stmt bran cond sub pod time code
1             =head1 Name
2              
3             Math::Cartesian::Product - Generate the Cartesian product of zero or more lists.
4              
5             =head1 Synopsis
6              
7             use Math::Cartesian::Product;
8              
9             cartesian {print "@_\n"} [qw(a b c)], [1..2];
10              
11             # a 1
12             # a 2
13             # b 1
14             # b 2
15             # c 1
16             # c 2
17              
18             cartesian {print "@_\n"} ([0..1]) x 8;
19              
20             # 0 0 0 0 0 0 0 0
21             # 0 0 0 0 0 0 0 1
22             # 0 0 0 0 0 0 1 0
23             # ...
24             # 1 1 1 1 1 1 1 0
25             # 1 1 1 1 1 1 1 1
26              
27             print "@$_\n" for
28             cartesian {"@{[reverse @_]}" eq "@_"}
29             ([' ', '*']) x 8;
30              
31             # * *
32             # * *
33             # * * * *
34             # * *
35             # * * * *
36             # * * * *
37             # * * * * * *
38             # * *
39             # * * * *
40             # * * * *
41             # * * * * * *
42             # * * * *
43             # * * * * * *
44             # * * * * * *
45             # * * * * * * * *
46              
47             =cut
48              
49             package Math::Cartesian::Product;
50              
51 1     1   24147 use Carp;
  1         2  
  1         117  
52 1     1   6 use strict;
  1         2  
  1         656  
53              
54             sub cartesian(&@) # Generate the Cartesian product of zero or more lists
55 75     75 0 20681 {my $s = shift; # Subroutine to call to process each element of the product
56              
57 75         164 my @C = @_; # Lists to be multiplied
58 75         99 my @c = (); # Current element of Cartesian product
59 75         84 my @P = (); # Cartesian product
60 75         88 my $n = 0; # Number of elements in product
61              
62             # return 0 if @C == 0; # Empty product per Philipp Rumpf
63              
64 75 50       137 @C == grep {ref eq 'ARRAY'} @C or croak("Arrays of things required by cartesian");
  300         633  
65              
66             # Generate each Cartesian product when there are no prior Cartesian products.
67             # The first variant builds the results array, the second does not per Justin Case
68              
69 75         74 my $p; $p = wantarray() ? sub
70 560 100   560   815 {if (@c < @C)
71 277         232 {for(@{$C[@c]})
  277         538  
72 549         565 {push @c, $_;
73 549         681 &$p();
74 549         2777 pop @c;
75             }
76             }
77             else
78 283         555 {my $p = [@c];
79 283 100       525 push @P, bless $p if &$s(@$p);
80             }
81             } : sub # List not required per Justin Case
82 6820057 100   6820057   8414887 {if (@c < @C)
83 2162838         1535264 {for(@{$C[@c]})
  2162838         3153390  
84 6820004         5532081 {push @c, $_;
85 6820004         7090727 &$p();
86 6820004         15928408 pop @c;
87             }
88             }
89             else
90 4657219 100       6046115 {++$n if &$s(@c);
91             }
92 75 100       332 };
93              
94             # Generate each Cartesian product allowing for prior Cartesian products.
95              
96 75         93 my $q; $q = wantarray() ? sub
97 903 100   903   1452 {if (@c < @C)
98 126         115 {for(@{$C[@c]})
  126         259  
99 897         914 {push @c, $_;
100 897         1149 &$q();
101 897         7154 pop @c;
102             }
103             }
104             else
105 777 100       806 {my $p = [map {ref eq __PACKAGE__ ? @$_ : $_} @c];
  2283         5642  
106 777 100       2298 push @P, bless $p if &$s(@$p);
107             }
108             } : sub # List not required per Justin Case
109 623 100   623   920 {if (@c < @C)
110 89         110 {for(@{$C[@c]})
  89         181  
111 618         622 {push @c, $_;
112 618         768 &$q();
113 618         3320 pop @c;
114             }
115             }
116             else
117 534 100       560 {++$n if &$s(map {ref eq __PACKAGE__ ? @$_ : $_} @c);
  1554 100       3738  
118             }
119 75 100       280 };
120              
121             # Determine optimal method of forming Cartesian products for this call
122              
123 75 100       107 if (grep {grep {ref eq __PACKAGE__} @$_} @C)
  300         345  
  842         1184  
124 11         25 {&$q
125             }
126             else
127 64         112 {&$p
128             }
129              
130 75         172 $p = $q = undef; # Break memory loops per Philipp Rumpf
131 75 100       1502 wantarray() ? @P : $n # Product or count per Justin Case
132             }
133              
134             # Export details
135              
136             require 5;
137             require Exporter;
138              
139 1     1   7 use vars qw(@ISA @EXPORT $VERSION);
  1         19  
  1         150  
140              
141             @ISA = qw(Exporter);
142             @EXPORT = qw(cartesian);
143             $VERSION = '1.009'; # Tuesday 18 Aug 2015
144              
145             =head1 Description
146              
147             Generate the Cartesian product of zero or more lists.
148              
149             Given two lists, say: [a,b] and [1,2,3], the Cartesian product is the
150             set of all ordered pairs:
151              
152             (a,1), (a,2), (a,3), (b,1), (b,2), (b,3)
153              
154             which select their first element from all the possibilities listed in
155             the first list, and select their second element from all the
156             possibilities in the second list.
157              
158             The idea can be generalized to n-tuples selected from n lists where all the
159             elements of the first list are combined with all the elements of the second
160             list, the results of which are then combined with all the member of the third
161             list and so on over all the input lists.
162              
163             It should be noted that Cartesian product of one or more lists where one or
164             more of the lists are empty (representing the empty set) is the empty set
165             and thus has zero members; and that the Cartesian product of zero lists is a
166             set with exactly one member, namely the empty set.
167              
168             C<cartesian()> takes the following parameters:
169              
170             1. A block of code to process each n-tuple. this code should return true
171             if the current n-tuple should be included in the returned value of the
172             C<cartesian()> function, otherwise false.
173              
174             2. Zero or more lists.
175              
176             C<cartesian()> returns an array of references to all the n-tuples selected by
177             the code block supplied as parameter 1 if called in list context, else it
178             returns a count of the selected n-tuples.
179              
180             C<cartesian()> croaks if you try to form the Cartesian product of
181             something other than lists of things or prior Cartesian products.
182              
183             The cartesian product of lists A,B,C is associative, that is:
184              
185             (A X B) X C = A X (B X C)
186              
187             C<cartesian()> respects associativity by allowing you to include a
188             Cartesian product produced by an earlier call to C<cartesian()> in the
189             set of lists whose Cartesian product is to be formed, at the cost of a
190             performance penalty if this option is chosen.
191              
192             use Math::Cartesian::Product;
193              
194             my $a = [qw(a b)];
195             my $b = [cartesian {1} $a, $a];
196             cartesian {print "@_\n"} $b, $b;
197              
198             # a a a a
199             # a a a b
200             # a a b a
201             # ...
202              
203             C<cartesian()> is easy to use and fast. It is written in 100% Pure Perl.
204              
205             =head1 Export
206              
207             The C<cartesian()> function is exported.
208              
209             =head1 Installation
210              
211             Standard Module::Build process for building and installing modules:
212              
213             perl Build.PL
214             ./Build
215             ./Build test
216             ./Build install
217              
218             Or, if you're on a platform (like DOS or Windows) that doesn't require
219             the "./" notation, you can do this:
220              
221             perl Build.PL
222             Build
223             Build test
224             Build install
225              
226             =head1 Author
227              
228             Philip R Brenan at gmail dot com
229              
230             http://www.appaapps.com
231              
232             =head1 Acknowledgements
233              
234             With much help and good natured advice from Philipp Rumpf and Justin Case to
235             whom I am indebted.
236              
237             =head1 See Also
238              
239             =over
240              
241             =item L<Math::Disarrange::List>
242              
243             =item L<Math::Permute::List>
244              
245             =item L<Math::Permute::Lists>
246              
247             =item L<Math::Permute::Partitions>
248              
249             =item L<Math::Subsets::List>
250              
251             =item L<Math::Transform::List>
252              
253             =back
254              
255             =head1 Copyright
256              
257             Copyright (c) 2009-2015 Philip R Brenan.
258              
259             This module is free software. It may be used, redistributed and/or
260             modified under the same terms as Perl itself.
261              
262             =cut