@@ -7,11 +7,11 @@ use List::Util qw(first);
77use Carp;
88use Exporter qw( import) ;
99use AUR::Vercmp qw( vercmp) ;
10- our @EXPORT_OK = qw( recurse prune graph) ;
1110
1211use constant EX_SUCCESS => 0;
1312use constant EX_FAILURE => 1;
1413use constant EX_OUT_OF_RANGE => 34;
14+ our @EXPORT_OK = qw( recurse prune graph tsort) ;
1515our $VERSION = ' unstable' ;
1616
1717# Maximum number of calling the callback
@@ -296,4 +296,65 @@ sub prune {
296296 return @removals ;
297297}
298298
299+ =head2 tsort()
300+
301+ Topological sorting adapted from PerlPowerTools.
302+
303+ =Performs depth-first traversal by default.
304+
305+ =over
306+
307+ =item C<$bfs > Perform breadth-first traversal.
308+
309+ =back
310+
311+ =cut
312+
313+ sub tsort {
314+ my ($bfs , $input ) = @_ ;
315+ $bfs //= 0;
316+
317+ my %pairs ; # all pairs ($l, $r)
318+ my %npred ; # number of predecessors
319+ my %succ ; # list of successors
320+ my @output ;
321+
322+ if (scalar (@{$input }) % 2 == 1) {
323+ say STDERR __PACKAGE__ . " : odd number of tokens" ;
324+ exit EX_FAILURE;
325+ }
326+
327+ while (@{$input }) {
328+ my $l = shift @${input} ;
329+ my $r = shift @${input} ;
330+
331+ next if defined $pairs {$l }{$r };
332+ $pairs {$l }{$r }++;
333+ $npred {$l } += 0;
334+
335+ next if $l eq $r ;
336+ ++$npred {$r };
337+ push @{$succ {$l }}, $r ;
338+ }
339+
340+ # create a list of nodes without predecessors
341+ my @list = grep {!$npred {$_ }} keys %npred ;
342+
343+ while (@list ) {
344+ $_ = pop @list ;
345+ push @output , $_ ;
346+
347+ foreach my $child (@{$succ {$_ }}) {
348+ if ($bfs ) { # breadth-first
349+ unshift @list , $child unless --$npred {$child };
350+ } else { # depth-first (default)
351+ push @list , $child unless --$npred {$child };
352+ }
353+
354+ }
355+ }
356+ warn " $Program : cycle detected\n " if grep {$npred {$_ }} keys %npred ;
357+
358+ return @output ;
359+ }
299360# vim: set et sw=4 sts=4 ft=perl:
0 commit comments