Tree::Binary - An implementation of a binary tree


Tree documentation Contained in the Tree distribution.

Index


Code Index:

NAME

Top

Tree::Binary - An implementation of a binary tree

SYNOPSIS

Top

  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
  }

DESCRIPTION

Top

This is an implementation of a binary tree. This class inherits from Tree, 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 Tree. This should have no effect on your usage of this class.

METHODS

Top

In addition to the methods provided by Tree, the following items are provided or overriden.

* left([$child]) / right([$child])

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 $tree>left( undef );

* children()

This will return the children of the tree.

NOTE: There will be two children, always. Tree::Binary implements a complete binary tree, filling in missing children with Tree::Null objects. (Please see Tree::Fast for more information on Tree::Null.)

* traverse( [$order] )

When called in list context (my @traversal = $tree->traverse()), this will return a list of the nodes in the given traversal order. When called in scalar context (my $traversal = $tree->traverse()), 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 Tree, Tree::Binary provides in-order traversals.

* 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).

NOTE: You have access to all the methods provided by Tree, but it is not recommended that you use many of them, unless you know what you're doing. This list includes add_child() and remove_child().

TODO

Top

* Make in-order closure traversal work iteratively
* Make post-order closure traversal work iteratively

CODE COVERAGE

Top

Please see the relevant sections of Tree.

SUPPORT

Top

Please see the relevant sections of Tree.

AUTHORS

Top

Rob Kinyon <rob.kinyon@iinteractive.com>

Stevan Little <stevan.little@iinteractive.com>

Thanks to Infinity Interactive for generously donating our time.

COPYRIGHT AND LICENSE

Top


Tree documentation Contained in the Tree distribution.

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__