File Coverage

blib/lib/List/Intersperse.pm
Criterion Covered Total %
statement 37 37 100.0
branch 3 4 75.0
condition 2 3 66.6
subroutine 9 9 100.0
pod 2 2 100.0
total 53 55 96.3


line stmt bran cond sub pod time code
1             package List::Intersperse;
2              
3 1     1   3348 use strict;
  1         4  
  1         49  
4 1     1   6 use Exporter;
  1         2  
  1         72  
5              
6 1     1   7 use vars qw/$VERSION @ISA @EXPORT @EXPORT_OK/;
  1         7  
  1         3819  
7              
8             @ISA = qw/Exporter/;
9             @EXPORT = qw//;
10             @EXPORT_OK = qw/intersperseq intersperse/;
11              
12             $VERSION = '1.00';
13              
14             =pod
15              
16             =head1 NAME
17              
18             List::Intersperse - Intersperse / unsort / disperse a list
19              
20             =head1 SYNOPSIS
21              
22             use List::Intersperse qw/intersperseq/;
23              
24             @ispersed = intersperseq {substr($_[0],0,1)} qw/A1 A2 B1 B2 C1 C2/;
25              
26             @ispersed = List::Intersperse::intersperse qw/A A B B B B B B C/;
27              
28             =head1 DESCRIPTION
29              
30             C and C evenly distribute elements of a
31             list. Elements that are considered equal are spaced as far apart from each
32             other as possible.
33              
34             =head1 FUNCTIONS
35              
36             =over 4
37              
38             =item intersperse LIST
39              
40             This function returns a list of elements interspersed so that equivalent items
41             are evenly distributed throughout the list.
42              
43             =item intersperseq BLOCK LIST
44              
45             C works like C but it applies BLOCK to the elements
46             of LIST to determine the equivalance key.
47              
48             =cut
49              
50             sub intersperseq(&@) {
51             # wrapper with a prototype, allows calling like map
52 5     5 1 1079 _intersperse( @_ )
53             }
54              
55             sub intersperse(@) { # no key func
56 115     115 1 289 _intersperse( sub { $_[0] }, @_ )
  5     5   363  
57             }
58              
59             sub _intersperse {
60 10     10   19 my $keyf = shift;
61 10         15 my %h;
62 10         40 for ( @_ ) { push @{$h{$keyf->($_)}}, $_; }
  230         535  
  230         345  
63 10         62 my( $b, @bins ) = sort { @$a <=> @$b } values %h;
  113         136  
64 10         26 my @result = @$b;
65 10         17 for $b ( @bins ) {
66             # (consider rotating @result here.)
67              
68 48         131 @result = _intersperse2( $b, \@result );
69             }
70             @result
71 10         203 }
72              
73             sub _take_one {
74 598     598   808 my( $counter_sr, $source_ar ) = @_;
75 598         908 ${$counter_sr}++;
  598         700  
76 598         2519 shift @$source_ar
77             }
78              
79             sub _intersperse2 {
80 48     48   62 my( $aa, $ab ) = @_; # two arrays, by ref.
81 48 50       100 @$aa > @$ab and ( $aa, $ab ) = ( $ab, $aa );
82             # so that @$aa is the shorter array,
83             # and @$ab is the longer array.
84              
85 48         72 my $ratio = @$ab / @$aa;
86 48         50 my @accum;
87 48         64 my( $na, $nb ) = (0,0);
88              
89             # take one from each, to start with:
90 48         83 push @accum, _take_one( \$nb, $ab );
91 48         84 push @accum, _take_one( \$na, $aa );
92              
93 48   66     198 while ( @$aa and @$ab ) {
94 502 100       1159 push @accum, _take_one(
95             $nb / $na < $ratio
96             ? ( \$nb, $ab )
97             : ( \$na, $aa )
98             );
99             }
100              
101 48         647 ( @accum, @$ab, @$aa )
102             }
103              
104             1;
105              
106             __END__