home *** CD-ROM | disk | FTP | other *** search
- package Graph::Traversal;
-
- use strict;
- local $^W = 1;
-
- use Graph::Base;
-
- use vars qw(@ISA);
- @ISA = qw(Graph::Base);
-
- =head1 NAME
-
- Graph::Traversal - graph traversal
-
- =head1 SYNOPSIS
-
- use Graph::Traversal;
-
- =head1 DESCRIPTION
-
- =over 4
-
- =cut
-
- =pod
- =item new
-
- $s = Graph::Traversal->new($G, %param)
-
- Returns a new graph search object for the graph $G
- and the parameters %param.
-
- Usually not used directly but instead via frontends like
- Graph::DFS for depth-first searching and Graph::BFS for
- breadth-first searching:
-
- $dfs = Graph::DFS->new($G, %param)
- $bfs = Graph::BFS->new($G, %param)
-
- I<%param documentation to be written>
-
- =cut
- sub new {
- my $class = shift;
- my $G = shift;
-
- my $S = { G => $G };
-
- bless $S, $class;
-
- $S->reset(@_);
-
- return $S;
- }
-
- =pod
- =item reset
-
- $S->reset
-
- Resets a graph search object $S to its initial state.
-
- =cut
- sub reset {
- my $S = shift;
- my $G = $S->{ G };
-
- @{ $S->{ pool } }{ $G->vertices } = ( );
- $S->{ active_list } = [ ];
- $S->{ root_list } = [ ];
- $S->{ preorder_list } = [ ];
- $S->{ postorder_list } = [ ];
- $S->{ active_pool } = { };
- $S->{ vertex_found } = { };
- $S->{ vertex_root } = { };
- $S->{ vertex_successors } = { };
- $S->{ param } = { @_ };
- $S->{ when } = 0;
- }
-
- # _get_next_root_vertex
- #
- # $o = $S->_get_next_root_vertex(\%param)
- #
- # (INTERNAL USE ONLY)
- # Returns a vertex hopefully suitable as a root vertex of a tree.
- #
- # If $param->{ get_next_root } exists, it will be used the determine
- # the root. If it is a code reference, the result of running it
- # with parameters ($S, %param) will be the next root. Otherwise
- # it is assumed to be the next root vertex as it is.
- #
- # Otherwise an unseen vertex having the maximal out-degree
- # will be selected.
- #
- sub _get_next_root_vertex {
- my $S = shift;
- my %param = ( %{ $S->{ param } }, @_ ? %{ $_[0] } : ( ));
- my $G = $S->{ G };
-
- if ( exists $param{ get_next_root } ) {
- if ( ref $param{ get_next_root } eq 'CODE' ) {
- return $param{ get_next_root }->( $S, %param ); # Dynamic.
- } else {
- my $get_next_root = $param{ get_next_root }; # Static.
-
- # Use only once.
- delete $S->{ param }->{ get_next_root };
- delete $_[0]->{ get_next_root } if @_;
-
- return $get_next_root;
- }
- } else {
- return $G->largest_out_degree( keys %{ $S->{ pool } } );
- }
- }
-
- # _mark_vertex_found
- #
- # $S->_mark_vertex_found( $u )
- #
- # (INTERNAL USE ONLY)
- # Marks the vertex $u as a new vertex in the search object $S.
- #
- sub _mark_vertex_found {
- my ( $S, $u ) = @_;
-
- $S->{ vertex_found }->{ $u } = $S->{ when }++;
- delete $S->{ pool }->{ $u };
- }
-
- # _next_state
- #
- # $o = $S->_next_state(%param)
- #
- # (INTERNAL USE ONLY)
- # Returns a graph search object.
- #
- sub _next_state {
- my $S = shift; # The current state.
-
- my $G = $S->{ G }; # The current graph.
- my %param = ( %{ $S->{ param } }, @_);
- my ($u, $v); # The current vertex and its successor.
- my $return = 0; # Return when this becomes true.
-
- until ( $return ) {
-
- # Initialize our search when needed.
- # (Start up a new tree.)
- unless ( @{ $S->{ active_list } } ) {
- do {
- $u = $S->_get_next_root_vertex(\%param);
- return wantarray ? ( ) : $u unless defined $u;
- } while exists $S->{ vertex_found }->{ $u };
-
- # A new root vertex found.
- push @{ $S->{ active_list } }, $u;
- $S->{ active_pool }->{ $u } = 1;
- push @{ $S->{ root_list } }, $u;
- $S->{ vertex_root }->{ $u } = $#{ $S->{ root_list } };
- }
-
- # Get the current vertex.
- $u = $param{ current }->( $S );
- return wantarray ? () : $u unless defined $u;
-
- # Record the vertex if necessary.
- unless ( exists $S->{ vertex_found }->{ $u } ) {
- $S->_mark_vertex_found( $u );
- push @{ $S->{ preorder_list } }, $u;
- # Time to return?
- $return++ if $param{ return_next_preorder };
- }
-
- # Initialized the list successors if necessary.
- $S->{ vertex_successors }->{ $u } = [ $G->successors( $u ) ]
- unless exists $S->{ vertex_successors }->{ $u };
-
- # Get the next successor vertex.
- $v = shift @{ $S->{ vertex_successors }->{ $u } };
-
- if ( defined $v ) {
- # Something to do for each successor?
- $param{ successor }->( $u, $v, $S )
- if exists $param{ successor };
-
- unless ( exists $S->{ vertex_found }->{ $v } ) {
- # An unseen successor.
- $S->_mark_vertex_found( $v );
- push @{ $S->{ preorder_list } }, $v;
- $S->{ vertex_root }->{ $v } = $S->{ vertex_root }->{ $u };
- push @{ $S->{ active_list } }, $v;
- $S->{ active_pool }->{ $v } = 1;
-
- # Something to for each unseen edge?
- # For multiedges, triggered only for the first edge.
- $param{ unseen_successor }->( $u, $v, $S )
- if exists $param{ unseen_successor };
- } else {
- # Something to do for each seen edge?
- # For multiedges, triggered for the 2nd, etc, edges.
- $param{ seen_successor }->( $u, $v, $S )
- if exists $param{ seen_successor };
- }
-
- # Time to return?
- $return++ if $param{ return_next_edge };
-
- } elsif ( not exists $S->{ vertex_finished }->{ $u } ) {
- # Finish off with this vertex (we run out of descendants).
- $param{ finish }->( $S );
-
- $S->{ vertex_finished }->{ $u } = $S->{ when }++;
- push @{ $S->{ postorder_list } }, $u;
- delete $S->{ active_pool }->{ $u };
-
- # Time to return?
- $return++ if $param{ return_next_postorder };
- }
- }
-
- # Return an edge if so asked.
- return ( $u, $v ) if $param{ return_next_edge };
-
- # Return a vertex.
- return $u;
- }
-
- =pod
- =item next_preorder
-
- $v = $s->next_preorder
-
- Returns the next vertex in preorder of the graph
- encapsulated within the search object $s.
-
- =cut
- sub next_preorder {
- my $S = shift;
-
- $S->_next_state( return_next_preorder => 1, @_ );
- }
-
- =cut
- =item next_postorder
-
- $v = $S->next_postorder
-
- Returns the next vertex in postorder of the graph
- encapsulated within the search object $S.
-
- =cut
- sub next_postorder {
- my $S = shift;
-
- $S->_next_state( return_next_postorder => 1, @_ );
- }
-
- =pod
- =item next_edge
-
- ($u, $v) = $s->next_edge
-
- Returns the vertices of the next edge of the graph
- encapsulated within the search object $s.
-
- =cut
- sub next_edge {
- my $S = shift;
-
- $S->_next_state( return_next_edge => 1, @_ );
- }
-
- =pod
- =item preorder
-
- @V = $S->preorder
-
- Returns all the vertices in preorder of the graph
- encapsulated within the search object $S.
-
- =cut
- sub preorder {
- my $S = shift;
-
- 1 while defined $S->next_preorder; # Process entire graph.
-
- return @{ $S->{ preorder_list } };
- }
-
- =pod
- =item postorder
-
- @V = $S->postorder
-
- Returns all the vertices in postorder of the graph
- encapsulated within the search object $S.
-
- =cut
- sub postorder {
- my $S = shift;
-
- 1 while defined $S->next_postorder; # Process entire graph.
-
- return @{ $S->{ postorder_list } };
- }
-
- =pod
- =item edges
-
- @V = $S->edges
-
- Returns all the edges of the graph
- encapsulated within the search object $S.
-
- =cut
- sub edges {
- my $S = shift;
- my (@E, $u, $v);
-
- push @E, $u, $v while ($u, $v) = $S->next_edge;
-
- return @E;
- }
-
- =pod
- =item roots
-
- @R = $S->roots
-
- Returns all the root vertices of the trees of
- the graph encapsulated within the search object $S.
- "The root vertices" is ambiguous: what really happens
- is that either the roots from the previous search made
- on the $s are returned; or a preorder search is done
- and the roots of this search are returned.
-
- =cut
- sub roots {
- my $S = shift;
-
- $S->preorder
- unless exists $S->{ preorder_list } and
- @{ $S->{ preorder_list } } == $S->{ G }->vertices;
-
- return @{ $S->{ root_list } };
- }
-
- =pod
- =item vertex_roots
-
- %R = $S->vertex_roots
-
- Returns as a hash of ($vertex, $root) pairs all the vertices
- and the root vertices of their search trees of the graph
- encapsulated within the search object $S.
- "The root vertices" is ambiguous; see the documentation of
- the roots() method for more details.
-
- =cut
- sub vertex_roots {
- my $S = shift;
- my $G = $S->{ G };
-
- $S->preorder
- unless exists $S->{ preorder_list } and
- @{ $S->{ preorder_list } } == $G->vertices;
-
- return
- map { ( $_, $S->{ vertex_root }->{ $_ } ) } $G->vertices;
- }
-
- # DELETE
- #
- # (INTERNAL USE ONLY)
- # The Destructor.
- #
- sub DELETE {
- my $S = shift;
-
- delete $S->{ G }; # Release the graph.
- }
-
- =pod
-
- =head1 COPYRIGHT
-
- Copyright 1999, O'Reilly & Associates.
-
- This code is distributed under the same copyright terms as Perl itself.
-
- =cut
-
- 1;
-