File Coverage

blib/lib/Tree/Binary.pm
Criterion Covered Total %
statement 159 159 100.0
branch 72 74 97.3
condition 18 21 85.7
subroutine 32 32 100.0
pod 25 25 100.0
total 306 311 98.3


line stmt bran cond sub pod time code
1              
2             package Tree::Binary;
3              
4 11     11   80330 use strict;
  11         12  
  11         248  
5 11     11   28 use warnings;
  11         10  
  11         337  
6              
7             our $VERSION = '1.07';
8              
9 11     11   34 use Scalar::Util qw(blessed);
  11         11  
  11         14183  
10              
11             ## ----------------------------------------------------------------------------
12             ## Tree::Binary
13             ## ----------------------------------------------------------------------------
14              
15             ### constructor
16              
17             sub new {
18 71     71 1 1778 my ($_class, $node) = @_;
19 71   66     160 my $class = ref($_class) || $_class;
20 71         60 my $binary_tree = {};
21 71         63 bless($binary_tree, $class);
22 71         71 $binary_tree->_init($node);
23 70         126 return $binary_tree;
24             }
25              
26             ### ---------------------------------------------------------------------------
27             ### methods
28             ### ---------------------------------------------------------------------------
29              
30             ## ----------------------------------------------------------------------------
31             ## private methods
32              
33             sub _init {
34 6239     6239   4216 my ($self, $node) = @_;
35 6239 100       7163 (defined($node)) || die "Insufficient Arguments : you must provide a node value";
36             # set the value of the unique id
37 6237         19425 ($self->{_uid}) = ("$self" =~ /\((.*?)\)$/);
38             # set the value of the node
39 6237         5208 $self->{_node} = $node;
40             # create the child nodes
41 6237         4395 $self->{_left} = undef;
42 6237         4191 $self->{_right} = undef;
43             # initialize the parent and depth here
44 6237         4352 $self->{_parent} = undef;
45 6237         9046 $self->{_depth} = 0;
46             }
47              
48             ## ----------------------------------------------------------------------------
49             ## mutators
50              
51             sub setNodeValue {
52 6     6 1 201 my ($self, $node_value) = @_;
53 6 100       20 (defined($node_value)) || die "Insufficient Arguments : must supply a value for node";
54 5         9 $self->{_node} = $node_value;
55             }
56              
57             sub setUID {
58 2     2 1 817 my ($self, $uid) = @_;
59 2 100       11 ($uid) || die "Insufficient Arguments : Custom Unique ID's must be a true value";
60 1         2 $self->{_uid} = $uid;
61             }
62              
63             sub setLeft {
64 3081     3081 1 3179 my ($self, $tree) = @_;
65 3081 100 100     12505 (blessed($tree) && $tree->isa("Tree::Binary"))
66             || die "Insufficient Arguments : left argument must be a Tree::Binary object";
67 3077         2697 $tree->{_parent} = $self;
68 3077         2235 $self->{_left} = $tree;
69 3077 100       3169 unless ($tree->isLeaf()) {
70 1582         1739 $tree->fixDepth();
71             }
72             else {
73 1495         1685 $tree->{_depth} = $self->getDepth() + 1;
74             }
75 3077         5215 $self;
76             }
77              
78             sub removeLeft {
79 38     38 1 242 my ($self) = @_;
80 38 100       42 ($self->hasLeft()) || die "Illegal Operation: cannot remove node that doesnt exist";
81 37         35 my $left = $self->{_left};
82 37         36 $left->{_parent} = undef;
83 37 100       38 unless ($left->isLeaf()) {
84 8         13 $left->fixDepth();
85             }
86             else {
87 29         26 $left->{_depth} = 0;
88             }
89 37         42 $self->{_left} = undef;
90 37         52 return $left;
91             }
92              
93             sub setRight {
94 3037     3037 1 2849 my ($self, $tree) = @_;
95 3037 100 100     11834 (blessed($tree) && $tree->isa("Tree::Binary"))
96             || die "Insufficient Arguments : right argument must be a Tree::Binary object";
97 3033         2627 $tree->{_parent} = $self;
98 3033         2069 $self->{_right} = $tree;
99 3033 100       2910 unless ($tree->isLeaf()) {
100 1682         1617 $tree->fixDepth();
101             }
102             else {
103 1351         1421 $tree->{_depth} = $self->getDepth() + 1;
104             }
105 3033         5208 $self;
106             }
107              
108             sub removeRight {
109 21     21 1 647 my ($self) = @_;
110 21 100       25 ($self->hasRight()) || die "Illegal Operation: cannot remove node that doesnt exist";
111 20         21 my $right = $self->{_right};
112 20         16 $right->{_parent} = undef;
113 20 100       23 unless ($right->isLeaf()) {
114 11         20 $right->fixDepth();
115             }
116             else {
117 9         10 $right->{_depth} = 0;
118             }
119 20         37 $self->{_right} = undef;
120 20         29 return $right;
121             }
122              
123             ## ----------------------------------------------------------------------------
124             ## accessors
125              
126             sub getUID {
127 2     2 1 4 my ($self) = @_;
128 2         3 return $self->{_uid};
129             }
130              
131             sub getParent {
132 26733     26733 1 15982 my ($self)= @_;
133 26733         28716 return $self->{_parent};
134             }
135              
136             sub getDepth {
137 29548     29548 1 17809 my ($self) = @_;
138 29548         27948 return $self->{_depth};
139             }
140              
141             sub getNodeValue {
142 13396     13396 1 9171 my ($self) = @_;
143 13396         15194 return $self->{_node};
144             }
145              
146             sub getLeft {
147 6751     6751 1 5350 my ($self) = @_;
148 6751         8990 return $self->{_left};
149             }
150              
151             sub getRight {
152 7508     7508 1 5780 my ($self) = @_;
153 7508         10343 return $self->{_right};
154             }
155              
156             ## ----------------------------------------------------------------------------
157             ## informational
158              
159             sub isLeaf {
160 6204     6204 1 4749 my ($self) = @_;
161 6204   100     15669 return (!defined $self->{_left} && !defined $self->{_right});
162             }
163              
164             sub hasLeft {
165 24839     24839 1 16563 my ($self) = @_;
166 24839         46386 return defined $self->{_left};
167             }
168              
169             sub hasRight {
170 25564     25564 1 16351 my ($self) = @_;
171 25564         48207 return defined $self->{_right};
172             }
173              
174             sub isRoot {
175 26751     26751 1 17939 my ($self) = @_;
176 26751         33449 return not defined $self->{_parent};
177             }
178              
179             ## ----------------------------------------------------------------------------
180             ## misc
181              
182             # NOTE:
183             # Occasionally one wants to have the
184             # depth available for various reasons
185             # of convience. Sometimes that depth
186             # field is not always correct.
187             # If you create your tree in a top-down
188             # manner, this is usually not an issue
189             # since each time you either add a child
190             # or create a tree you are doing it with
191             # a single tree and not a hierarchy.
192             # If however you are creating your tree
193             # bottom-up, then you might find that
194             # when adding hierarchies of trees, your
195             # depth fields are all out of whack.
196             # This is where this method comes into play
197             # it will recurse down the tree and fix the
198             # depth fields appropriately.
199             # This method is called automatically when
200             # a subtree is added to a child array
201             sub fixDepth {
202 3291     3291 1 2099 my ($self) = @_;
203             # make sure the tree's depth
204             # is up to date all the way down
205             $self->traverse(sub {
206 26729     26729   16134 my ($tree) = @_;
207 26729 100       22952 unless ($tree->isRoot()) {
208 26702         22408 $tree->{_depth} = $tree->getParent()->getDepth() + 1;
209             }
210             else {
211 27         27 $tree->{_depth} = 0;
212             }
213             }
214 3291         8085 );
215             }
216              
217             sub traverse {
218 30221     30221 1 19905 my ($self, $func) = @_;
219 30221 100       31863 (defined($func)) || die "Insufficient Arguments : Cannot traverse without traversal function";
220 30220 100       34380 (ref($func) eq "CODE") || die "Incorrect Object Type : traversal function is not a function";
221 30219         27498 $func->($self);
222 30219 100       45320 $self->{_left}->traverse($func) if defined $self->{_left};
223 30219 100       42292 $self->{_right}->traverse($func) if defined $self->{_right};
224             }
225              
226             sub mirror {
227 5232     5232 1 3174 my ($self) = @_;
228             # swap left for right
229 5232         3305 my $temp = $self->{_left};
230 5232         3389 $self->{_left} = $self->{_right};
231 5232         3199 $self->{_right} = $temp;
232             # and recurse
233 5232 100       4281 $self->{_left}->mirror() if $self->hasLeft();
234 5232 100       5126 $self->{_right}->mirror() if $self->hasRight();
235 5232         3841 $self;
236             }
237              
238             sub size {
239 3553     3553 1 16996 my ($self) = @_;
240 3553         1994 my $size = 1;
241 3553 100       3000 $size += $self->{_left}->size() if $self->hasLeft();
242 3553 100       3449 $size += $self->{_right}->size() if $self->hasRight();
243 3553         2726 return $size;
244             }
245              
246             sub height {
247 895     895 1 609 my ($self) = @_;
248 895         596 my ($left_height, $right_height) = (0, 0);
249 895 100       777 $left_height = $self->{_left}->height() if $self->hasLeft();
250 895 100       897 $right_height = $self->{_right}->height() if $self->hasRight();
251 895 100       1120 return 1 + (($left_height > $right_height) ? $left_height : $right_height);
252             }
253              
254             sub accept {
255 215     215 1 4283 my ($self, $visitor) = @_;
256             # it must be a blessed reference and ...
257 215 100 66     1953 (blessed($visitor) &&
      66        
258             # either a Tree::Simple::Visitor object, or ...
259             ($visitor->isa("Tree::Binary::Visitor") ||
260             # it must be an object which has a 'visit' method avaiable
261             $visitor->can('visit')))
262             || die "Insufficient Arguments : You must supply a valid Visitor object";
263 211         526 $visitor->visit($self);
264             }
265              
266             ## ----------------------------------------------------------------------------
267             ## cloning
268              
269             sub clone {
270 12     12 1 9 my ($self) = @_;
271             # first clone the value in the node
272 12         13 my $cloned_node = _cloneNode($self->getNodeValue());
273             # create a new Tree::Simple object
274             # here with the cloned node, however
275             # we do not assign the parent node
276             # since it really does not make a lot
277             # of sense. To properly clone it would
278             # be to clone back up the tree as well,
279             # which IMO is not intuitive. So in essence
280             # when you clone a tree, you detach it from
281             # any parentage it might have
282 12         16 my $clone = $self->new($cloned_node);
283             # however, because it is a recursive thing
284             # when you clone all the children, and then
285             # add them to the clone, you end up setting
286             # the parent of the children to be that of
287             # the clone (which is correct)
288 12 100       15 $clone->setLeft($self->{_left}->clone()) if $self->hasLeft();
289 12 100       16 $clone->setRight($self->{_right}->clone()) if $self->hasRight();
290             # return the clone
291 12         23 return $clone;
292             }
293              
294              
295             # this allows cloning of single nodes while
296             # retaining connections to a tree, this is sloppy
297             sub cloneShallow {
298 2     2 1 4 my ($self) = @_;
299 2         2 my $cloned_tree = { %{$self} };
  2         9  
300 2         4 bless($cloned_tree, ref($self));
301             # just clone the node (if you can)
302 2         3 my $cloned_node =_cloneNode($self->getNodeValue());
303 2 50       4 (defined($cloned_node)) || die "Node did not clone : " . $self->getNodeValue();
304 2         4 $cloned_tree->setNodeValue($cloned_node);
305 2         3 return $cloned_tree;
306             }
307              
308             # this is a helper function which
309             # recursively clones the node
310             sub _cloneNode {
311 5238     5238   3595 my ($node, $seen) = @_;
312             # create a cache if we dont already
313             # have one to prevent circular refs
314             # from being copied more than once
315 5238 100       7521 $seen = {} unless defined $seen;
316             # now here we go...
317 5238         3240 my $clone;
318             # if it is not a reference, then lets just return it
319 5238 100       10240 return $node unless ref($node);
320             # if it is in the cache, then return that
321 10 50       5 return $seen->{$node} if exists ${$seen}{$node};
  10         19  
322             # if it is an object, then ...
323 10 100       19 if (blessed($node)) {
324             # see if we can clone it
325 4 100       25 if ($node->can('clone')) {
326 2         4 $clone = $node->clone();
327             }
328             # otherwise respect that it does
329             # not want to be cloned
330             else {
331 2         2 $clone = $node;
332             }
333             }
334             else {
335             # if the current slot is a scalar reference, then
336             # dereference it and copy it into the new object
337 6 100 100     24 if (ref($node) eq "SCALAR" || ref($node) eq "REF") {
    100          
    100          
338 3         3 my $var = "";
339 3         2 $clone = \$var;
340 3         3 ${$clone} = _cloneNode(${$node}, $seen);
  3         4  
  3         6  
341             }
342             # if the current slot is an array reference
343             # then dereference it and copy it
344             elsif (ref($node) eq "ARRAY") {
345 1         2 $clone = [ map { _cloneNode($_, $seen) } @{$node} ];
  4         3  
  1         2  
346             }
347             # if the current reference is a hash reference
348             # then dereference it and copy it
349             elsif (ref($node) eq "HASH") {
350 1         1 $clone = {};
351 1         1 foreach my $key (keys %{$node}) {
  1         6  
352 2         4 $clone->{$key} = _cloneNode($node->{$key}, $seen);
353             }
354             }
355             else {
356             # all other ref types are not copied
357 1         1 $clone = $node;
358             }
359             }
360             # store the clone in the cache and
361 10         20 $seen->{$node} = $clone;
362             # then return the clone
363 10         13 return $clone;
364             }
365              
366              
367             ## ----------------------------------------------------------------------------
368             ## Desctructor
369              
370             sub DESTROY {
371 6738     6738   11804 my ($self) = @_;
372             # we need to call DESTORY on all our children
373             # (first checking if they are defined
374             # though since we never know how perl's
375             # garbage collector will work)
376 6738 100       8286 $self->{_left}->DESTROY() if defined $self->{_left};
377 6738 100       7908 $self->{_right}->DESTROY() if defined $self->{_right};
378 6738         5315 $self->{_parent} = undef;
379             }
380              
381             1;
382              
383             __END__