File Coverage

blib/lib/List/Flat.pm
Criterion Covered Total %
statement 45 47 95.7
branch 13 16 81.2
condition 4 6 66.6
subroutine 10 10 100.0
pod 3 3 100.0
total 75 82 91.4


line stmt bran cond sub pod time code
1             package List::Flat;
2 3     3   28616 use 5.008001;
  3         11  
3 3     3   17 use strict;
  3         7  
  3         61  
4 3     3   16 use warnings;
  3         6  
  3         235  
5              
6             our $VERSION = "0.001_003";
7             $VERSION = eval $VERSION;
8              
9 3     3   19 use Exporter 5.57 'import';
  3         63  
  3         393  
10             our @EXPORT_OK = qw/flat flat_r flat_f/;
11              
12             # if PERL_LIST_FLAT_NO_REF_UTIL environment variable is set to a true
13             # value, or $List::Flat::NO_REF_UTIL is set to a true value,
14             # uses the pure-perl version of is_plain_arrayref.
15             # Otherwise, uses Ref::Util if it can be successfully loaded.
16              
17             BEGIN {
18             my $impl = $ENV{PERL_LIST_FLAT_NO_REF_UTIL}
19 3   66 3   29 || our $NO_REF_UTIL;
20              
21 3 100 66     13 if ( !$impl && eval { require Ref::Util; 1 } ) {
  2         878  
  2         2862  
22 2         656 Ref::Util->import('is_plain_arrayref');
23             }
24             else {
25 1     216   275 *is_plain_arrayref = sub { ref( $_[0] ) eq 'ARRAY' };
  216         462  
26             }
27             }
28              
29             {
30             my $croak;
31              
32             sub flat {
33 24     24 1 19009 $croak = 1;
34 24         66 goto &_flat;
35             # call _flat with current @_
36             }
37              
38             sub flat_r {
39 26     26 1 12593 undef $croak;
40 26         67 goto &_flat;
41             # call _flat with current @_
42             }
43              
44             sub _flat {
45              
46 50     50   85 my @results;
47             my @seens;
48              
49             # this uses @_ as the queue of items to process.
50             # An item is plucked off the queue. If it's not an array ref,
51             # put it in @results.
52              
53             # If it is an array ref, check to see if it's the same as any
54             # of the arrayrefs we are currently in the middle of processing.
55             # If it is, either croak if called as flat, or if called as
56             # flat_r, don't do anything -- skip to the next one.
57             # If it hasn't been seen before, put all the items it
58             # contains back on the @_ queue.
59             # Also, for each of the items, push a reference into @seens
60             # that contains references to all the arrayrefs we are currently
61             # in the middle of processing, plus this arrayref.
62             # Note that @seens will be empty at the top level, so we must
63             # handle both when it is empty and when it is not.
64              
65 50         115 while (@_) {
66              
67 294 100       574 if ( is_plain_arrayref( my $element = shift @_ ) ) {
68 56 100       115 if ( !defined( my $seen_r = shift @seens ) ) {
    50          
    0          
69 32         46 unshift @_, @{$element};
  32         68  
70 32         63 unshift @seens, ( ( [$element] ) x scalar @{$element} );
  32         100  
71             }
72             ## no critic (ProhibitBooleanGrep)
73 32         75 elsif ( !grep { $element == $_ } @$seen_r ) {
74             ## use critic
75             # until the recursion gets very deep, the overhead in calling
76             # List::Util::none seems to be taking more time than the
77             # additional comparisons required by grep
78 24         50 unshift @_, @{$element};
  24         39  
79             unshift @seens,
80 24         47 ( ( [ @$seen_r, $element ] ) x scalar @{$element} );
  24         60  
81             }
82             elsif ($croak) {
83 0         0 require Carp;
84 0         0 Carp::croak( 'Circular reference passed to '
85             . __PACKAGE__
86             . '::flat' );
87             }
88             # else do nothing
89             } ## tidy end: if ( is_plain_arrayref...)
90              
91             else { # not arrayref
92 238         325 shift @seens;
93 238         543 push @results, $element;
94             }
95              
96             } ## tidy end: while (@_)
97              
98 50 100       195 return wantarray ? @results : \@results;
99              
100             } ## tidy end: sub _flat
101              
102             }
103              
104             sub flat_f {
105              
106             # this uses @_ as the queue of items to process.
107             # An item is plucked off the queue. If it's not an array ref,
108             # put it in @results.
109             # If it is an array ref, put its contents back in the queue.
110              
111             # Mark Jason Dominus calls this the "agenda method" of turning
112             # a recursive function into an iterative one.
113              
114 22     22 1 12739 my @results;
115              
116 22         57 while (@_) {
117              
118 138 100       280 if ( is_plain_arrayref( my $element = shift @_ ) ) {
119 28         41 unshift @_, @{$element};
  28         84  
120             }
121             else {
122 110         264 push @results, $element;
123             }
124              
125             }
126              
127 22 100       84 return wantarray ? @results : \@results;
128              
129             } ## tidy end: sub flat_f
130              
131             1;
132              
133             __END__