Algorithm-TSort
view release on metacpan or search on metacpan
lib/Algorithm/TSort.pm view on Meta::CPAN
# vim: ft=perl sts=4 ts=8 sw=4 et ff=unix
package Algorithm::TSort;
use 5.007003;
use strict;
use warnings;
require Exporter;
our @ISA = qw(Exporter);
# Items to export into callers namespace by default. Note: do not export
# names by default without a very good reason. Use EXPORT_OK instead.
# Do not simply export all your public functions/methods/constants.
# This allows declaration use Algorithm::TSort ':all';
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
# will save memory.
our %EXPORT_TAGS = ( 'all' => [ qw(
tsort
Graph
) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw( tsort );
our $VERSION = '0.05';
{
package Algorithm::TSort::ADJ;
sub adj_nodes {
my $self = shift;
my $node = shift;
for ( $self->{$node} ) {
return @$_ if ref;
}
return ();
}
sub nodes {
return keys %{ $_[0] };
}
package Algorithm::TSort::ADJSUB;
sub adj_nodes {
my $self = shift;
my $node = shift;
return $$self->($node);
}
package Algorithm::TSort::ADJSUB_ARRAYREF;
sub adj_nodes {
my $array = $_[0]->( $_[1] );
return $array ? @$array : ();
}
package Algorithm::TSort::Guard;
sub new{
return bless $_[1], $_[0];
}
sub DESTROY { $_[0]->() };
}
sub Graph($$) {
my $what = shift;
my $data = shift;
die "Graph: undefined input" unless defined $what;
if ( $what eq 'IO' || $what eq 'SCALAR' ) {
my %c;
my $line;
my $fh;
if ( $what eq 'SCALAR' ) {
open $fh, "<", \$data;
}
else {
$fh = $data;
}
local $/ = "\n";
while ( defined( $line = <$fh> ) ) {
chomp $line;
next unless $line =~ m/\S/;
my ( $node, @deps ) = split ' ', $line;
$c{$node} = \@deps;
}
return bless \%c, 'Algorithm::TSort::ADJ';
}
elsif ( $what eq 'ADJSUB' ) {
return bless \( my $s = $data ), 'Algorithm::TSort::ADJSUB';
}
elsif ( $what eq 'ADJSUB_ARRAYREF' ) {
return bless $data, 'Algorithm::TSort::ADJSUB_ARRAYREF';
}
elsif ( $what eq 'ADJ' ) {
my %c = %$data;
return bless \%c, 'Algorithm::TSort::ADJ';
}
else {
require Carp;
Carp::croak("Graph: don't know about \$what='$what'");
}
}
# Preloaded methods go here.
sub tsort($;@) {
my $object = shift;
my @nodes = @_;
my @sorted;
my %seen;
my $req_sub;
my $guard;
unless (@nodes) {
if ( UNIVERSAL::can( $object, 'nodes') ) {
@nodes = $object->nodes();
}
else {
require Carp;
Carp::croak("tsort: no nodes for sort");
}
}
$guard = Algorithm::TSort::Guard->new(sub {
$req_sub = undef; # remove circular dependency;
});
$req_sub = sub {
my $node = shift;
if ( $seen{$node} ) {
die "Algorithm::TSort - can't tsort cicle detected" if ( $seen{$node} == 1 );
return;
}
$seen{$node} = 1;
for ( $object->adj_nodes($node) ) {
$req_sub->($_);
}
$seen{$node} = 2;
push @sorted, $node;
};
for (@nodes) {
next if $seen{$_};
$req_sub->($_);
}
return reverse @sorted;
}
1;
__END__
# Below is stub documentation for your module. You'd better edit it!
=head1 NAME
Algorithm::TSort - Perl extension for topological sort
=head1 SYNOPSIS
( run in 0.810 second using v1.01-cache-2.11-cpan-39bf76dae61 )