File Coverage

blib/lib/Algorithm/TSort.pm
Criterion Covered Total %
statement 60 71 84.5
branch 20 28 71.4
condition 3 3 100.0
subroutine 12 13 92.3
pod 0 2 0.0
total 95 117 81.2


line stmt bran cond sub pod time code
1             package Algorithm::TSort;
2 2     2   34308 use 5.008000;
  2         8  
  2         99  
3 2     2   12 use strict;
  2         5  
  2         71  
4 2     2   10 use warnings;
  2         7  
  2         3471  
5             require Exporter;
6             our @ISA = qw(Exporter);
7            
8             # Items to export into callers namespace by default. Note: do not export
9             # names by default without a very good reason. Use EXPORT_OK instead.
10             # Do not simply export all your public functions/methods/constants.
11            
12             # This allows declaration use Algorithm::TSort ':all';
13             # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
14             # will save memory.
15             our %EXPORT_TAGS = ( 'all' => [ qw(
16             tsort
17             Graph
18             ) ] );
19            
20             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
21            
22             our @EXPORT = qw( tsort );
23            
24             our $VERSION = '0.04';
25             {
26             package Algorithm::TSort::ADJ;
27             sub adj_nodes {
28 43     43   5759 my $self = shift;
29 43         52 my $node = shift;
30 43         98 for ( $self->{$node} ) {
31 43 100       173 return @$_ if ref;
32             }
33 9         28 return ();
34             }
35            
36             sub nodes {
37 0     0   0 return keys %{ $_[0] };
  0         0  
38             }
39             package Algorithm::TSort::ADJSUB;
40             sub adj_nodes {
41 12     12   14 my $self = shift;
42 12         91 my $node = shift;
43 12         36 return $$self->($node);
44             }
45             package Algorithm::TSort::ADJSUB_ARRAYREF;
46             sub adj_nodes {
47 12     12   34 my $array = $_[0]->( $_[1] );
48 12 50       61 return $array ? @$array : ();
49             }
50             package Algorithm::TSort::Guard;
51             sub new{
52 30     30   85 return bless $_[1], $_[0];
53             }
54 30     30   63 sub DESTROY { $_[0]->() };
55             }
56             sub Graph($$) {
57 5     5 0 2530 my $what = shift;
58 5         10 my $data = shift;
59 5 50       16 die "Graph: undefined input" unless defined $what;
60 5 100 100     37 if ( $what eq 'IO' || $what eq 'SCALAR' ) {
    100          
    100          
    50          
61 2         3 my %c;
62             my $line;
63 0         0 my $fh;
64 2 100       7 if ( $what eq 'SCALAR' ) {
65 1         10 open $fh, "<", \$data;
66             }
67             else {
68 1         2 $fh = $data;
69             }
70 2         9 local $/ = "\n";
71 2         13 while ( defined( $line = <$fh> ) ) {
72 12         19 chomp $line;
73 12 50       41 next unless $line =~ m/\S/;
74 12         34 my ( $node, @deps ) = split ' ', $line;
75 12         57 $c{$node} = \@deps;
76             }
77 2         18 return bless \%c, 'Algorithm::TSort::ADJ';
78             }
79             elsif ( $what eq 'ADJSUB' ) {
80 1         9 return bless \( my $s = $data ), 'Algorithm::TSort::ADJSUB';
81             }
82             elsif ( $what eq 'ADJSUB_ARRAYREF' ) {
83 1         18 return bless $data, 'Algorithm::TSort::ADJSUB_ARRAYREF';
84             }
85             elsif ( $what eq 'ADJ' ) {
86 1         9 my %c = %$data;
87 1         5 return bless \%c, 'Algorithm::TSort::ADJ';
88             }
89             else {
90 0         0 require Carp;
91 0         0 Carp::croak("Graph: don't know about \$what='$what'");
92             }
93             }
94            
95            
96             # Preloaded methods go here.
97             sub tsort($;@) {
98 30     30 0 17650 my $object = shift;
99 30         51 my @nodes = @_;
100 30         33 my @sorted;
101             my %seen;
102 0         0 my $req_sub;
103 0         0 my $guard;
104 30 50       68 unless (@nodes) {
105 0 0       0 if ( UNIVERSAL::can( $object, 'nodes') ) {
106 0         0 @nodes = $object->nodes();
107             }
108             else {
109 0         0 require Carp;
110 0         0 Carp::croak("tsort: no nodes for sort");
111             }
112             }
113             $guard = Algorithm::TSort::Guard->new(sub {
114 30     30   457 $req_sub = undef; # remove circular dependency;
115 30         142 });
116            
117            
118             $req_sub = sub {
119 75     75   113 my $node = shift;
120 75 100       136 if ( $seen{$node} ) {
121 15 100       119 die "Algorithm::TSort - can't tsort cicle detected" if ( $seen{$node} == 1 );
122 5         23 return;
123             }
124 60         108 $seen{$node} = 1;
125 60         127 for ( $object->adj_nodes($node) ) {
126 45         166 $req_sub->($_);
127             }
128 45         87 $seen{$node} = 2;
129 45         126 push @sorted, $node;
130 30         97 };
131            
132 30         59 for (@nodes) {
133 30 50       77 next if $seen{$_};
134 30         53 $req_sub->($_);
135             }
136 20         77 return reverse @sorted;
137             }
138            
139             1;
140             __END__