File Coverage

blib/lib/List/Comprehensions.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package List::Comprehensions;
2 1     1   34337 use warnings;
  1         3  
  1         41  
3 1     1   6 use Carp;
  1         2  
  1         106  
4              
5             # for comp2
6 1     1   490 use Alias qw(attr);
  0            
  0            
7             use Array::RefElem qw(av_push);
8             use PadWalker qw(peek_my);
9              
10             require Exporter;
11             @ISA = qw(Exporter);
12             @EXPORT = qw(comp1 comp2 P PF);
13              
14             $VERSION = 0.13;
15              
16             =head1 NAME
17              
18             List::Comprehensions - allows for list comprehensions in Perl.
19              
20             =head1 SYNOPSIS
21              
22             use List::Comprehensions;
23             use warnings;
24            
25             my @res = ();
26              
27             @res = comp1 { [ @_ ] } [0..4], [0..4], [0..4];
28              
29             no warnings 'once';
30             @res = comp2 { [$i, $j, $k] }
31             i => [0..4],
32             j => [0..4],
33             k => [0..4];
34              
35             # if strict 'vars' is on, use lexicals. eg:
36             use strict 'vars';
37            
38             my ($i, $j, $k);
39             @res = comp2 { [$i, $j, $k] }
40             i => [0..4],
41             j => [0..4],
42             k => [0..4];
43            
44             # each being less efficient but equivelant to
45              
46             @res = ();
47             for $i ( 0..4 ) {
48             for $j ( 0..4 ) {
49             for $k ( 0..4 ) {
50             push @res, [$i, $j, $k];
51             }
52             }
53             }
54              
55             =head1 FUNCTIONS
56              
57             =over 4
58              
59             =cut
60              
61             sub min_length_of {
62             my $min = scalar( @{$_[0]} );
63              
64             my ($i, $len);
65             for $i ( 1..$#_ ) {
66             $len = scalar( @{$_[$i]} );
67             $min = $len if $len < $min;
68             }
69              
70             return $min;
71             }
72              
73             sub zipn_flat {
74             my @ret = ();
75             my $len = $#_;
76             my $min = min_length_of @_;
77            
78             my ($n, $i);
79             for $n ( 0..($min - 1) ) {
80             for $i ( 0..$len ) {
81             push @ret, $_[$i]->[$n];
82             }
83             }
84              
85             return \@ret;
86             }
87              
88             =item B
89              
90             For parallel comprehensions. ( flat zips according to minimal length )
91             eg: PF( [0..5], ['a'..'z'] )
92             is: [ 0, 'a', 1,'b' ... 5,'f' ]
93              
94             =cut
95              
96             sub PF($$;@) {
97             return zipn_flat(@_);
98             }
99              
100             sub zipn {
101             my @ret = ();
102             my $len = $#_;
103             my $min = min_length_of @_;
104            
105             my ($n, $i);
106             for $n ( 0..($min - 1) ) {
107             $ret[$n] = [];
108             for $i ( 0..$len ) {
109             push @{$ret[$n]}, $_[$i]->[$n];
110             }
111             }
112              
113             return \@ret;
114             }
115              
116             =item B
117              
118             For parallel comprehensions. ( zips according to minimal length )
119             eg: P( [0..5], ['a'..'z'] )
120             is: [ [0,'a'], [1,'b'] ... [5,'f'] ]
121              
122             =cut
123              
124             sub P(@) {
125             return zipn(@_);
126             }
127              
128             sub run {
129             my $i = shift;
130             my $arg;
131             if( $i + 1 <= $#sets ) {
132             for $arg ( @{$sets[$i]} ) {
133             $args[$i] = $arg;
134             run($i + 1);
135             }
136             } else {
137             SET:
138             for $arg ( @{$sets[$i]} ) {
139             $args[$i] = $arg;
140              
141             for $guard ( @guards ) {
142             &$guard(@args) or next SET;
143             }
144            
145             push @return, &$code(@args);
146             }
147             }
148             }
149              
150             =item B
151              
152             Anonymous comprehensions (slighly faster)
153             comp1 sub { }, arg, [arg]
154             arg: array ref | guard subs
155              
156             =cut
157              
158             sub comp1(&@) {
159             local $code = shift;
160             local @guards;
161             local @sets;
162             local @args;
163              
164             for my $a (@_) {
165             if( ref($a) ) {
166             if( ref($a) eq 'CODE' ) {
167             push @guards, $a;
168             }
169             elsif( ref($a) eq 'ARRAY' ) {
170             push @sets, $a;
171             }
172             else {
173             croak "expected ARRAY or CODE ref";
174             }
175             } else {
176             croak "expected ARRAY or CODE ref";
177             }
178             }
179              
180             local @return;
181             run 0;
182             return @return;
183             }
184              
185             =item B
186              
187             Named comprehensions
188             comp2 sub { }, arg, [arg]
189             arg: [name => ] array ref | guard subs
190              
191             =cut
192              
193             sub comp2(&@) {
194             local $code = shift;
195             local @guards;
196             local @sets;
197             local @args;
198              
199             my @aliases;
200             my %aliased;
201              
202             my $their_lexicals = peek_my(1);
203             my %overridden_lexicals = ();
204              
205             while( my $arg = shift @_ ) {
206             if( ref($arg) ) {
207             if( ref($arg) eq 'CODE' ) {
208             push @guards, $arg;
209             }
210             elsif( ref($arg) eq 'ARRAY' ) {
211             push @args, 0;
212              
213             push @sets, $arg;
214             }
215             else {
216             croak "expected ARRAY or CODE ref";
217             }
218             }
219             else {
220             if( ref($_[0]) eq 'ARRAY' ) {
221             if( exists $their_lexicals->{"\$$arg"} ) {
222             my $value = $their_lexicals->{"\$$arg"};
223             $overridden_lexicals{"\$$arg"} = $$value;
224             av_push(@args, $$value);
225             }
226             else {
227             push @aliases, $arg;
228             av_push(@args, $aliased{$aliases[-1]});
229             }
230              
231             $args[-1] = 0;
232             push @sets, shift();
233             }
234             else {
235             croak "expected ARRAY or CODE ref";
236             }
237             }
238             }
239              
240             my ($package) = caller ();
241             $Alias::AttrPrefix = $package . "::";
242            
243             attr \%aliased;
244              
245             local @return;
246            
247             run 0;
248            
249             # restore lexicals
250             while( my ($k, $v) = each %overridden_lexicals ) {
251             ${$their_lexicals->{$k}} = $v;
252             }
253            
254             return @return;
255             }
256              
257             =back
258              
259             =head1 AUTHOR
260              
261             Jeremy Cortner EFE
262              
263             =head1 COPYRIGHT
264              
265             Copyright (C) 2003, Jeremy Cortner
266              
267             This module is free software; you can redistribute it or modify it
268             under the same terms as Perl itself.
269              
270             =cut
271              
272             1;
273