File Coverage

blib/lib/Math/Transform/List.pm
Criterion Covered Total %
statement 63 63 100.0
branch 21 28 75.0
condition 6 15 40.0
subroutine 3 3 100.0
pod 0 1 0.0
total 93 110 84.5


line stmt bran cond sub pod time code
1             =head1 Name
2              
3             Math::Transform::List - Generate specified transformations of a list.
4              
5             =head1 Synopsis
6              
7             use Math::Transform::List;
8              
9             transform {say "@_"} [qw(a b c)], [1..3];
10              
11             # a b c
12             # b c a
13             # c b a
14              
15             transform {say "@_"} [qw(a b c d)], [1..2], [3..4];
16              
17             # a b c d
18             # b a c d
19             # a b d c
20             # b a d c
21              
22             transform {say "@_"} [qw(a b c d)], [[1, 3], [2, 4]];
23              
24             # a b c d
25             # c d a b
26              
27             =cut
28              
29 3     3   94803 use strict;
  3         7  
  3         2814  
30              
31             package Math::Transform::List;
32              
33              
34 14     14 0 8363 sub transform(&$@)
35             {my $s = shift; # Subroutine to process each transformation
36              
37              
38             # List to be transformed
39              
40 14         23 my $L = shift;
41 14 50       54 ref($L) or die "transform(2): $L not a reference";
42 14         17 {my @L = @$L;
  14         42  
43 14 100       72 if (@L == 0)
  1 100       7  
44 1         3 {return 0
45             }
46             elsif (@L == 1)
47             {&$s(@L);
48 1         7 return 1
49             }
50             }
51 12         32 my ($N1, $N) = (scalar(@$L)-1, scalar(@$L));
52              
53              
54             # Transformations - check
55              
56 12         40 for(0..$#_)
  18         51  
57 18         21 {my @p = @{$_[$_]};
58 18         23 my $p = grep {ref($_)} @{$_[$_]};
  72         99  
  18         33  
59 18 50 66     91 $p == 0 or $p == @p or die "Transform, transformation(@p) must be all references to cycles or one cycle";
60             }
61              
62            
63             # Transformations - load
64            
65 12         21 my $T;
66 12 50       30 for(0..$#_)
  18         59  
67             {ref($_[$_]) or die "transform(".(3+$_).") ".($_[$_])." not a reference";
68 18         24 my @P = @{$_[$_]};
  18         47  
69 18         113 for my $P(0..$#P)
  72         78  
70             {my $p = $P[$P];
71 72 100       97 if (ref($p))
  3         7  
72 69 50 33     535 {my @Q = @$p;
      33        
73 3         5 for my $Q(0..$#Q)
  8         7  
74             {my $q = $Q[$Q];
75 8 50 33     62 ("$q" =~ /\A\d+\Z/ and $q > 0 and $q <= $N) or die "transform(".($_+3)."->$P->$Q): $q not a number between 1 and $N";
      33        
76 8 100       14 if ($Q)
  5         7  
77             {my $q1 = $Q[$Q-1];
78 5 50       11 !defined($T->[$_][$q1-1]) or die "transform(".($_+3)."->$P->$Q): transformation from $q1 to $q already defined";
79 5         11 $T->[$_][$q1-1] = $q-1;
80             }
81             }
82 3         7 $T->[$_][$Q[-1]-1] = $Q[0]-1;
83             }
84             else
85             {("$p" =~ /\A\d+\Z/ and $p > 0 and $p <= $N) or die "transform(".($_+3)."->$P): $p not a number between 1 and $N";
86 69 100       121 if ($P)
  53         60  
87             {my $p1 = $P[$P-1];
88 53 50       109 !defined($T->[$_][$p1]) or die "transform(".($_+3)."->$P): transformation from $p1 to $p already defined";
89 53         111 $T->[$_][$p1-1] = $p-1;
90             }
91             }
92             }
93 18         1318239 $T->[$_][$P[-1]-1] = $P[0]-1;
94             }
95              
96              
97             # Set unset transforms
98              
99 12 100       45 for my $a(0..$#_)
  93         215  
100 18         32 {for my $b(0..$N1)
101             {$T->[$a][$b] = $b unless defined $T->[$a][$b];
102             }
103             }
104              
105              
106             # Initialize transformer
107              
108 12         61 my @T = ([0..$N1]); # Transforms stack
109 12         19 my $S; # Transforms already processed
110              
111              
112             # Generate transformations
113              
114 12         20 my $n = 0;
115 12         34 for(;@T;)
  279         350  
116             {my $a = pop @T;
117 279         369 for my $b(@$T)
  2978         4519  
118 511         742 {my @C = map {$b->[$a->[$_]]} 0..$N1;
119              
120 511 100       3771 unless ($S->{"@C"}++)
  267         717  
121             {push @T, [@C];
122 267         440 &$s(map {$L->[$C[$_]]} 0..$N1);
  1807         2995  
123 267         2128 $n++;
124             }
125             }
126             }
127              
128            
129 12         400624 $n # Number of transformations
130             }
131              
132              
133             # Export details
134            
135             require 5;
136             require Exporter;
137              
138 3     3   22 use vars qw(@ISA @EXPORT $VERSION);
  3         6  
  3         380  
139              
140             @ISA = qw(Exporter);
141             @EXPORT = qw(transform);
142             $VERSION = '1.005'; # Monday 26 Jan 2015
143              
144             =head1 Description
145              
146             Generate and process all the all the transformations of a list using the
147             standard Perl metaphor.
148              
149             C returns the number of transformations in both scalar and
150             array context.
151              
152             C is easy to use and fast. It is written in 100% Pure Perl.
153              
154             Please note that the order in which the transformations are generated is not
155             guaranteed, so please do not rely on it.
156              
157             The parameters to C are:
158              
159             1: The code to be executed for each transformation.
160              
161             2: A reference to the list to be transformed. This list is transformed
162             as specified by the transformations. Each transformation of the list is
163             handed to the code supplied in parameter 1 to be processed.
164              
165             3: One or more transformations to be applied to the list. The
166             transformations are applied repeatedly in all orders until no new
167             transformations of the list are generated. Each new transformation of the
168             list is handed to the code supplied in parameter 1 for processing.
169              
170             Transformations are represented as permutations in cyclic format based from
171             1 not 0. Two representations can be used to specify transformations.
172              
173             3a: Single cycle.
174            
175             [1,2,3]
176              
177             The first element of the list will be replaced by the second, the second by
178             the third, and the third by the first.
179              
180             3a: Multi cycle.
181            
182             [[1,3], [2,4]]
183              
184             The first element of the list will be replaced by the third and vice versa,
185             while simultaneously the second element is replaced by the fourth and vice
186             versa.
187              
188             transform {say "@_"} [qw(a b c d)], [[1, 3], [2, 4]];
189              
190             # a b c d
191             # c d a b
192              
193             If you want to produce all possible transformations of a list you should
194             consider L as it is faster and easier to use than the
195             equivalent:
196              
197             transform {} [1..$n], [1,2], [1..$n];
198              
199             =head1 Export
200              
201             The C function is exported.
202              
203             =head1 Installation
204              
205             Standard Module::Build process for building and installing modules:
206              
207             perl Build.PL
208             ./Build
209             ./Build test
210             ./Build install
211              
212             Or, if you're on a platform (like DOS or Windows) that doesn't require the
213             "./" notation, you can do this:
214              
215             perl Build.PL
216             Build
217             Build test
218             Build install
219              
220             =head1 Author
221              
222             PhilipRBrenan@appaapps.com
223              
224             http://www.appaapps.com
225              
226             =head1 Acknowledgements
227              
228             With much help and good natured advice from Philipp Rumpf to whom I am
229             indebted.
230              
231             =head1 See Also
232              
233             =over
234              
235             =item L
236              
237             =item L
238              
239             =item L
240              
241             =item L
242              
243             =back
244              
245             =head1 Copyright
246              
247             Copyright (c) 2009 Philip R Brenan.
248              
249             This module is free software. It may be used, redistributed and/or modified
250             under the same terms as Perl itself.
251              
252             =cut