File Coverage

blib/lib/List/EvenMoreUtils.pm
Criterion Covered Total %
statement 65 95 68.4
branch 12 40 30.0
condition 1 4 25.0
subroutine 7 12 58.3
pod 7 8 87.5
total 92 159 57.8


line stmt bran cond sub pod time code
1              
2             package List::EvenMoreUtils;
3              
4 3     3   54498 use strict;
  3         5  
  3         110  
5 3     3   15 use warnings;
  3         5  
  3         101  
6             require Exporter;
7 3     3   14 use Carp qw(confess);
  3         8  
  3         3686  
8              
9             our @ISA = qw(Exporter);
10             our @EXPORT = ();
11             our @EXPORT_OK = qw(
12             do_sublist
13             keys_to_regex
14             list_to_text
15             partial_ordering_differs
16             list_difference_position
17             initial_sublist_match
18             longer_list
19             repeatable_list_shuffler
20             );
21              
22             our $VERSION = 0.11;
23              
24             sub do_sublist(&&@)
25             {
26 1     1 1 27 my $selector = shift;
27 1         2 my $actor = shift;
28              
29 1         2 my @order;
30             my %buckets;
31              
32 1         2 for (@_) {
33 7         12 my $bucket = &$selector;
34 7 100       24 if ($buckets{$bucket}) {
35 5         6 push(@{$buckets{$bucket}}, $_);
  5         11  
36             } else {
37 2         4 push(@order, $bucket);
38 2         5 $buckets{$bucket} = [ $_ ];
39             }
40             }
41 1         1 my @ret;
42 1         3 for my $sublist (@buckets{@order}) {
43 2         14 push(@ret, $actor->(@$sublist));
44             }
45 1         12 return @ret;
46             }
47              
48             sub keys_to_regex
49             {
50 0     0 1 0 my (%hash) = @_;
51 0         0 my $s = join('|', map { "\Q$_\E" } sort keys %hash);
  0         0  
52 0         0 return qr/(?:$s)/;
53             }
54              
55             sub list_to_text
56             {
57 0     0 1 0 my ($last, @rest) = reverse @_;
58 0 0       0 return $last unless @rest;
59 0         0 return join(", ", reverse @rest) . " and " . $last;
60             }
61              
62             #
63             # name = \@list
64             #
65             sub partial_ordering_differs
66             {
67 7     7 1 3037 my (%lists) = @_;
68              
69 7         8 my %positions;
70 7         21 for my $list (keys %lists) {
71 21         27 my $c = 1;
72 21         24 $positions{$list} = { map { $_ => $c++ } @{$lists{$list}} };
  81         204  
  21         54  
73             }
74            
75 7         12 my %done;
76 7         17 for my $one (keys %lists) {
77 20         36 for my $two (keys %lists) {
78 60 100       144 next if $done{$one}{$two}++;
79 41 100       121 next if $done{$two}{$one}++;
80              
81 21         23 my @common = grep { exists $positions{$two}{$_} } @{$lists{$one}};
  73         175  
  21         54  
82              
83 21 100       68 next unless @common;
84              
85 6         21 my @onekeys = sort { $positions{$one}{$a} <=> $positions{$one}{$b} } @common;
  16         42  
86 6         15 my @twokeys = sort { $positions{$two}{$a} <=> $positions{$two}{$b} } @common;
  16         34  
87              
88 6         7 my $after;
89 6         14 while (@onekeys) {
90 15 100       36 next if $onekeys[0] eq $twokeys[0];
91 1 50       5 if ($after) {
92 0         0 return "Item '$onekeys[0]' in $one needs to come after '$after' since it does so in $two";
93             } else {
94 1         14 return "Item '$onekeys[0]' in $one needs to come before '$onekeys[1]' since it does so in $two";
95             }
96             } continue {
97 14         15 $after = shift @onekeys;
98 14         44 shift @twokeys;
99             }
100             }
101             }
102 6         51 return undef;
103             }
104              
105             #
106             # A return value of 1 means the first elements are
107             # different.
108             #
109              
110             sub list_difference_position(\@\@)
111             {
112 0     0 1 0 my ($a, $b, $start) = @_;
113 0   0     0 $start ||= 0;
114 0         0 for my $i ($start..$#$a) {
115 0 0       0 if (defined($a->[$i])) {
116 0 0       0 return $i+1 unless defined $b->[$i];
117 0 0       0 return $i+1 unless $a->[$i] eq $b->[$i];
118             } else {
119 0 0       0 return $i+1 if defined $b->[$i];
120             }
121             }
122 0 0       0 if ($#$a < $#$b) {
    0          
123 0         0 return $#$a+2;
124             } elsif ($#$b < $#$a) {
125 0         0 return $#$b+2;
126             } else {
127 0         0 return undef;
128             }
129             }
130              
131             sub initial_sublist_match(\@\@)
132             {
133 0     0 1 0 my ($a, $b) = @_;
134 0         0 for my $i (0..$#$a) {
135 0 0       0 return 1 if $i > $#$b;
136 0 0       0 if (defined($a->[$i])) {
137 0 0       0 return 0 unless defined $b->[$i];
138 0 0       0 return 0 unless $a->[$i] eq $b->[$i];
139             } else {
140 0 0       0 return 0 if defined $b->[$i];
141             }
142             }
143 0         0 return 1;
144             }
145              
146             sub longer_list(\@\@)
147             {
148 0     0 1 0 my ($a, $b) = @_;
149 0 0       0 return $a if $#$a > $#$b;
150 0         0 return $b;
151             }
152              
153              
154             #
155             # Determanistic pseudo-random list shuffler
156             #
157             sub repeatable_list_shuffler
158             {
159 2     2 0 20 my ($seed) = @_;
160 2         4 my $previous = 0;
161 2         927 require String::CRC;
162             return sub {
163 3     3   20 my (@list) = @_;
164 3         5 my %ret;
165 3         5 for my $l (@list) {
166 48         44 $previous++;
167 48   50     157 my $pos = String::CRC::crc($previous.($l || '').$seed, 32);
168 48 50       93 redo if exists $ret{$pos};
169 48         102 $ret{$pos} = $l;
170             }
171 3         16 return map { $ret{$_} } sort { $a <=> $b } keys %ret;
  48         82  
  134         143  
172 2         563 };
173             }
174              
175              
176             1;
177              
178             __END__