1 package Graph
::Traversal
;
5 # $SIG{__DIE__ } = sub { use Carp; confess };
6 # $SIG{__WARN__} = sub { use Carp; confess };
12 $self->{ unseen
} = { map { $_ => $_ } $self->{ graph
}->vertices };
13 $self->{ seen
} = { };
14 $self->{ order
} = [ ];
15 $self->{ preorder
} = [ ];
16 $self->{ postorder
} = [ ];
17 $self->{ roots
} = [ ];
19 Graph
->new( directed
=> $self->{ graph
}->directed );
20 delete $self->{ terminate
};
28 my $see_active = sub {
30 delete @
{ $self->{ active
} }{ $self->see };
34 my ($u, $v, $t, $s) = @_;
35 $s->{ has_a_cycle
} = 1;
40 my ($u, $v, $t, $s) = @_;
42 push @cycle, $v unless $u eq $v;
43 my $path = $t->{ order
};
46 while ($i >= 0 && $path->[ $i ] ne $v) { $i-- }
48 unshift @cycle, @
{ $path }[ $i+1 .. $#$path ];
51 $s->{ a_cycle
} = \
@cycle;
56 my ($self, %attr) = @_;
57 $self->{ pre
} = $attr{ pre
} if exists $attr{ pre
};
58 $self->{ post
} = $attr{ post
} if exists $attr{ post
};
59 $self->{ pre_vertex
} = $attr{ pre_vertex
} if exists $attr{ pre_vertex
};
60 $self->{ post_vertex
} = $attr{ post_vertex
} if exists $attr{ post_vertex
};
61 $self->{ pre_edge
} = $attr{ pre_edge
} if exists $attr{ pre_edge
};
62 $self->{ post_edge
} = $attr{ post_edge
} if exists $attr{ post_edge
};
63 if (exists $attr{ successor
}) { # Graph 0.201 compatibility.
64 $self->{ tree_edge
} = $self->{ non_tree_edge
} = $attr{ successor
};
66 if (exists $attr{ unseen_successor
}) {
67 if (exists $self->{ tree_edge
}) { # Graph 0.201 compatibility.
68 my $old_tree_edge = $self->{ tree_edge
};
69 $self->{ tree_edge
} = sub {
70 $old_tree_edge->( @_ );
71 $attr{ unseen_successor
}->( @_ );
74 $self->{ tree_edge
} = $attr{ unseen_successor
};
77 if ($self->graph->multiedged || $self->graph->countedged) {
78 $self->{ seen_edge
} = $attr{ seen_edge
} if exists $attr{ seen_edge
};
79 if (exists $attr{ seen_successor
}) { # Graph 0.201 compatibility.
80 $self->{ seen_edge
} = $attr{ seen_edge
};
83 $self->{ non_tree_edge
} = $attr{ non_tree_edge
} if exists $attr{ non_tree_edge
};
84 $self->{ pre_edge
} = $attr{ tree_edge
} if exists $attr{ tree_edge
};
85 $self->{ back_edge
} = $attr{ back_edge
} if exists $attr{ back_edge
};
86 $self->{ down_edge
} = $attr{ down_edge
} if exists $attr{ down_edge
};
87 $self->{ cross_edge
} = $attr{ cross_edge
} if exists $attr{ cross_edge
};
88 if (exists $attr{ start
}) {
89 $attr{ first_root
} = $attr{ start
};
90 $attr{ next_root
} = undef;
92 if (exists $attr{ get_next_root
}) {
93 $attr{ next_root
} = $attr{ get_next_root
}; # Graph 0.201 compat.
95 $self->{ next_root
} =
96 exists $attr{ next_root
} ?
98 $attr{ next_alphabetic
} ?
99 \
&Graph
::_next_alphabetic
:
100 $attr{ next_numeric
} ?
101 \
&Graph
::_next_numeric
:
102 \
&Graph
::_next_random
;
103 $self->{ first_root
} =
104 exists $attr{ first_root
} ?
105 $attr{ first_root
} :
106 exists $attr{ next_root
} ?
108 $attr{ next_alphabetic
} ?
109 \
&Graph
::_next_alphabetic
:
110 $attr{ next_numeric
} ?
111 \
&Graph
::_next_numeric
:
112 \
&Graph
::_next_random
;
113 $self->{ next_successor
} =
114 exists $attr{ next_successor
} ?
115 $attr{ next_successor
} :
116 $attr{ next_alphabetic
} ?
117 \
&Graph
::_next_alphabetic
:
118 $attr{ next_numeric
} ?
119 \
&Graph
::_next_numeric
:
120 \
&Graph
::_next_random
;
121 if (exists $attr{ has_a_cycle
}) {
123 ref $attr{ has_a_cycle
} eq 'CODE' ?
124 $attr{ has_a_cycle
} : \
&has_a_cycle
;
125 $self->{ back_edge
} = $has_a_cycle;
126 if ($self->{ graph
}->is_undirected) {
127 $self->{ down_edge
} = $has_a_cycle;
130 if (exists $attr{ find_a_cycle
}) {
132 ref $attr{ find_a_cycle
} eq 'CODE' ?
133 $attr{ find_a_cycle
} : \
&find_a_cycle
;
134 $self->{ back_edge
} = $find_a_cycle;
135 if ($self->{ graph
}->is_undirected) {
136 $self->{ down_edge
} = $find_a_cycle;
139 $self->{ add
} = \
&add_order
;
140 $self->{ see
} = $see;
142 pre post pre_edge post_edge
143 successor unseen_successor seen_successor
144 tree_edge non_tree_edge
145 back_edge down_edge cross_edge seen_edge
147 next_root next_alphabetic next_numeric next_random next_successor
149 has_a_cycle find_a_cycle
153 my @attr = sort keys %attr;
154 Carp
::croak
(sprintf "Graph::Traversal: unknown attribute%s @{[map { qq['$_'] } @attr]}\n", @attr == 1 ?
'' : 's');
161 unless (ref $g && $g->isa('Graph')) {
163 Carp
::croak
("Graph::Traversal: first argument is not a Graph");
165 my $self = { graph
=> $g, state => { } };
168 $self->configure( @_ );
174 $self->{ terminate
} = 1;
178 my ($self, @next) = @_;
179 push @
{ $self->{ order
} }, @next;
183 my ($self, @next) = @_;
184 delete @
{ $self->{ unseen
} }{ @next };
185 print "unseen = @{[sort keys %{$self->{unseen}}]}\n" if DEBUG
;
186 @
{ $self->{ seen
} }{ @next } = @next;
187 print "seen = @{[sort keys %{$self->{seen}}]}\n" if DEBUG
;
188 $self->{ add
}->( $self, @next );
189 print "order = @{$self->{order}}\n" if DEBUG
;
190 if (exists $self->{ pre
}) {
191 my $p = $self->{ pre
};
199 my ($self, @next) = @_;
200 push @
{ $self->{ preorder
} }, @next;
202 $self->{ preordern
}->{ $v } = $self->{ preorderi
}++;
204 print "preorder = @{$self->{preorder}}\n" if DEBUG
;
205 $self->visit( @next );
208 sub visit_postorder
{
210 my @post = reverse $self->{ see
}->( $self );
211 push @
{ $self->{ postorder
} }, @post;
213 $self->{ postordern
}->{ $v } = $self->{ postorderi
}++;
215 print "postorder = @{$self->{postorder}}\n" if DEBUG
;
216 if (exists $self->{ post
}) {
217 my $p = $self->{ post
};
222 if (exists $self->{ post_edge
}) {
223 my $p = $self->{ post_edge
};
224 my $u = $self->current;
227 $p->( $u, $v, $self, $self->{ state });
234 my ($self, $current, @all) = @_;
236 my $nontree = $self->{ non_tree_edge
};
237 my $back = $self->{ back_edge
};
238 my $down = $self->{ down_edge
};
239 my $cross = $self->{ cross_edge
};
240 my $seen = $self->{ seen_edge
};
241 my $bdc = defined $back || defined $down || defined $cross;
242 if (defined $nontree || $bdc || defined $seen) {
244 my $preu = $self->{ preordern
}->{ $u };
245 my $postu = $self->{ postordern
}->{ $u };
247 my $e = $self->{ tree
}->has_edge( $u, $v );
248 if ( !$e && (defined $nontree || $bdc) ) {
249 if ( exists $self->{ seen
}->{ $v }) {
250 $nontree->( $u, $v, $self, $self->{ state })
253 my $postv = $self->{ postordern
}->{ $v };
255 (!defined $postv || $postv >= $postu)) {
256 $back ->( $u, $v, $self, $self->{ state });
258 my $prev = $self->{ preordern
}->{ $v };
259 if ($down && $prev > $preu) {
260 $down ->( $u, $v, $self, $self->{ state });
261 } elsif ($cross && $prev < $preu) {
262 $cross->( $u, $v, $self, $self->{ state });
269 my $c = $self->graph->get_edge_count($u, $v);
271 $seen->( $u, $v, $self, $self->{ state } );
280 return undef if $self->{ terminate
};
282 while ($self->seeing) {
283 my $current = $self->current;
284 print "current = $current\n" if DEBUG
;
285 @next = $self->{ graph
}->successors( $current );
286 print "next.0 - @next\n" if DEBUG
;
287 my %next; @next{ @next } = @next;
288 # delete $next{ $current };
289 print "next.1 - @next\n" if DEBUG
;
292 print "all = @all\n" if DEBUG
;
293 delete @next{ $self->seen };
295 print "next.2 - @next\n" if DEBUG
;
297 @next = $self->{ next_successor
}->( $self, \
%next );
298 print "next.3 - @next\n" if DEBUG
;
300 $self->{ tree
}->add_edge( $current, $v );
302 if (exists $self->{ pre_edge
}) {
303 my $p = $self->{ pre_edge
};
304 my $u = $self->current;
306 $p->( $u, $v, $self, $self->{ state });
311 $self->visit_postorder;
313 return undef if $self->{ terminate
};
314 $self->_callbacks($current, @all);
315 # delete $next{ $current };
317 print "next.4 - @next\n" if DEBUG
;
319 unless ( @
{ $self->{ roots
} } ) {
320 my $first = $self->{ first_root
};
321 if (defined $first) {
323 ref $first eq 'CODE' ?
324 $self->{ first_root
}->( $self, $self->{ unseen
} ) :
330 return unless defined $self->{ next_root
};
331 return unless @next =
332 $self->{ next_root
}->( $self, $self->{ unseen
} );
334 return if exists $self->{ seen
}->{ $next[0] }; # Sanity check.
335 print "next.5 - @next\n" if DEBUG
;
336 push @
{ $self->{ roots
} }, $next[0];
338 print "next.6 - @next\n" if DEBUG
;
340 $self->visit_preorder( @next );
346 my ($self, $order) = @_;
347 1 while defined $self->next;
348 my $wantarray = wantarray;
350 @
{ $self->{ $order } };
351 } elsif (defined $wantarray) {
352 shift @
{ $self->{ $order } };
358 $self->_order( 'preorder' );
363 $self->_order( 'postorder' );
368 values %{ $self->{ unseen
} };
373 values %{ $self->{ seen
} };
378 @
{ $self->{ order
} };
383 @
{ $self->{ roots
} };
388 for my $u (@
{ $self->{ roots
} }) {
389 return 1 if $u eq $v;
404 sub vertex_by_postorder
{
406 exists $self->{ postorder
} && $self->{ postorder
}->[ $i ];
409 sub postorder_by_vertex
{
411 exists $self->{ postordern
} && $self->{ postordern
}->{ $v };
414 sub postorder_vertices
{
416 exists $self->{ postordern
} ?
%{ $self->{ postordern
} } : ();
419 sub vertex_by_preorder
{
421 exists $self->{ preorder
} && $self->{ preorder
}->[ $i ];
424 sub preorder_by_vertex
{
426 exists $self->{ preordern
} && $self->{ preordern
}->{ $v };
429 sub preorder_vertices
{
431 exists $self->{ preordern
} ?
%{ $self->{ preordern
} } : ();
435 my ($self, $var) = @_;
436 exists $self->{ state } && exists $self->{ state }->{ $var };
440 my ($self, $var) = @_;
441 exists $self->{ state } ?
$self->{ state }->{ $var } : undef;
445 my ($self, $var, $val) = @_;
446 $self->{ state }->{ $var } = $val;
451 my ($self, $var) = @_;
452 delete $self->{ state }->{ $var };
453 delete $self->{ state } unless keys %{ $self->{ state } };
463 Graph::Traversal - traverse graphs
467 Don't use Graph::Traversal directly, use Graph::Traversal::DFS
468 or Graph::Traversal::BFS instead.
473 use Graph::Traversal::...;
474 my $t = Graph::Traversal::...->new(%opt);
479 You can control how the graph is traversed by the various callback
480 parameters in the C<%opt>. In the parameters descriptions below the
481 $u and $v are vertices, and the $self is the traversal object itself.
483 =head2 Callback parameters
485 The following callback parameters are available:
491 Called when traversing an edge that belongs to the traversal tree.
492 Called with arguments ($u, $v, $self).
496 Called when an edge is met which either leads back to the traversal tree
497 (either a C<back_edge>, a C<down_edge>, or a C<cross_edge>).
498 Called with arguments ($u, $v, $self).
502 Called for edges in preorder.
503 Called with arguments ($u, $v, $self).
507 Called for edges in postorder.
508 Called with arguments ($u, $v, $self).
512 Called for back edges.
513 Called with arguments ($u, $v, $self).
517 Called for down edges.
518 Called with arguments ($u, $v, $self).
522 Called for cross edges.
523 Called with arguments ($u, $v, $self).
529 Called for vertices in preorder.
530 Called with arguments ($v, $self).
536 Called for vertices in postorder.
537 Called with arguments ($v, $self).
541 Called when choosing the first root (start) vertex for traversal.
542 Called with arguments ($self, $unseen) where $unseen is a hash
543 reference with the unseen vertices as keys.
547 Called when choosing the next root (after the first one) vertex for
548 traversal (useful when the graph is not connected). Called with
549 arguments ($self, $unseen) where $unseen is a hash reference with
550 the unseen vertices as keys. If you want only the first reachable
551 subgraph to be processed, set the next_root to C<undef>.
555 Identical to defining C<first_root> and undefining C<next_root>.
557 =item next_alphabetic
559 Set this to true if you want the vertices to be processed in
560 alphabetic order (and leave first_root/next_root undefined).
564 Set this to true if you want the vertices to be processed in
565 numeric order (and leave first_root/next_root undefined).
569 Called when choosing the next vertex to visit. Called with arguments
570 ($self, $next) where $next is a hash reference with the possible
571 next vertices as keys. Use this to provide a custom ordering for
572 choosing vertices, as opposed to C<next_numeric> or C<next_alphabetic>.
576 The parameters C<first_root> and C<next_successor> have a 'hierarchy'
577 of how they are determined: if they have been explicitly defined, use
578 that value. If not, use the value of C<next_alphabetic>, if that has
579 been defined. If not, use the value of C<next_numeric>, if that has
580 been defined. If not, the next vertex to be visited is chose randomly.
584 The following methods are available:
590 Return the unseen vertices in random order.
594 Return the seen vertices in random order.
598 Return the active fringe vertices in random order.
602 Return the vertices in preorder traversal order.
606 Return the vertices in postorder traversal order.
608 =item vertex_by_preorder
610 $v = $t->vertex_by_preorder($i)
612 Return the ith (0..$V-1) vertex by preorder.
614 =item preorder_by_vertex
616 $i = $t->preorder_by_vertex($v)
618 Return the preorder index (0..$V-1) by vertex.
620 =item vertex_by_postorder
622 $v = $t->vertex_by_postorder($i)
624 Return the ith (0..$V-1) vertex by postorder.
626 =item postorder_by_vertex
628 $i = $t->postorder_by_vertex($v)
630 Return the postorder index (0..$V-1) by vertex.
632 =item preorder_vertices
634 Return a hash with the vertices as the keys and their preorder indices
637 =item postorder_vertices
639 Return a hash with the vertices as the keys and their postorder
640 indices as the values.
644 Return the traversal tree as a graph.
650 Test whether the traversal has state 's' attached to it.
656 Get the state 's' attached to the traversal (C<undef> if none).
660 $t->set_state('s', $s)
662 Set the state 's' attached to the traversal.
666 $t->delete_state('s')
668 Delete the state 's' from the traversal.
672 =head2 Backward compatibility
674 The following parameters are for backward compatibility to Graph 0.2xx:
684 Identical to having C<tree_edge> both C<non_tree_edge> defined
687 =item unseen_successor
697 =head2 Special callbacks
699 If in a callback you call the special C<terminate> method,
700 the traversal is terminated, no more vertices are traversed.
704 L<Graph::Traversal::DFS>, L<Graph::Traversal::BFS>
706 =head1 AUTHOR AND COPYRIGHT
708 Jarkko Hietaniemi F<jhi@iki.fi>
712 This module is licensed under the same terms as Perl itself.