File Coverage

blib/lib/Algorithm/TSort.pm
Criterion Covered Total %
statement 59 70 84.2
branch 20 28 71.4
condition 3 3 100.0
subroutine 12 13 92.3
pod 0 2 0.0
total 94 116 81.0


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