File Coverage

blib/lib/Math/Permute/Lists.pm
Criterion Covered Total %
statement 60 60 100.0
branch 12 12 100.0
condition 3 3 100.0
subroutine 9 9 100.0
pod 1 2 50.0
total 85 86 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             package Math::Permute::Lists;
34             our $VERSION = '20170828';
35 12     12   800506 use v5.16.0;
  12         69  
36 12     12   90 use warnings FATAL => qw(all);
  12         44  
  12         638  
37 12     12   91 use strict;
  12         31  
  12         7240  
38            
39             sub permute(&@) # Generate permutations of lists - user interface
40 25     25 0 5169 {my $s = shift; # Subroutine to call to process each permutation
41 25         100 &Permute($s, undef, @_); # Perform permutations
42             }
43            
44             sub Permute # Generate and expand permutations - private
45 169     169 1 248 {my $S = shift; # User subroutine to call to process each permutation
46 169         236 my $R = shift; # Subroutine to expand replacements
47            
48 169         259 my $Single = __PACKAGE__.'Single'; # User supplied item
49 169         237 my $Expand = __PACKAGE__.'Expand'; # Sub permutations of user items
50 169         222 my $mirror; $mirror = sub # Mirror permutation structure
51 169     169   221 {my @p; # Items to be permuted discovered at this level
52 169         282 for(@_)
53 311 100 100     937 {if (ref eq "ARRAY" or ref eq $Expand) # Array of sub items to be permuted together
54 45         132 {push @p, bless [0, bless $_, $Expand], $Single; # Not in use, sublist
55             }
56             else # A single item
57 266         664 {push @p, bless [0, $_], $Single; # Not in use, user item
58             }
59             }
60             @p # Result
61 169         483 };
  169         363  
62            
63 169         365 my $M = [&$mirror(@_)]; # Mirrors the user supplied permutation structure but with additional data
64 169         268 my @Q = (); # Permuted array = output area
65 169         228 my $N = 0; # Number of permutations encountered
66            
67 169         210 my $replace; $replace = sub # Replace sub permutations with their expansions
68 1185     1185   1911 {my @q = @_; # Fully or partially expanded row
69 1185 100       1506 if (grep {ref($_) eq $Expand} @q) # Check whether results if fully expanded yet
  6334         9912  
70 144         208 {my @p; # Prefix elements that are fully expanded
71 144         246 for(;@q;) # Remove leading block of items that do not need expansion
72 419         575 {my $q = shift @q; # Each element, leaving trailing elements
73 419 100       672 if (ref($q) ne $Expand) # Leading expanded elements
74 275         487 {push @p, $q; # Save leading expanded element
75             }
76             else # First element requiring expansion
77 144         575 {&Permute($S, sub {&$replace(@p, @_, @q);}, @$q); # Expand sub permutation and use it to expand the current row
  260         428  
78 144         315 return;
79             }
80             }
81             }
82             else # Fully expanded - call user processing routine
83 1041         1193 {++$N; # Number of permutations encountered
84 1041         1653 &$S(@q); # Pass to user
85             }
86 169         566 };
87            
88 169         248 my $permute; $permute = sub # Generate permutations
89 3136 100   3136   5003 {if (scalar(@Q) == scalar(@$M)) # Row has been generated when it has enough elements
90 1185 100       1633 {($R ? $R : $replace)->(map {$_->[1]} @Q); # Subsequent or first replacement of user data
  5662         8227  
91 1184         3506 return;
92             }
93            
94 1951         2794 my ($P) = @_; # Permutations to be performed
95 1951         2610 for my $p(@$P) # Find an item that has not been used so far in this permutation
96 9589 100       14321 {if (!$p->[0]) # Not in use
97 2967         3643 {push @Q, $p; # Place it in the next position in the output area
98 2967         3698 $p->[0] = 1; # Mark it as in use
99 2967         5215 &$permute($P); # Choose again
100 2965         3776 $p->[0] = 0; # Mark it as available
101 2965         4224 pop @Q; # Free space in output area
102             }
103             }
104 169         435 };
105            
106 169         387 &$permute($M); # Permute per user
107 168         1082 $mirror = $replace = $permute = undef; # Break memory cycles
108 168         538 $N # Return number of permutations performed
109             }
110            
111             # Export details
112            
113             require Exporter;
114            
115 12     12   115 use vars qw(@ISA @EXPORT $VERSION);
  12         25  
  12         1250  
116            
117             @ISA = qw(Exporter);
118             @EXPORT = qw(permute);
119            
120             =head1 Description
121            
122             Generate all the permutations of zero or more nested lists using the standard
123             Perl metaphor.
124            
125             C returns the number of permutations in both scalar and array
126             context.
127            
128             C is 100% Pure Perl.
129            
130             =head1 Export
131            
132             The C function is exported.
133            
134             =head1 Installation
135            
136             Standard Module::Build process for building and installing modules:
137            
138             perl Build.PL
139             ./Build
140             ./Build test
141             ./Build install
142            
143             Or, if you're on a platform (like DOS or Windows) that doesn't require
144             the "./" notation, you can do this:
145            
146             perl Build.PL
147             Build
148             Build test
149             Build install
150            
151             =head1 Author
152            
153             PhilipRBrenan@appaapps.com
154            
155             http://www.appaapps.com
156            
157             =head1 Acknowledgements
158            
159             From a suggestion by Philipp Rumpf.
160            
161             =head1 See Also
162            
163             =over
164            
165             =item L
166            
167             =item L
168            
169             =item L
170            
171             =item L
172            
173             =item L
174            
175             =item L
176            
177             =back
178            
179             =head1 Copyright
180            
181             Copyright (c) 2009 Philip R Brenan.
182            
183             This module is free software. It may be used, redistributed and/or
184             modified under the same terms as Perl itself.
185            
186             =cut