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   30068 use 5.008001;
  3         11  
3 3     3   17 use strict;
  3         8  
  3         71  
4 3     3   15 use warnings;
  3         6  
  3         179  
5              
6             our $VERSION = "0.003";
7             $VERSION = eval $VERSION;
8              
9 3     3   19 use Exporter 5.57 'import';
  3         57  
  3         411  
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   36 || our $NO_REF_UTIL;
20              
21 3 100 66     15 if ( !$impl && eval { require Ref::Util; 1 } ) {
  2         1138  
  2         3335  
22 2         907 Ref::Util->import('is_plain_arrayref');
23             }
24             else {
25 1     216   253 *is_plain_arrayref = sub { ref( $_[0] ) eq 'ARRAY' };
  216         484  
26             }
27             }
28              
29             {
30             my $croak;
31              
32             sub flat {
33 24     24 1 22598 $croak = 1;
34 24         78 goto &_flat;
35             # call _flat with current @_
36             }
37              
38             sub flat_r {
39 26     26 1 15045 undef $croak;
40 26         66 goto &_flat;
41             # call _flat with current @_
42             }
43              
44             sub _flat {
45              
46 50     50   99 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         133 while (@_) {
66              
67 294 100       601 if ( is_plain_arrayref( my $element = shift @_ ) ) {
68 56 100       133 if ( !defined( my $seen_r = shift @seens ) ) {
    50          
    0          
69 32         52 unshift @_, @{$element};
  32         76  
70 32         71 unshift @seens, ( ( [$element] ) x scalar @{$element} );
  32         108  
71             }
72             ## no critic (ProhibitBooleanGrep)
73 32         90 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         42 unshift @_, @{$element};
  24         45  
79             unshift @seens,
80 24         54 ( ( [ @$seen_r, $element ] ) x scalar @{$element} );
  24         74  
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         331 shift @seens;
93 238         575 push @results, $element;
94             }
95              
96             } ## tidy end: while (@_)
97              
98 50 100       193 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 14947 my @results;
115              
116 22         64 while (@_) {
117              
118 138 100       271 if ( is_plain_arrayref( my $element = shift @_ ) ) {
119 28         49 unshift @_, @{$element};
  28         87  
120             }
121             else {
122 110         267 push @results, $element;
123             }
124              
125             }
126              
127 22 100       92 return wantarray ? @results : \@results;
128              
129             } ## tidy end: sub flat_f
130              
131             1;
132              
133             __END__