File Coverage

blib/lib/Tree/Binary.pm
Criterion Covered Total %
statement 159 159 100.0
branch 72 74 97.3
condition 19 21 90.4
subroutine 32 32 100.0
pod 25 25 100.0
total 307 311 98.7


line stmt bran cond sub pod time code
1             package Tree::Binary;
2              
3 12     12   345583 use strict;
  12         65  
  12         325  
4 12     12   49 use warnings;
  12         21  
  12         432  
5              
6             our $VERSION = '1.09';
7              
8 12     12   54 use Scalar::Util qw(blessed);
  12         19  
  12         19421  
9              
10             ## ----------------------------------------------------------------------------
11             ## Tree::Binary
12             ## ----------------------------------------------------------------------------
13              
14             ### constructor
15              
16             sub new {
17 71     71 1 2938 my ($_class, $node) = @_;
18 71   66     182 my $class = ref($_class) || $_class;
19 71         99 my $binary_tree = {};
20 71         114 bless($binary_tree, $class);
21 71         117 $binary_tree->_init($node);
22 70         179 return $binary_tree;
23             }
24              
25             ### ---------------------------------------------------------------------------
26             ### methods
27             ### ---------------------------------------------------------------------------
28              
29             ## ----------------------------------------------------------------------------
30             ## private methods
31              
32             sub _init {
33 7163     7163   9815 my ($self, $node) = @_;
34 7163 100       10252 (defined($node)) || die "Insufficient Arguments : you must provide a node value";
35             # set the value of the unique id
36 7161         29216 ($self->{_uid}) = ("$self" =~ /\((.*?)\)$/);
37             # set the value of the node
38 7161         11137 $self->{_node} = $node;
39             # create the child nodes
40 7161         9203 $self->{_left} = undef;
41 7161         8211 $self->{_right} = undef;
42             # initialize the parent and depth here
43 7161         8586 $self->{_parent} = undef;
44 7161         13472 $self->{_depth} = 0;
45             }
46              
47             ## ----------------------------------------------------------------------------
48             ## mutators
49              
50             sub setNodeValue {
51 6     6 1 323 my ($self, $node_value) = @_;
52 6 100       23 (defined($node_value)) || die "Insufficient Arguments : must supply a value for node";
53 5         10 $self->{_node} = $node_value;
54             }
55              
56             sub setUID {
57 2     2 1 1295 my ($self, $uid) = @_;
58 2 100       12 ($uid) || die "Insufficient Arguments : Custom Unique ID's must be a true value";
59 1         3 $self->{_uid} = $uid;
60             }
61              
62             sub setLeft {
63 3553     3553 1 6035 my ($self, $tree) = @_;
64 3553 100 100     14441 (blessed($tree) && $tree->isa("Tree::Binary"))
65             || die "Insufficient Arguments : left argument must be a Tree::Binary object";
66 3549         5746 $tree->{_parent} = $self;
67 3549         4170 $self->{_left} = $tree;
68 3549 100       4544 unless ($tree->isLeaf()) {
69 2023         3012 $tree->fixDepth();
70             }
71             else {
72 1526         2399 $tree->{_depth} = $self->getDepth() + 1;
73             }
74 3549         7950 $self;
75             }
76              
77             sub removeLeft {
78 38     38 1 337 my ($self) = @_;
79 38 100       60 ($self->hasLeft()) || die "Illegal Operation: cannot remove node that doesnt exist";
80 37         49 my $left = $self->{_left};
81 37         50 $left->{_parent} = undef;
82 37 100       48 unless ($left->isLeaf()) {
83 8         17 $left->fixDepth();
84             }
85             else {
86 29         46 $left->{_depth} = 0;
87             }
88 37         86 $self->{_left} = undef;
89 37         65 return $left;
90             }
91              
92             sub setRight {
93 3489     3489 1 6114 my ($self, $tree) = @_;
94 3489 100 100     14369 (blessed($tree) && $tree->isa("Tree::Binary"))
95             || die "Insufficient Arguments : right argument must be a Tree::Binary object";
96 3485         5415 $tree->{_parent} = $self;
97 3485         4011 $self->{_right} = $tree;
98 3485 100       4723 unless ($tree->isLeaf()) {
99 1943         2798 $tree->fixDepth();
100             }
101             else {
102 1542         2197 $tree->{_depth} = $self->getDepth() + 1;
103             }
104 3485         7591 $self;
105             }
106              
107             sub removeRight {
108 21     21 1 997 my ($self) = @_;
109 21 100       36 ($self->hasRight()) || die "Illegal Operation: cannot remove node that doesnt exist";
110 20         30 my $right = $self->{_right};
111 20         26 $right->{_parent} = undef;
112 20 100       54 unless ($right->isLeaf()) {
113 11         21 $right->fixDepth();
114             }
115             else {
116 9         13 $right->{_depth} = 0;
117             }
118 20         47 $self->{_right} = undef;
119 20         41 return $right;
120             }
121              
122             ## ----------------------------------------------------------------------------
123             ## accessors
124              
125             sub getUID {
126 2     2 1 6 my ($self) = @_;
127 2         6 return $self->{_uid};
128             }
129              
130             sub getParent {
131 31611     31611 1 39307 my ($self)= @_;
132 31611         45355 return $self->{_parent};
133             }
134              
135             sub getDepth {
136 34648     34648 1 41060 my ($self) = @_;
137 34648         45883 return $self->{_depth};
138             }
139              
140             sub getNodeValue {
141 15376     15376 1 20305 my ($self) = @_;
142 15376         23717 return $self->{_node};
143             }
144              
145             sub getLeft {
146 8130     8130 1 11507 my ($self) = @_;
147 8130         14475 return $self->{_left};
148             }
149              
150             sub getRight {
151 8262     8262 1 11787 my ($self) = @_;
152 8262         14638 return $self->{_right};
153             }
154              
155             ## ----------------------------------------------------------------------------
156             ## informational
157              
158             sub isLeaf {
159 7128     7128 1 9555 my ($self) = @_;
160 7128   100     18825 return (!defined $self->{_left} && !defined $self->{_right});
161             }
162              
163             sub hasLeft {
164 28930     28930 1 36462 my ($self) = @_;
165 28930         63487 return defined $self->{_left};
166             }
167              
168             sub hasRight {
169 29018     29018 1 36464 my ($self) = @_;
170 29018         62125 return defined $self->{_right};
171             }
172              
173             sub isRoot {
174 31629     31629 1 38060 my ($self) = @_;
175 31629         54186 return not defined $self->{_parent};
176             }
177              
178             ## ----------------------------------------------------------------------------
179             ## misc
180              
181             # NOTE:
182             # Occasionally one wants to have the
183             # depth available for various reasons
184             # of convience. Sometimes that depth
185             # field is not always correct.
186             # If you create your tree in a top-down
187             # manner, this is usually not an issue
188             # since each time you either add a child
189             # or create a tree you are doing it with
190             # a single tree and not a hierarchy.
191             # If however you are creating your tree
192             # bottom-up, then you might find that
193             # when adding hierarchies of trees, your
194             # depth fields are all out of whack.
195             # This is where this method comes into play
196             # it will recurse down the tree and fix the
197             # depth fields appropriately.
198             # This method is called automatically when
199             # a subtree is added to a child array
200             sub fixDepth {
201 3993     3993 1 5059 my ($self) = @_;
202             # make sure the tree's depth
203             # is up to date all the way down
204             $self->traverse(sub {
205 31607     31607   36143 my ($tree) = @_;
206 31607 100       38783 unless ($tree->isRoot()) {
207 31580         40034 $tree->{_depth} = $tree->getParent()->getDepth() + 1;
208             }
209             else {
210 27         39 $tree->{_depth} = 0;
211             }
212             }
213 3993         13535 );
214             }
215              
216             sub traverse {
217 35627     35627 1 45959 my ($self, $func) = @_;
218 35627 100       49459 (defined($func)) || die "Insufficient Arguments : Cannot traverse without traversal function";
219 35626 100       52815 (ref($func) eq "CODE") || die "Incorrect Object Type : traversal function is not a function";
220 35625         57379 $func->($self);
221 35625 100       71819 $self->{_left}->traverse($func) if defined $self->{_left};
222 35625 100       57981 $self->{_right}->traverse($func) if defined $self->{_right};
223             }
224              
225             sub mirror {
226 6024     6024 1 7162 my ($self) = @_;
227             # swap left for right
228 6024         7274 my $temp = $self->{_left};
229 6024         6655 $self->{_left} = $self->{_right};
230 6024         6551 $self->{_right} = $temp;
231             # and recurse
232 6024 100       7496 $self->{_left}->mirror() if $self->hasLeft();
233 6024 100       8169 $self->{_right}->mirror() if $self->hasRight();
234 6024         7253 $self;
235             }
236              
237             sub size {
238 4081     4081 1 25293 my ($self) = @_;
239 4081         4316 my $size = 1;
240 4081 100       4919 $size += $self->{_left}->size() if $self->hasLeft();
241 4081 100       5530 $size += $self->{_right}->size() if $self->hasRight();
242 4081         5132 return $size;
243             }
244              
245             sub height {
246 1027     1027 1 1354 my ($self) = @_;
247 1027         1240 my ($left_height, $right_height) = (0, 0);
248 1027 100       1200 $left_height = $self->{_left}->height() if $self->hasLeft();
249 1027 100       1418 $right_height = $self->{_right}->height() if $self->hasRight();
250 1027 100       1663 return 1 + (($left_height > $right_height) ? $left_height : $right_height);
251             }
252              
253             sub accept {
254 215     215 1 6325 my ($self, $visitor) = @_;
255             # it must be a blessed reference and ...
256 215 100 66     1954 (blessed($visitor) &&
      100        
257             # either a Tree::Simple::Visitor object, or ...
258             ($visitor->isa("Tree::Binary::Visitor") ||
259             # it must be an object which has a 'visit' method avaiable
260             $visitor->can('visit')))
261             || die "Insufficient Arguments : You must supply a valid Visitor object";
262 211         592 $visitor->visit($self);
263             }
264              
265             ## ----------------------------------------------------------------------------
266             ## cloning
267              
268             sub clone {
269 12     12 1 19 my ($self) = @_;
270             # first clone the value in the node
271 12         18 my $cloned_node = _cloneNode($self->getNodeValue());
272             # create a new Tree::Simple object
273             # here with the cloned node, however
274             # we do not assign the parent node
275             # since it really does not make a lot
276             # of sense. To properly clone it would
277             # be to clone back up the tree as well,
278             # which IMO is not intuitive. So in essence
279             # when you clone a tree, you detach it from
280             # any parentage it might have
281 12         20 my $clone = $self->new($cloned_node);
282             # however, because it is a recursive thing
283             # when you clone all the children, and then
284             # add them to the clone, you end up setting
285             # the parent of the children to be that of
286             # the clone (which is correct)
287 12 100       21 $clone->setLeft($self->{_left}->clone()) if $self->hasLeft();
288 12 100       18 $clone->setRight($self->{_right}->clone()) if $self->hasRight();
289             # return the clone
290 12         22 return $clone;
291             }
292              
293              
294             # this allows cloning of single nodes while
295             # retaining connections to a tree, this is sloppy
296             sub cloneShallow {
297 2     2 1 6 my ($self) = @_;
298 2         3 my $cloned_tree = { %{$self} };
  2         10  
299 2         6 bless($cloned_tree, ref($self));
300             # just clone the node (if you can)
301 2         5 my $cloned_node =_cloneNode($self->getNodeValue());
302 2 50       6 (defined($cloned_node)) || die "Node did not clone : " . $self->getNodeValue();
303 2         6 $cloned_tree->setNodeValue($cloned_node);
304 2         5 return $cloned_tree;
305             }
306              
307             # this is a helper function which
308             # recursively clones the node
309             sub _cloneNode {
310 6030     6030   7595 my ($node, $seen) = @_;
311             # create a cache if we dont already
312             # have one to prevent circular refs
313             # from being copied more than once
314 6030 100       10664 $seen = {} unless defined $seen;
315             # now here we go...
316 6030         7126 my $clone;
317             # if it is not a reference, then lets just return it
318 6030 100       12875 return $node unless ref($node);
319             # if it is in the cache, then return that
320 10 50       12 return $seen->{$node} if exists ${$seen}{$node};
  10         17  
321             # if it is an object, then ...
322 10 100       24 if (blessed($node)) {
323             # see if we can clone it
324 4 100       21 if ($node->can('clone')) {
325 2         6 $clone = $node->clone();
326             }
327             # otherwise respect that it does
328             # not want to be cloned
329             else {
330 2         4 $clone = $node;
331             }
332             }
333             else {
334             # if the current slot is a scalar reference, then
335             # dereference it and copy it into the new object
336 6 100 100     29 if (ref($node) eq "SCALAR" || ref($node) eq "REF") {
    100          
    100          
337 3         5 my $var = "";
338 3         5 $clone = \$var;
339 3         2 ${$clone} = _cloneNode(${$node}, $seen);
  3         6  
  3         10  
340             }
341             # if the current slot is an array reference
342             # then dereference it and copy it
343             elsif (ref($node) eq "ARRAY") {
344 1         1 $clone = [ map { _cloneNode($_, $seen) } @{$node} ];
  4         5  
  1         3  
345             }
346             # if the current reference is a hash reference
347             # then dereference it and copy it
348             elsif (ref($node) eq "HASH") {
349 1         1 $clone = {};
350 1         2 foreach my $key (keys %{$node}) {
  1         4  
351 2         4 $clone->{$key} = _cloneNode($node->{$key}, $seen);
352             }
353             }
354             else {
355             # all other ref types are not copied
356 1         2 $clone = $node;
357             }
358             }
359             # store the clone in the cache and
360 10         24 $seen->{$node} = $clone;
361             # then return the clone
362 10         21 return $clone;
363             }
364              
365              
366             ## ----------------------------------------------------------------------------
367             ## Desctructor
368              
369             sub DESTROY {
370 7830     7830   20859 my ($self) = @_;
371             # we need to call DESTORY on all our children
372             # (first checking if they are defined
373             # though since we never know how perl's
374             # garbage collector will work)
375 7830 100       13617 $self->{_left}->DESTROY() if defined $self->{_left};
376 7830 100       12612 $self->{_right}->DESTROY() if defined $self->{_right};
377 7830         10683 $self->{_parent} = undef;
378             }
379              
380             1;
381              
382             __END__