File Coverage

blib/lib/Math/Permute/Lists.pm
Criterion Covered Total %
statement 61 61 100.0
branch 12 12 100.0
condition 3 3 100.0
subroutine 9 9 100.0
pod 1 2 50.0
total 86 87 98.8


line stmt bran cond sub pod time code
1             =head1 Name
2            
3             Math::Permute::Lists - Generate all the permutations of zero or more nested lists.
4            
5             =head1 Synopsis
6            
7             use Math::Permute::Lists;
8            
9             permute {say "@_"} [1,2],[3,4];
10            
11             # 1 2 3 4
12             # 1 2 4 3
13             # 2 1 3 4
14             # 2 1 4 3
15             # 3 4 1 2
16             # 3 4 2 1
17             # 4 3 1 2
18             # 4 3 2 1
19            
20             permute {say "@_"} 1,[2,[3,4]];
21            
22             # 1 2 3 4
23             # 1 2 4 3
24             # 1 3 4 2
25             # 1 4 3 2
26             # 2 3 4 1
27             # 2 4 3 1
28             # 3 4 2 1
29             # 4 3 2 1
30            
31             =cut
32            
33 12     12   661329 use strict;
  12         30  
  12         488  
34            
35             package Math::Permute::Lists;
36            
37 12     12   137 use warnings FATAL => qw(all);
  12         26  
  12         467  
38 12     12   60 use strict;
  12         24  
  12         5270  
39            
40             sub permute(&@) # Generate permutations of lists - user interface
41 24     24 0 4152 {my $s = shift; # Subroutine to call to process each permutation
42 24         76 &Permute($s, undef, @_); # Perform permutations
43             }
44            
45             sub Permute # Generate and expand permutations - private
46 162     162 1 237 {my $S = shift; # User subroutine to call to process each permutation
47 162         208 my $R = shift; # Subroutine to expand replacements
48            
49 162         241 my $Single = __PACKAGE__.'Single'; # User supplied item
50 162         215 my $Expand = __PACKAGE__.'Expand'; # Sub permutations of user items
51 162         208 my $mirror; $mirror = sub # Mirror permutation structure
52 162     162   204 {my @p; # Items to be permuted discovered at this level
53 162         280 for(@_)
54 297 100 100     874 {if (ref eq "ARRAY" or ref eq $Expand) # Array of sub items to be permuted together
55 43         141 {push @p, bless [0, bless $_, $Expand], $Single; # Not in use, sublist
56             }
57             else # A single item
58 254         620 {push @p, bless [0, $_], $Single; # Not in use, user item
59             }
60             }
61             @p # Result
62 162         436 };
  162         309  
63            
64 162         310 my $M = [&$mirror(@_)]; # Mirrors the user supplied permutation structure but with additional data
65 162         255 my @Q = (); # Permuted array = output area
66 162         210 my $N = 0; # Number of permutations encountered
67            
68 162         195 my $replace; $replace = sub # Replace sub permutations with their expansions
69 1171     1171   1769 {my @q = @_; # Fully or partially expanded row
70 1171 100       1569 if (grep {ref($_) eq $Expand} @q) # Check whether results if fully expanded yet
  6286         10018  
71 138         182 {my @p; # Prefix elements that are fully expanded
72 138         254 for(;@q;) # Remove leading block of items that do not need expansion
73 405         571 {my $q = shift @q; # Each element, leaving trailing elements
74 405 100       665 if (ref($q) ne $Expand) # Leading expanded elements
75 267         517 {push @p, $q; # Save leading expanded element
76             }
77             else # First element requiring expansion
78 138         593 {&Permute($S, sub {&$replace(@p, @_, @q);}, @$q); # Expand sub permutation and use it to expand the current row
  248         433  
79 138         344 return;
80             }
81             }
82             }
83             else # Fully expanded - call user processing routine
84 1033         1222 {++$N; # Number of permutations encountered
85 1033         1655 &$S(@q); # Pass to user
86             }
87 162         517 };
88            
89 162         225 my $permute; $permute = sub # Generate permutations
90 3101 100   3101   4993 {if (scalar(@Q) == scalar(@$M)) # Row has been generated when it has enough elements
91 1171 100       1617 {($R ? $R : $replace)->(map {$_->[1]} @Q); # Subsequent or first replacement of user data
  5634         8741  
92 1170         3562 return;
93             }
94            
95 1930         2667 my ($P) = @_; # Permutations to be performed
96 1930         2636 for my $p(@$P) # Find an item that has not been used so far in this permutation
97 9547 100       15113 {if (!$p->[0]) # Not in use
98 2939         3614 {push @Q, $p; # Place it in the next position in the output area
99 2939         3457 $p->[0] = 1; # Mark it as in use
100 2939         5429 &$permute($P); # Choose again
101 2937         3710 $p->[0] = 0; # Mark it as available
102 2937         4017 pop @Q; # Free space in output area
103             }
104             }
105 162         474 };
106            
107 162         364 &$permute($M); # Permute per user
108 161         1041 $mirror = $replace = $permute = undef; # Break memory cycles
109 161         493 $N # Return number of permutations performed
110             }
111            
112             # Export details
113            
114             require 5;
115             require Exporter;
116            
117 12     12   82 use vars qw(@ISA @EXPORT $VERSION);
  12         21  
  12         1051  
118            
119             @ISA = qw(Exporter);
120             @EXPORT = qw(permute);
121             $VERSION = '20170808';
122            
123             =head1 Description
124            
125             Generate all the permutations of zero or more nested lists using the standard
126             Perl metaphor.
127            
128             C returns the number of permutations in both scalar and array
129             context.
130            
131             C is 100% Pure Perl.
132            
133             =head1 Export
134            
135             The C function is exported.
136            
137             =head1 Installation
138            
139             Standard Module::Build process for building and installing modules:
140            
141             perl Build.PL
142             ./Build
143             ./Build test
144             ./Build install
145            
146             Or, if you're on a platform (like DOS or Windows) that doesn't require
147             the "./" notation, you can do this:
148            
149             perl Build.PL
150             Build
151             Build test
152             Build install
153            
154             =head1 Author
155            
156             PhilipRBrenan@appaapps.com
157            
158             http://www.appaapps.com
159            
160             =head1 Acknowledgements
161            
162             From a suggestion by Philipp Rumpf.
163            
164             =head1 See Also
165            
166             =over
167            
168             =item L
169            
170             =item L
171            
172             =item L
173            
174             =item L
175            
176             =item L
177            
178             =item L
179            
180             =back
181            
182             =head1 Copyright
183            
184             Copyright (c) 2009 Philip R Brenan.
185            
186             This module is free software. It may be used, redistributed and/or
187             modified under the same terms as Perl itself.
188            
189             =cut