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   81191 use strict;
  11         11  
  11         245  
5 11     11   33 use warnings;
  11         11  
  11         351  
6              
7             our $VERSION = '1.08';
8              
9 11     11   50 use Scalar::Util qw(blessed);
  11         15  
  11         13921  
10              
11             ## ----------------------------------------------------------------------------
12             ## Tree::Binary
13             ## ----------------------------------------------------------------------------
14              
15             ### constructor
16              
17             sub new {
18 71     71 1 1762 my ($_class, $node) = @_;
19 71   66     201 my $class = ref($_class) || $_class;
20 71         58 my $binary_tree = {};
21 71         62 bless($binary_tree, $class);
22 71         76 $binary_tree->_init($node);
23 70         134 return $binary_tree;
24             }
25              
26             ### ---------------------------------------------------------------------------
27             ### methods
28             ### ---------------------------------------------------------------------------
29              
30             ## ----------------------------------------------------------------------------
31             ## private methods
32              
33             sub _init {
34 8542     8542   6089 my ($self, $node) = @_;
35 8542 100       9930 (defined($node)) || die "Insufficient Arguments : you must provide a node value";
36             # set the value of the unique id
37 8540         26174 ($self->{_uid}) = ("$self" =~ /\((.*?)\)$/);
38             # set the value of the node
39 8540         7287 $self->{_node} = $node;
40             # create the child nodes
41 8540         7119 $self->{_left} = undef;
42 8540         5690 $self->{_right} = undef;
43             # initialize the parent and depth here
44 8540         5698 $self->{_parent} = undef;
45 8540         12001 $self->{_depth} = 0;
46             }
47              
48             ## ----------------------------------------------------------------------------
49             ## mutators
50              
51             sub setNodeValue {
52 6     6 1 200 my ($self, $node_value) = @_;
53 6 100       21 (defined($node_value)) || die "Insufficient Arguments : must supply a value for node";
54 5         8 $self->{_node} = $node_value;
55             }
56              
57             sub setUID {
58 2     2 1 819 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 4250     4250 1 4345 my ($self, $tree) = @_;
65 4250 100 100     16620 (blessed($tree) && $tree->isa("Tree::Binary"))
66             || die "Insufficient Arguments : left argument must be a Tree::Binary object";
67 4246         3803 $tree->{_parent} = $self;
68 4246         3078 $self->{_left} = $tree;
69 4246 100       4293 unless ($tree->isLeaf()) {
70 2347         2355 $tree->fixDepth();
71             }
72             else {
73 1899         1930 $tree->{_depth} = $self->getDepth() + 1;
74             }
75 4246         7170 $self;
76             }
77              
78             sub removeLeft {
79 38     38 1 339 my ($self) = @_;
80 38 100       41 ($self->hasLeft()) || die "Illegal Operation: cannot remove node that doesnt exist";
81 37         34 my $left = $self->{_left};
82 37         34 $left->{_parent} = undef;
83 37 100       41 unless ($left->isLeaf()) {
84 8         12 $left->fixDepth();
85             }
86             else {
87 29         29 $left->{_depth} = 0;
88             }
89 37         48 $self->{_left} = undef;
90 37         46 return $left;
91             }
92              
93             sub setRight {
94 4171     4171 1 4327 my ($self, $tree) = @_;
95 4171 100 100     16152 (blessed($tree) && $tree->isa("Tree::Binary"))
96             || die "Insufficient Arguments : right argument must be a Tree::Binary object";
97 4167         3734 $tree->{_parent} = $self;
98 4167         3035 $self->{_right} = $tree;
99 4167 100       3992 unless ($tree->isLeaf()) {
100 2363         2446 $tree->fixDepth();
101             }
102             else {
103 1804         1924 $tree->{_depth} = $self->getDepth() + 1;
104             }
105 4167         7092 $self;
106             }
107              
108             sub removeRight {
109 21     21 1 745 my ($self) = @_;
110 21 100       27 ($self->hasRight()) || die "Illegal Operation: cannot remove node that doesnt exist";
111 20         21 my $right = $self->{_right};
112 20         15 $right->{_parent} = undef;
113 20 100       22 unless ($right->isLeaf()) {
114 11         22 $right->fixDepth();
115             }
116             else {
117 9         10 $right->{_depth} = 0;
118             }
119 20         35 $self->{_right} = undef;
120 20         29 return $right;
121             }
122              
123             ## ----------------------------------------------------------------------------
124             ## accessors
125              
126             sub getUID {
127 2     2 1 3 my ($self) = @_;
128 2         5 return $self->{_uid};
129             }
130              
131             sub getParent {
132 40887     40887 1 23283 my ($self)= @_;
133 40887         42210 return $self->{_parent};
134             }
135              
136             sub getDepth {
137 44559     44559 1 27603 my ($self) = @_;
138 44559         42448 return $self->{_depth};
139             }
140              
141             sub getNodeValue {
142 18331     18331 1 12607 my ($self) = @_;
143 18331         20442 return $self->{_node};
144             }
145              
146             sub getLeft {
147 10998     10998 1 7748 my ($self) = @_;
148 10998         14327 return $self->{_left};
149             }
150              
151             sub getRight {
152 10171     10171 1 7456 my ($self) = @_;
153 10171         13566 return $self->{_right};
154             }
155              
156             ## ----------------------------------------------------------------------------
157             ## informational
158              
159             sub isLeaf {
160 8507     8507 1 6049 my ($self) = @_;
161 8507   100     21975 return (!defined $self->{_left} && !defined $self->{_right});
162             }
163              
164             sub hasLeft {
165 35841     35841 1 24136 my ($self) = @_;
166 35841         66397 return defined $self->{_left};
167             }
168              
169             sub hasRight {
170 34961     34961 1 23224 my ($self) = @_;
171 34961         63140 return defined $self->{_right};
172             }
173              
174             sub isRoot {
175 40905     40905 1 24539 my ($self) = @_;
176 40905         52019 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 4737     4737 1 3347 my ($self) = @_;
203             # make sure the tree's depth
204             # is up to date all the way down
205             $self->traverse(sub {
206 40883     40883   22752 my ($tree) = @_;
207 40883 100       34629 unless ($tree->isRoot()) {
208 40856         33810 $tree->{_depth} = $tree->getParent()->getDepth() + 1;
209             }
210             else {
211 27         26 $tree->{_depth} = 0;
212             }
213             }
214 4737         11215 );
215             }
216              
217             sub traverse {
218 45691     45691 1 30383 my ($self, $func) = @_;
219 45691 100       48841 (defined($func)) || die "Insufficient Arguments : Cannot traverse without traversal function";
220 45690 100       53002 (ref($func) eq "CODE") || die "Incorrect Object Type : traversal function is not a function";
221 45689         39812 $func->($self);
222 45689 100       68334 $self->{_left}->traverse($func) if defined $self->{_left};
223 45689 100       62186 $self->{_right}->traverse($func) if defined $self->{_right};
224             }
225              
226             sub mirror {
227 7206     7206 1 4220 my ($self) = @_;
228             # swap left for right
229 7206         4648 my $temp = $self->{_left};
230 7206         4771 $self->{_left} = $self->{_right};
231 7206         4462 $self->{_right} = $temp;
232             # and recurse
233 7206 100       5971 $self->{_left}->mirror() if $self->hasLeft();
234 7206 100       6551 $self->{_right}->mirror() if $self->hasRight();
235 7206         5167 $self;
236             }
237              
238             sub size {
239 4869     4869 1 18120 my ($self) = @_;
240 4869         2868 my $size = 1;
241 4869 100       4100 $size += $self->{_left}->size() if $self->hasLeft();
242 4869 100       4670 $size += $self->{_right}->size() if $self->hasRight();
243 4869         3716 return $size;
244             }
245              
246             sub height {
247 1224     1224 1 810 my ($self) = @_;
248 1224         800 my ($left_height, $right_height) = (0, 0);
249 1224 100       975 $left_height = $self->{_left}->height() if $self->hasLeft();
250 1224 100       1147 $right_height = $self->{_right}->height() if $self->hasRight();
251 1224 100       1415 return 1 + (($left_height > $right_height) ? $left_height : $right_height);
252             }
253              
254             sub accept {
255 215     215 1 4789 my ($self, $visitor) = @_;
256             # it must be a blessed reference and ...
257 215 100 66     2021 (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         534 $visitor->visit($self);
264             }
265              
266             ## ----------------------------------------------------------------------------
267             ## cloning
268              
269             sub clone {
270 12     12 1 11 my ($self) = @_;
271             # first clone the value in the node
272 12         14 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         14 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       13 $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         17 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         8  
300 2         5 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         4 return $cloned_tree;
306             }
307              
308             # this is a helper function which
309             # recursively clones the node
310             sub _cloneNode {
311 7212     7212   4905 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 7212 100       10253 $seen = {} unless defined $seen;
316             # now here we go...
317 7212         4345 my $clone;
318             # if it is not a reference, then lets just return it
319 7212 100       13970 return $node unless ref($node);
320             # if it is in the cache, then return that
321 10 50       6 return $seen->{$node} if exists ${$seen}{$node};
  10         19  
322             # if it is an object, then ...
323 10 100       18 if (blessed($node)) {
324             # see if we can clone it
325 4 100       31 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         2 my $var = "";
339 3         2 $clone = \$var;
340 3         3 ${$clone} = _cloneNode(${$node}, $seen);
  3         3  
  3         7  
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         1 $clone = [ map { _cloneNode($_, $seen) } @{$node} ];
  4         2  
  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         0 foreach my $key (keys %{$node}) {
  1         4  
352 2         2 $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         23 $seen->{$node} = $clone;
362             # then return the clone
363 10         14 return $clone;
364             }
365              
366              
367             ## ----------------------------------------------------------------------------
368             ## Desctructor
369              
370             sub DESTROY {
371 9843     9843   12427 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 9843 100       12416 $self->{_left}->DESTROY() if defined $self->{_left};
377 9843 100       12074 $self->{_right}->DESTROY() if defined $self->{_right};
378 9843         7724 $self->{_parent} = undef;
379             }
380              
381             1;
382              
383             __END__