package Tree::Binary; use 5.006; use strict; use warnings FATAL => 'all'; use Scalar::Util qw( blessed ); use base qw( Tree ); our $VERSION = '1.01'; sub _init { my $self = shift; $self->SUPER::_init( @_ ); # Make this class a complete binary tree, # filling in with Tree::Null as appropriate. $self->{_children}->[$_] = $self->_null for 0 .. 1; return $self; } sub left { my $self = shift; return $self->_set_get_child( 0, @_ ); } sub right { my $self = shift; return $self->_set_get_child( 1, @_ ); } sub _set_get_child { my $self = shift; my $index = shift; if ( @_ ) { my $node = shift; $node = $self->_null unless $node; my $old = $self->children->[$index]; $self->children->[$index] = $node; if ( $node ) { $node->_set_parent( $self ); $node->_set_root( $self->root ); $node->_fix_depth; } if ( $old ) { $old->_set_parent( $old->_null ); $old->_set_root( $old->_null ); $old->_fix_depth; } $self->_fix_height; $self->_fix_width; return $self; } else { return $self->children->[$index]; } } sub _clone_children { my ($self, $clone) = @_; @{ $clone->{_children} } = (); $clone->add_child({}, map { $_->clone } @{ $self->{_children} }); } sub children { my $self = shift; if ( @_ ) { my @idx = @_; return @{$self->{_children}}[@idx]; } else { if ( caller->isa( __PACKAGE__ ) || $self->isa( scalar(caller) ) ) { return wantarray ? @{$self->{_children}} : $self->{_children}; } else { return grep { $_ } @{$self->{_children}}; } } } use constant IN_ORDER => 4; # One of the things we have to do in a traversal is to remove all of the # Tree::Null elements that are appended to the tree to make this a complete # binary tree. The user isn't going to expect them, because they're an # internal nicety. sub traverse { my $self = shift; my $order = shift; $order = $self->PRE_ORDER unless $order; if ( wantarray ) { if ( $order == $self->IN_ORDER ) { return grep { $_ } ( $self->left->traverse( $order ), $self, $self->right->traverse( $order ), ); } else { return grep { $_ } $self->SUPER::traverse( $order ); } } else { my $closure; if ( $order eq $self->IN_ORDER ) { my @list = $self->traverse( $order ); $closure = sub { return unless @list; return shift @list; }; } elsif ( $order eq $self->PRE_ORDER ) { my $next_node = $self; my @stack = ( $self ); my @next_meth = ( 0 ); my @meths = qw( left right ); $closure = sub { my $node = $next_node; return unless $node; $next_node = undef; while ( @stack && !$next_node ) { while ( @next_meth && $next_meth[0] == 2 ) { shift @stack; shift @next_meth; } if ( @stack ) { my $meth = $meths[ $next_meth[0]++ ]; $next_node = $stack[0]->$meth; next unless $next_node; unshift @stack, $next_node; unshift @next_meth, 0; } } return $node; }; } elsif ( $order eq $self->POST_ORDER ) { my @list = $self->traverse( $order ); $closure = sub { return unless @list; return shift @list; }; #my @stack = ( $self ); #my @next_idx = ( 0 ); #while ( @{ $stack[0]->{_children} } ) { # unshift @stack, $stack[0]->{_children}[0]; # unshift @next_idx, 0; #} # #$closure = sub { # my $node = $stack[0] || return; # # shift @stack; shift @next_idx; # $next_idx[0]++; # # while ( @stack && exists $stack[0]->{_children}[ $next_idx[0] ] ) { # unshift @stack, $stack[0]->{_children}[ $next_idx[0] ]; # unshift @next_idx, 0; # } # # return $node; #}; } elsif ( $order eq $self->LEVEL_ORDER ) { my @nodes = ($self); $closure = sub { my $node = shift @nodes; return unless $node; push @nodes, grep { $_ } @{$node->{_children}}; return $node; }; } else { return $self->error( "traverse(): '$order' is an illegal traversal order" ); } return $closure; } } 1; __END__ =head1 NAME Tree::Binary - An implementation of a binary tree =head1 SYNOPSIS my $tree = Tree::Binary->new( 'root' ); my $left = Tree::Binary->new( 'left' ); $tree->left( $left ); my $right = Tree::Binary->new( 'left' ); $tree->right( $right ); my $right_child = $tree->right; $tree->right( undef ); # Unset the right child. my @nodes = $tree->traverse( $tree->POST_ORDER ); my $traversal = $tree->traverse( $tree->IN_ORDER ); while ( my $node = $traversal->() ) { # Do something with $node here } =head1 DESCRIPTION This is an implementation of a binary tree. This class inherits from L, which is an N-ary tree implemenation. Because of this, this class actually provides an implementation of a complete binary tree vs. a sparse binary tree. The empty nodes are instances of Tree::Null, which is described in L. This should have no effect on your usage of this class. =head1 METHODS In addition to the methods provided by L, the following items are provided or overriden. =over 4 =item * C / C These access the left and right children, respectively. They are mutators, which means that their behavior changes depending on if you pass in a value. If you do not pass in any parameters, then it will act as a getter for the specific child, return the child (if set) or undef (if not). If you pass in a child, it will act as a setter for the specific child, setting the child to the passed-in value and returning the $tree. (Thus, this method chains.) If you wish to unset the child, do C<$treeEleft( undef );> =item * C This will return the children of the tree. B There will be two children, always. Tree::Binary implements a complete binary tree, filling in missing children with Tree::Null objects. (Please see L for more information on Tree::Null.) =item * B When called in list context (Ctraverse()>), this will return a list of the nodes in the given traversal order. When called in scalar context (Ctraverse()>), this will return a closure that will, over successive calls, iterate over the nodes in the given traversal order. When finished it will return false. The default traversal order is pre-order. In addition to the traversal orders provided by L, Tree::Binary provides in-order traversals. =over 4 =item * In-order This will return the result of an in-order traversal on the left node (if any), then the node, then the result of an in-order traversal on the right node (if any). =back =back B You have access to all the methods provided by L, but it is not recommended that you use many of them, unless you know what you're doing. This list includes C and C. =head1 TODO =over 4 =item * Make in-order closure traversal work iteratively =item * Make post-order closure traversal work iteratively =back =head1 CODE COVERAGE Please see the relevant sections of L. =head1 SUPPORT Please see the relevant sections of L. =head1 AUTHORS Rob Kinyon Erob.kinyon@iinteractive.comE Stevan Little Estevan.little@iinteractive.comE Thanks to Infinity Interactive for generously donating our time. =head1 COPYRIGHT AND LICENSE Copyright 2004, 2005 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut