File Coverage

blib/lib/Number/Continuation.pm
Criterion Covered Total %
statement 75 82 91.4
branch 45 56 80.3
condition 26 30 86.6
subroutine 7 7 100.0
pod 1 1 100.0
total 154 176 87.5


line stmt bran cond sub pod time code
1             package Number::Continuation;
2              
3 4     4   35588 use strict;
  4         7  
  4         190  
4 4     4   27 use warnings;
  4         10  
  4         126  
5 4     4   20 use base qw(Exporter);
  4         12  
  4         525  
6              
7 4     4   20 use Carp qw(croak);
  4         12  
  4         284  
8 4     4   21 use Scalar::Util qw(refaddr);
  4         7  
  4         4181  
9              
10             our ($VERSION, @EXPORT_OK);
11              
12             $VERSION = '0.04';
13             @EXPORT_OK = qw(continuation);
14              
15             sub continuation
16             {
17 7 100   7 1 6189 my $opts = pop if ref $_[-1] eq 'HASH';
18              
19 0         0 my $input = ref $_[0] eq 'ARRAY'
20 7 50       54 ? join ' ', @{$_[0]}
    50          
    50          
21             : @_ > 1
22             ? join ' ', @_
23             : !refaddr $_[0]
24             ? $_[0]
25             : croak 'continuation($set | @set | \@set [, { options } ])';
26              
27 7         19 _validate($input);
28              
29 7         15 my $wantarray = wantarray;
30              
31 7   100     39 $opts->{delimiter} ||= '';
32 7   100     27 $opts->{range} ||= '-';
33 7   100     24 $opts->{separator} ||= ',';
34              
35 7         19 @{$opts->{delimiters}} = split //, $opts->{delimiter};
  7         19  
36              
37 7         49 my @nums = split /\s+/, $input;
38              
39 7         12 my (%constructed, $have_neg_continuation, @lists, $output, @output);
40              
41 7         10 my $reset = 1;
42              
43 7         24 for (my $i = 0; $i < @nums; $i++) {
44             # handy variables
45 97   50     222 my $prev_number = $nums[$i-1] || 0;
46 97   50     179 my $current_number = $nums[$i ] || 0;
47 97   100     210 my $next_number = $nums[$i+1] || 0;
48              
49             # set if preceeded by continuation
50 97 100 100     559 my $prev_continuation = 1 if $constructed{begin}
      100        
51             && $constructed{middle}
52             && $constructed{end};
53              
54             # set if negative continuation sensed (i.e. 3 2 1)
55 97 100       217 $have_neg_continuation = 1 if ($prev_number - $next_number == 2);
56              
57             # previous number greater than current one
58 97 100 100     297 if ($prev_number > $current_number && $i != 0 && !$have_neg_continuation) {
      100        
59             # previous number *exactly* greater 1
60 6 50       16 if ($prev_number - $current_number == 1) {
61 0 0       0 if ($wantarray) {
62 0 0       0 if (@lists) {
63 0         0 push @output, [ @lists ];
64 0         0 undef @lists;
65             }
66             } else {
67 0         0 $output .= "$opts->{separator} ";
68             }
69             # previous number greater than 1 and no previous continuation
70             } else {
71 6 100       13 if ($wantarray) {
72 3 100       7 if (@lists) {
73 2         5 push @output, [ @lists ];
74 2         3 undef @lists;
75             }
76             } else {
77 3 100       11 $output .= "$opts->{separator} " unless $prev_continuation;
78             }
79             }
80             # reset processing continuation state
81 6         9 $reset = 1;
82             }
83             # processing new continuation
84 97 100 100     331 if ($reset) {
    100 33        
85 47 100       67 if ($wantarray) {
86 22         34 push @lists, $nums[$i];
87 22 100       47 push @output, [ @lists ] if $i == $#nums;
88             } else {
89 25 100       60 $output .= $opts->{delimiters}->[0] if $opts->{delimiters}->[0];
90 25         40 $output .= $nums[$i];
91             }
92 47 100       92 if (($next_number - $current_number) > 1) {
93 14 100       23 if ($wantarray) {
94 7 50       13 if (@lists) {
95 7         14 push @output, [ @lists ];
96 7         9 undef @lists;
97             }
98             } else {
99 7         17 $output .= "$opts->{separator} ";
100             }
101 14         36 next;
102             }
103 33         44 ($have_neg_continuation, $reset) = (0,0);
104 33         59 undef %constructed;
105              
106 33         113 $constructed{begin} = 1;
107             # process numbers in between (skipping if scalar context)
108             } elsif (defined($next_number) && (($next_number - $current_number) == 1
109             || ($current_number - $next_number) == 1)) {
110 23 100       47 if ($wantarray) {
111 10         37 push @lists, $current_number;
112             } else { # blissfully do nothing when scalar context
113             }
114 23         101 $constructed{middle} = 1;
115             # end processing current continuation
116             } else {
117 27 100       53 if ($wantarray) {
118 12         13 push @lists, $current_number;
119 12         30 push @output, [ @lists ];
120 12         24 undef @lists;
121             } else {
122 15         28 $output .= $opts->{range}.$current_number;
123 15 100       37 $output .= $opts->{delimiters}->[-1] if $opts->{delimiters}->[-1];
124 15 100       38 $output .= "$opts->{separator} " unless $i == $#nums;
125             }
126 27         30 $reset = 1;
127 27         79 $constructed{end} = 1;
128             }
129             }
130              
131 7 100       57 return wantarray ? @output : $output;
132             }
133              
134             sub _validate
135             {
136 7     7   10 my ($set) = @_;
137              
138 7 50       20 croak 'continuation(): empty set provided' unless defined $set;
139              
140 7         32 my $RE_valid = qr{(?:[\d\-]+\ ?)+};
141 7         256 1 while $set =~ /\G$RE_valid/gc;
142 7 50       41 unless ($set =~ /\G$/) {
143 0           croak "continuation(): invalid set provided: '$set`";
144             }
145             }
146              
147             1;
148             __END__