line
stmt
bran
cond
sub
pod
time
code
1
#!/usr/bin/perl -I/home/phil/perl/cpan/DataTableText/lib
2
#-------------------------------------------------------------------------------
3
# Tree operations
4
# Philip R Brenan at gmail dot com, Appa Apps Ltd Inc., 2020
5
#-------------------------------------------------------------------------------
6
# podDocumentation
7
package Tree::Ops;
8
our $VERSION = 20200725;
9
require v5.26;
10
1
1
1242
use warnings FATAL => qw(all);
1
8
1
37
11
1
1
9
use strict;
1
2
1
33
12
1
1
5
use Carp;
1
2
1
95
13
1
1
563
use Data::Dump qw(dump);
1
8065
1
62
14
1
1
3985
use Data::Table::Text qw(:all);
1
148412
1
4370
15
1
1
35
use feature qw(current_sub say);
1
6
1
250
16
1
1
1112
use experimental qw(smartmatch);
1
4780
1
6
17
18
my $logFile = q(/home/phil/z/z/z/zzz.txt); # Log printed results if developing
19
20
#D1 Build # Create a tree. There is no implicit ordering applied to the tree, the relationships between parents and children within the tree are as established by the user and can be reorganized at will using the methods in this module.
21
22
sub new(;$$) #S Create a new child optionally recording the specified key or value.
23
263
263
1
809
{my ($key, $value) = @_; # Key, value
24
263
706
genHash(__PACKAGE__, # Child in the tree.
25
children => [], # Children of this child.
26
key => $key, # Key for this child - any thing that can be compared with the L operator.
27
value => $value, # Value for this child.
28
parent => undef, # Parent for this child.
29
lastChild => undef, # Last active child chain - enables us to find the currently open scope from the start if the tree.
30
);
31
}
32
33
sub activeScope($) # Locate the active scope in a tree.
34
426
426
1
592
{my ($tree) = @_; # Tree
35
426
518
my $active; # Latest active child
36
426
832
for(my $l = $tree; $l; $l = $l->lastChild) {$active = $l} # Skip down edge of parse tree to deepest active child.
1331
24384
37
426
2065
$active
38
}
39
40
sub setParentOfChild($$) #P Set the parent of a child and return the child.
41
227
227
1
395
{my ($child, $parent) = @_; # Child, parent
42
227
3655
$child->parent = $parent; # Parent child
43
227
1182
$child
44
}
45
46
sub open($;$$) # Add a child and make it the currently active scope into which new children will be added.
47
213
213
1
359
{my ($tree, $key, $value) = @_; # Tree, key, value to be recorded in the interior child being opened
48
213
377
my $parent = activeScope $tree; # Active parent
49
213
466
my $child = new $key, $value; # New child
50
213
16940
push $parent->children->@*, $child; # Place new child last under parent
51
213
4279
$parent->lastChild = $child; # Make child active
52
213
950
setParentOfChild $child, $parent # Parent child
53
}
54
55
sub close($) # Close the current scope returning to the previous scope.
56
210
210
1
328
{my ($tree) = @_; # Tree
57
210
320
my $parent = activeScope $tree; # Locate active scope
58
210
100
3433
delete $parent->parent->{lastChild} if $parent->parent; # Close scope
59
210
4795
$parent
60
}
61
62
sub single($;$$) # Add one child in the current scope.
63
129
129
1
241
{my ($tree, $key, $value) = @_; # Tree, key, value to be recorded in the child being created
64
129
289
$tree->open($key, $value); # Open scope
65
129
255
$tree->close; # Close scope immediately
66
}
67
68
sub include($$) # Include the specified tree in the currently open scope.
69
1
1
1
5
{my ($tree, $include) = @_; # Tree being built, tree to include
70
1
4
my $parent = activeScope $tree; # Active parent
71
1
18
my $n = new $include->key, $include->value; # New intermediate child
72
1
136
$n->children = $include->children; # Include children
73
1
37
$n->parent = $parent; # Parent new node
74
1
24
$parent->putLast($n) # Include node
75
}
76
77
sub fromLetters($) # Create a tree from a string of letters returning the children created in alphabetic order - useful for testing.
78
20
20
1
53
{my ($letters) = @_; # String of letters and ( ).
79
20
59
my $t = new(my $s = 'a');
80
20
1493
my @l = split //, $letters;
81
82
20
38
my @c; # Last letter seen
83
20
89
for my $l(split(//, $letters), '') # Each letter
84
371
582
{my $c = shift @c; # Last letter
85
371
50
782
if ($l eq '(') {$t->open ($c) if $c} # Open new scope
77
100
197
100
86
77
100
234
elsif ($l eq ')') {$t->single($c) if $c; $t->close} # Close scope
77
154
87
217
100
451
else {$t->single($c) if $c; @c = $l} # Save current letter as last letter
217
524
88
}
89
90
20
95
sort {$a->key cmp $b->key} $t->by # Sorted results
514
10169
91
}
92
93
#D1 Navigation # Navigate through a tree.
94
95
sub first($) # Get the first child under the specified parent.
96
84
84
1
256
{my ($parent) = @_; # Parent
97
84
1418
$parent->children->[0]
98
}
99
100
sub last($) # Get the last child under the specified parent.
101
68
68
1
168
{my ($parent) = @_; # Parent
102
68
1116
$parent->children->[-1]
103
}
104
105
sub indexOfChildInParent($) #P Get the index of a child within the specified parent.
106
152
152
1
282
{my ($child) = @_; # Child
107
152
50
2575
return undef unless my $parent = $child->parent; # Parent
108
152
2959
my $c = $parent->children; # Siblings
109
152
100
820
for(keys @$c) {return $_ if $$c[$_] == $child} # Locate child and return index
295
1440
110
undef # Root has no index
111
0
0
}
112
113
sub next($) # Get the next sibling following the specified child.
114
54
54
1
103
{my ($child) = @_; # Child
115
54
100
880
return undef unless my $parent = $child->parent; # Parent
116
50
998
my $c = $parent->children; # Siblings
117
50
100
66
377
return undef if @$c == 0 or $$c[-1] == $child; # No next child
118
49
106
$$c[+1 + indexOfChildInParent $child] # Next child
119
}
120
121
sub prev($) # Get the previous sibling of the specified child.
122
64
64
1
134
{my ($child) = @_; # Child
123
64
100
1069
return undef unless my $parent = $child->parent; # Parent
124
56
1082
my $c = $parent->children; # Siblings
125
56
100
66
424
return undef if @$c == 0 or $$c[0] == $child; # No previous child
126
55
130
$$c[-1 + indexOfChildInParent $child] # Previous child
127
}
128
129
sub firstMost($) # Return the first most descendant child in the tree starting at this parent or else return B if this parent has no children.
130
19
19
1
89
{my ($parent) = @_; # Parent
131
19
37
my $f;
132
19
38
for(my $p = $parent; $p; $p = $p->first) {$f = $p} # Go first most
44
188
133
19
180
$f
134
}
135
136
sub nextMost($) # Return the next child with no children, i.e. the next leaf of the tree, else return B if there is no such child.
137
20
20
1
48
{my ($child) = @_; # Current leaf
138
20
100
344
return firstMost $child if $child->children->@*; # First most child if we are not starting on a child with no children - i.e. on a leaf.
139
9
50
my $p = $child; # Traverse upwards and then right
140
9
20
$p = $p->parent while $p->isLast; # Traverse upwards
141
9
100
68
return undef unless $p = $p->next; # Traverse right else we are at the root
142
7
18
firstMost $p # First most child
143
}
144
145
sub prevMost($) # Return the previous child with no children, i.e. the previous leaf of the tree, else return B if there is no such child.
146
21
21
1
49
{my ($child) = @_; # Current leaf
147
21
35
my $p = $child; # Traverse upwards and then left
148
21
45
$p = $p->parent while $p->isFirst; # Traverse upwards
149
21
100
166
return undef unless $p = $p->prev; # Traverse left else we are at the root
150
15
35
lastMost $p # Last most child
151
}
152
153
sub lastMost($) # Return the last most descendant child in the tree starting at this parent or else return B if this parent has no children.
154
17
17
1
30
{my ($parent) = @_; # Parent
155
17
24
my $f;
156
17
42
for(my $p = $parent; $p; $p = $p->last) {$f = $p} # Go last most
32
124
157
17
188
$f
158
}
159
160
sub topMost($) # Return the top most parent in the tree containing the specified child.
161
1
1
1
4
{my ($child) = @_; # Child
162
1
100
5
for(my $p = $child; $p;) {return $p unless my $q = $p->parent; $p = $q} # Go up
4
67
3
17
163
0
0
confess "Child required";
164
}
165
166
sub mostRecentCommonAncestor($$) # Find the most recent common ancestor of the specified children.
167
2
2
1
8
{my ($first, $second) = @_; # First child, second child
168
2
50
7
return $first if $first == $second; # Same first and second child
169
2
7
my @f = context $first; # Context of first child
170
2
5
my @s = context $second; # Context of second child
171
2
33
4
my $c; $c = pop @f, pop @s while @f and @s and $f[-1] == $s[-1]; # Remove common ancestors
2
66
25
172
2
11
$c
173
}
174
175
sub go($@) # Return the child at the end of the path starting at the specified parent. A path is a list of zero based children numbers. Return B if the path is not valid.
176
10
10
1
30
{my ($parent, @path) = @_; # Parent, list of zero based children numbers
177
10
15
my $p = $parent; # Start
178
10
50
12
my $q; defined($q = $p->children->[$_]) ? $p = $q : return undef for @path; # Down # Same first and second child
10
157
179
10
381
$p
180
}
181
182
#D1 Location # Verify the current location.
183
184
sub context($) # Get the context of the current child.
185
21
21
1
34
{my ($child) = @_; # Child
186
21
31
my @c; # Context
187
21
44
for(my $c = $child; $c; $c = $c->parent) {push @c, $c} # Walk up
88
1679
188
@c
189
21
147
}
190
191
sub isFirst($) # Return the specified child if that child is first under its parent, else return B.
192
80
80
1
691
{my ($child) = @_; # Child
193
80
100
1317
return undef unless my $parent = $child->parent; # Parent
194
72
100
1438
$parent->children->[0] == $child ? $child : undef # There will be at least one child
195
}
196
197
sub isLast($) # Return the specified child if that child is last under its parent, else return B.
198
64
64
1
565
{my ($child) = @_; # Child
199
64
100
1020
return undef unless my $parent = $child->parent; # Parent
200
60
1172
my $c = $parent->children;
201
60
100
1106
$parent->children->[-1] == $child ? $child : undef # There will be at least one child
202
}
203
204
sub isTop($) # Return the specified parent if that parent is the top most parent in the tree.
205
2
2
1
6
{my ($parent) = @_; # Parent
206
2
100
38
$parent->parent ? undef : $parent
207
}
208
209
sub singleChildOfParent($) # Return the only child of this parent if the parent has an only child, else B
210
1
1
1
3
{my ($parent) = @_; # Parent
211
1
50
18
$parent->children->@* == 1 ? $parent->children->[0] : undef # Return only child if it exists
212
}
213
214
sub empty($) # Return the specified parent if it has no children else B
215
2
2
1
5
{my ($parent) = @_; # Parent
216
2
100
37
$parent->children->@* == 0 ? $parent : undef
217
}
218
219
#D1 Put # Insert children into a tree.
220
221
sub putFirst($$) # Place a new child first under the specified parent and return the child.
222
4
4
1
77
{my ($parent, $child) = @_; # Parent, child
223
4
69
unshift $parent->children->@*, $child; # Place child
224
4
28
setParentOfChild $child, $parent # Parent child
225
}
226
227
sub putLast($$) # Place a new child last under the specified parent and return the child.
228
6
6
1
93
{my ($parent, $child) = @_; # Parent, child
229
6
106
push $parent->children->@*, $child; # Place child
230
6
41
setParentOfChild $child, $parent # Parent child
231
}
232
233
sub putNext($$) # Place a new child after the specified child.
234
2
2
1
114
{my ($child, $new) = @_; # Existing child, new child
235
2
50
8
return undef unless defined(my $i = indexOfChildInParent $child); # Locate child within parent
236
2
43
splice $child->parent->children->@*, $i, 1, $child, $new; # Place new child
237
2
87
setParentOfChild $new, $child->parent # Parent child
238
}
239
240
sub putPrev($$) # Place a new child before the specified child.
241
2
2
1
90
{my ($child, $new) = @_; # Child, new child
242
2
50
6
return undef unless defined(my $i = indexOfChildInParent($child)); # Locate child within parent
243
2
46
splice $child->parent->children->@*, $i, 1, $new, $child; # Place new child
244
2
104
setParentOfChild $new, $child->parent # Parent child
245
}
246
247
#D1 Steps # Move the start or end of a scope forwards or backwards as suggested by Alex Monroe.
248
249
sub step($) # Make the first child of the specified parent the parents previous sibling and return the parent. In effect this moves the start of the parent one step forwards.
250
1
1
1
3
{my ($parent) = @_; # Parent
251
1
50
4
return undef unless my $f = $parent->first; # First child
252
1
11
putPrev $parent, cut $f; # Place first child
253
1
17
$parent
254
}
255
256
sub stepEnd($) # Make the next sibling of the specified parent the parents last child and return the parent. In effect this moves the end of the parent one step forwards.
257
3
3
1
9
{my ($parent) = @_; # Parent
258
3
50
10
return undef unless my $n = $parent->next; # Next sibling
259
3
12
putLast $parent, cut $n; # Place next sibling as first child
260
3
23
$parent
261
}
262
263
sub stepBack # Make the previous sibling of the specified parent the parents first child and return the parent. In effect this moves the start of the parent one step backwards.
264
2
2
1
9
{my ($parent) = @_; # Parent
265
2
50
17
return undef unless my $p = $parent->prev; # Previous sibling
266
2
15
putFirst $parent, cut $p; # Place previous sibling as first child
267
2
33
$parent
268
}
269
270
sub stepEndBack # Make the last child of the specified parent the parents next sibling and return the parent. In effect this moves the end of the parent one step backwards.
271
1
1
1
7
{my ($parent) = @_; # Parent
272
1
50
3
return undef unless my $l = $parent->last; # Last child sibling
273
1
25
putNext $parent, cut $l; # Place last child as first sibling
274
1
16
$parent
275
}
276
277
#D1 Edit # Edit a tree in situ.
278
279
sub cut($) # Cut out a child and all its content and children, return it ready for reinsertion else where.
280
10
10
1
21
{my ($child) = @_; # Child
281
10
50
175
return $child unless my $parent = $child->parent; # The whole tree
282
10
201
splice $parent->children->@*, indexOfChildInParent($child), 1; # Remove child
283
10
64
$child
284
}
285
286
sub dup($) # Duplicate a specified parent and all its descendants returning the root of the resulting tree.
287
1
1
1
4
{my ($parent) = @_; # Parent
288
289
sub # Duplicate a child
290
8
8
101
{my ($old) = @_; # Existing child
291
8
194
my $new = new $old->key, $old->value; # New child
292
8
633
push $new->children->@*, __SUB__->($_) for $old->children->@*; # Duplicate children of child
293
8
100
$new
294
1
7
}->($parent) # Start duplication at parent
295
}
296
297
sub transcribe($) # Duplicate a specified parent and all its descendants recording the mapping in a temporary {transcribed} field in the tree being transcribed. Returns the root parent of the tree being duplicated.
298
1
1
1
3
{my ($parent) = @_; # Parent
299
300
sub # Duplicate a child
301
8
8
125
{my ($old) = @_; # Existing child
302
8
136
my $new = new $old->key, $old->value; # New child
303
8
498
$old->{transcribedTo} = $new; # To where we went
304
8
11
$new->{transcribedFrom} = $old; # From where we came
305
8
156
push $new->children->@*, __SUB__->($_) for $old->children->@*; # Duplicate children of child and record transcription
306
8
113
$new
307
1
8
}->($parent) # Start duplication at parent
308
}
309
310
sub unwrap($) # Unwrap the specified child and return that child.
311
5
5
1
40
{my ($child) = @_; # Child
312
5
50
13
return undef unless defined(my $i = indexOfChildInParent $child); # Locate child within parent
313
5
86
my $parent = $child->parent; # Parent
314
5
96
$_->parent = $parent for $child->children->@*; # Reparent unwrapped children of child
315
5
154
delete $child ->{parent}; # Remove parent of unwrapped child
316
5
82
splice $parent->children->@*, $i, 1, $child->children->@*; # Remove child
317
5
94
$parent
318
}
319
320
sub wrap($;$$) # Wrap the specified child with a new parent and return the new parent optionally setting its L[key] and L[value].
321
5
5
1
115
{my ($child, $key, $value) = @_; # Child to wrap, optional key, optional value
322
5
50
12
return undef unless defined(my $i = indexOfChildInParent $child); # Locate child within existing parent
323
5
87
my $parent = $child->parent; # Existing parent
324
5
24
my $new = new $key, $value; # Create new parent
325
5
410
$new->parent = $parent; # Parent new parent
326
5
121
$new->children = [$child]; # Set children for new parent
327
5
115
splice $parent->children->@*, $i, 1, $new; # Place new parent in existing parent
328
5
121
$child->parent = $new # Reparent child to new parent
329
}
330
331
sub wrapChildren($;$$) # Wrap the children of the specified parent with a new intermediate parent that becomes the child of the specified parent, optionally setting the L[key] and the L[value] for the new parent. Return the new parent.
332
1
1
1
4
{my ($parent, $key, $value) = @_; # Child to wrap, optional key for new wrapping parent, optional value for new wrapping parent
333
1
3
my $new = new $key, $value; # Create new parent
334
1
90
$new->children = $parent->children; # Move children;
335
1
27
$parent->children = [$new]; # Grand parent
336
1
22
$new->parent = $parent; # Parent new parent
337
1
20
$_->parent = $new for $new->children->@*; # Reparent new children
338
1
64
$new # New parent
339
}
340
341
sub merge($) # Unwrap the children of the specified parent with the whose L[key] fields L that of their parent. Returns the specified parent regardless.
342
1
1
1
5
{my ($parent) = @_; # Merging parent
343
1
20
for my $c($parent->children->@*) # Children of parent
344
4
100
80
{unwrap $c if $c->key ~~ $parent->key; # Unwrap child if like parent
345
}
346
$parent
347
1
22
}
348
349
sub mergeLikePrev($) # Merge the preceding sibling of the specified child if that sibling exists and the L[key] data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
350
1
1
1
9
{my ($child) = @_; # Child
351
1
50
4
return $child unless my $prev = $child->prev; # No merge possible if child is first
352
1
4
$child->putFirst($prev->cut)->unwrap # Children to be merged
353
}
354
355
sub mergeLikeNext($) # Merge the following sibling of the specified child if that sibling exists and the L[key] data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
356
1
1
1
9
{my ($child) = @_; # Child
357
1
50
5
return $child unless my $next = $child->next; # No merge possible if child is last
358
1
4
$child->putLast($next->cut)->unwrap # Children to be merged
359
}
360
361
sub split($) # Make the specified parent a grandparent of each of its children by interposing a copy of the specified parent between the specified parent and each of its children. Return the specified parent.
362
1
1
1
5
{my ($parent) = @_; # Parent to make into a grand parent
363
1
20
wrap $_, $parent->key for $parent->children->@*; # Grandparent each child
364
1
25
$parent
365
}
366
367
#D1 Traverse # Traverse a tree.
368
369
sub by($;$) # Traverse a tree in post-order to process each child with the specified sub and return an array of the results of processing each child. If no sub sub is specified, the children are returned in tree order.
370
24
24
1
57
{my ($tree, $sub) = @_; # Tree, optional sub to process each child
371
24
100
239
177
$sub //= sub{@_}; # Default sub
239
501
372
373
24
35
my @r; # Results
374
sub # Traverse
375
262
262
3096
{my ($child) = @_; # Child
376
262
4323
__SUB__->($_) for $child->children->@*; # Children of child
377
262
2852
push @r, &$sub($child); # Process child saving result
378
24
101
}->($tree); # Start at root of tree
379
380
@r
381
24
793
}
382
383
sub select($$) # Select matching children in a tree in post-order. A child can be selected via named value, array of values, a hash of values, a regular expression or a sub reference.
384
7
7
1
51
{my ($tree, $select) = @_; # Tree, method to select a child
385
7
16
my $ref = ref $select; # Selector type
386
my $sel = # Selection method
387
10
10
44
$ref =~ m(array)i ? sub{grep{$_[0] eq $_} @$select} : # Array
20
46
388
10
10
55
$ref =~ m(hash)i ? sub{$$select{$_[0]}} : # Hash
389
17
17
160
$ref =~ m(exp)i ? sub{$_[0] =~ m($select)} : # Regular expression
390
17
17
311
$ref =~ m(code)i ? sub{&$select($_[0])} : # Sub
391
7
100
7
68
sub{$_[0] eq $select}; # Scalar
7
100
39
100
100
392
7
13
my @s; # Selection
393
394
sub # Traverse
395
61
61
344
{my ($child) = @_; # Child
396
61
100
993
push @s, $child if &$sel($child->key); # Select child if it matches
397
61
1011
__SUB__->($_) for $child->children->@*; # Each child
398
7
31
}->($tree); # Start at root
399
400
@s
401
7
181
}
402
403
#D1 Partitions # Various partitions of the tree
404
405
sub leaves($) # The set of all children without further children, i.e. each leaf of the tree.
406
2
2
1
5
{my ($tree) = @_; # Tree
407
2
4
my @leaves; # Leaves
408
sub # Traverse
409
20
20
25
{my ($child) = @_; # Child
410
20
100
327
if (my @c = $child->children->@*) # Children of child
411
11
81
{__SUB__->($_) for @c; # Process children of child
412
}
413
else
414
9
60
{push @leaves, $child; # Save leaf
415
}
416
2
14
}->($tree); # Start at root of tree
417
418
@leaves
419
2
19
}
420
421
sub parentsOrdered($$$) #P The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in the specified order.
422
7
7
1
13
{my ($tree, $preorder, $reverse) = @_; # Tree, pre-order if true else post-order, reversed if true
423
7
11
my @parents; # Parents
424
sub # Traverse
425
73
73
183
{my ($child) = @_; # Child
426
73
100
1193
if (my @c = $child->children->@*) # Children of child
427
36
100
205
{@c = reverse @c if $reverse; # Reverse if requested
428
36
100
59
push @parents, $child if $preorder; # Pre-order
429
36
101
__SUB__->($_) for @c; # Process children of child
430
36
100
177
push @parents, $child unless $preorder; # Post-order
431
}
432
7
40
}->($tree); # Start at root of tree
433
434
@parents
435
7
69
}
436
437
sub parentsPreOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal pre-order.
438
1
1
1
3
{my ($tree) = @_; # Tree
439
1
5
parentsOrdered($tree, 1, 0);
440
}
441
442
sub parentsPostOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
443
4
4
1
9
{my ($tree) = @_; # Tree
444
4
10
parentsOrdered($tree, 0, 0);
445
}
446
447
sub parentsReversePreOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse pre-order.
448
1
1
1
4
{my ($tree) = @_; # Tree
449
1
4
parentsOrdered($tree, 1, 1);
450
}
451
452
sub parentsReversePostOrder($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse post-order.
453
1
1
1
3
{my ($tree) = @_; # Tree
454
1
3
&parentsOrdered($tree, 0, 1);
455
}
456
457
sub parents($) # The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
458
2
2
1
6
{my ($tree) = @_; # Tree
459
2
6
&parentsPostOrder(@_);
460
}
461
462
#D1 Order # Check the order and relative position of children in a tree.
463
464
sub above($$) # Return the first child if it is above the second child else return B.
465
4
4
1
11
{my ($first, $second) = @_; # First child, second child
466
4
50
11
return undef if $first == $second; # A child cannot be above itself
467
4
41
my @f = context $first; # Context of first child
468
4
10
my @s = context $second; # Context of second child
469
4
66
51
pop @f, pop @s while @f and @s and $f[-1] == $s[-1]; # Find first different ancestor
100
470
4
100
28
!@f ? $first : undef # First is above second if the ancestors of first are also ancestors of second
471
}
472
473
sub below($$) # Return the first child if it is below the second child else return B.
474
2
2
1
7
{my ($first, $second) = @_; # First child, second child
475
2
100
10
above($second, $first) ? $first : undef
476
}
477
478
sub after($$) # Return the first child if it occurs strictly after the second child in the tree or else B if the first child is L[above], L[below] or L[before] the second child.
479
4
4
1
8
{my ($first, $second) = @_; # First child, second child
480
4
11
my @f = context $first; # Context of first child
481
4
10
my @s = context $second; # Context of second child
482
4
66
50
pop @f, pop @s while @f and @s and $f[-1] == $s[-1]; # Find first different ancestor
100
483
4
100
66
27
return undef unless @f and @s; # Not strictly after
484
2
50
6
indexOfChildInParent($f[-1]) > indexOfChildInParent($s[-1]) ? $first : undef # First child relative to second child at first common ancestor
485
}
486
487
sub before($$) # Return the first child if it occurs strictly before the second child in the tree or else B if the first child is L[above], L[below] or L[after] the second child.
488
2
2
1
7
{my ($first, $second) = @_; # First child, second child
489
2
100
5
after($second, $first) ? $first : undef
490
}
491
492
#D1 Paths # Find paths between nodes
493
494
sub path($) # Return the list of zero based child indexes for the path from the root of the tree containing the specified child to the specified child for use by the L[go] method.
495
1
1
1
4
{my ($child) = @_; # Child
496
1
2
my @p; # Path
497
1
24
for(my $p = $child; my $q = $p->parent; $p = $q) # Go up
498
3
20
{unshift @p, indexOfChildInParent $p # Record path
499
}
500
@p
501
1
22
}
502
503
sub pathFrom($$) # Return the list of zero based child indexes for the path from the specified ancestor to the specified child for use by the L[go] method else confess if the ancestor is not, in fact, an ancestor.
504
9
9
1
19
{my ($child, $ancestor) = @_; # Child, ancestor
505
9
100
41
return () if $child == $ancestor; # Easy case
506
8
43
my @p; # Path
507
8
148
for(my $p = $child; my $q = $p->parent; $p = $q) # Go up
508
15
102
{unshift @p, indexOfChildInParent $p; # Record path
509
15
100
283
return @p if $q == $ancestor; # Stop at ancestor
510
}
511
0
0
confess "Not an ancestor"
512
}
513
514
sub siblingsBefore($) # Return a list of siblings before the specified child.
515
1
1
1
4
{my ($child) = @_; # Child
516
1
50
18
return () unless my $parent = $child->parent; # Parent
517
1
25
my @c = $parent->children->@*; # Children
518
1
7
my $i = indexOfChildInParent $child; # Our position
519
1
24
@c[0..$i-1]
520
}
521
522
sub siblingsAfter($) # Return a list of siblings after the specified child.
523
1
1
1
4
{my ($child) = @_; # Child
524
1
50
19
return () unless my $parent = $child->parent; # Parent
525
1
24
my @c = $parent->children->@*; # Children
526
1
8
my $i = indexOfChildInParent $child; # Our position
527
1
9
@c[$i+1..$#c]
528
}
529
530
sub siblingsStrictlyBetween($$) # Return a list of the siblings strictly between two children of the same parent else return B.
531
2
2
1
7
{my ($start, $finish) = @_; # Start child, finish child
532
2
50
35
return () unless my $parent = $start->parent; # Parent
533
2
100
83
confess "Must be siblings" unless $parent == $finish->parent; # Check both children have the same parent
534
1
23
my @c = $parent->children->@*; # All siblings
535
1
66
15
shift @c while @c and $c[0] != $start; # Remove all siblings up to the start child
536
1
66
7
pop @c while @c and $c[-1] != $finish; # Remove all siblings after the finish child
537
1
50
3
shift @c; pop @c if @c; # Remove first and last child to make range strictly between
1
10
538
@c # Siblings strictly between start and finish
539
1
7
}
540
541
sub lineage($$) # Return the path from the specified child to the specified ancestor else return B if the child is not a descendant of the ancestor.
542
2
2
1
6
{my ($child, $ancestor) = @_; # Child, ancestor
543
2
5
my @p; # Path
544
2
8
for(my $p = $child; $p; $p = $p->parent) # Go up
545
8
34
{push @p, $p; # Record path
546
8
100
121
last if $p == $ancestor # Stop if we encounter the specified ancestor
547
}
548
2
100
66
36
return @p if !@p or $p[-1] == $ancestor; # Found the ancestor
549
undef # No such ancestor
550
1
6
}
551
552
sub nextPreOrderPath($) # Return a list of children visited between the specified child and the next child in pre-order.
553
22
22
1
38
{my ($start) = @_; # The child at the start of the path
554
22
100
358
return ($start->first) if $start->children->@*; # First child if possible
555
13
81
my $p = $start; # Traverse upwards and then right
556
13
15
my @p; # Path
557
13
37
push @p, $p = $p->parent while $p->isLast; # Traverse upwards
558
13
100
94
$p->next ? (@p, $p->next) : () # Traverse right else we are at the root
559
}
560
561
sub nextPostOrderPath($) # Return a list of children visited between the specified child and the next child in post-order.
562
22
22
1
44
{my ($start) = @_; # The child at the start of the path
563
22
29
my $p = $start; # Traverse upwards and then right, then first most
564
22
29
my @p; # Path
565
22
100
360
if (!$p->parent) # Starting at the root which is last in a post order traversal
566
2
16
{push @p, $p while $p = $p->first;
567
return @p
568
2
54
}
569
20
100
124
return (@p, $p->parent) if $p->isLast; # Traverse upwards
570
11
50
75
if (my $q = $p->next) # Traverse right
571
11
26
{for( ; $q; $q = $q->first) {push @p, $q} # Traverse first most
13
40
572
return @p
573
11
236
}
574
0
0
($p) # Back at the root
575
}
576
577
sub prevPostOrderPath($) # Return a list of children visited between the specified child and the previous child in post-order.
578
22
22
1
41
{my ($start) = @_; # The child at the start of the path
579
22
100
372
return ($start->last) if $start->children->@*; # Last child if possible
580
13
66
my $p = $start; # Traverse upwards and then left
581
13
19
my @p; # Path
582
13
27
push @p, $p = $p->parent while $p->isFirst; # Traverse upwards
583
13
100
91
$p->prev ? (@p, $p->prev) : () # Traverse left else we are at the root
584
}
585
586
sub prevPreOrderPath($) # Return a list of children visited between the specified child and the previous child in pre-order.
587
22
22
1
39
{my ($start) = @_; # The child at the start of the path
588
22
31
my $p = $start; # Traverse upwards and then left, then last most
589
22
28
my @p; # Path
590
22
100
363
if (!$p->parent) # Starting at the root which is last in a post order traversal
591
2
17
{push @p, $p while $p = $p->last;
592
return @p
593
2
53
}
594
20
100
128
return (@p, $p->parent) if $p->isFirst; # Traverse upwards
595
11
50
85
if (my $q = $p->prev) # Traverse left
596
11
27
{for( ; $q; $q = $q->last) {push @p, $q} # Traverse last most
18
65
597
return @p
598
11
232
}
599
0
0
($p) # Back at the root
600
}
601
602
#D1 Print # Print a tree.
603
604
sub printTree($$$$) #P String representation as a horizontal tree.
605
37
37
1
72
{my ($tree, $print, $preorder, $reverse) = @_; # Tree, optional print method, pre-order, reverse
606
37
55
my @s; # String representation
607
608
sub # Print a child
609
355
355
579
{my ($child, $depth) = @_; # Child, depth
610
355
5984
my $key = $child->key; # Key
611
355
6628
my $value = $child->value; # Value
612
355
50
1905
my $k = join '', ' ' x $depth, $print ? &$print($key) : $key; # Print key
613
355
50
699
my $v = !defined($value) ? '' : ref($value) ? dump($value) : $value; # Print value
100
614
355
100
975
push @s, [$k, $v] if $preorder;
615
355
100
6114
my @c = $child->children->@*; @c = reverse @c if $reverse;
355
1759
616
355
1054
__SUB__->($_, $depth+1) for @c; # Print children of child
617
355
100
923
push @s, [$k, $v] unless $preorder;
618
37
267
}->($tree, 0); # Print root
619
620
37
453
my $r = formatTableBasic [[qw(Key Value)], @s]; # Print tree
621
37
50
15853
owf($logFile, $r) if -e $logFile; # Log the result if requested
622
37
409
$r
623
}
624
625
sub printPreOrder($;$) # Print tree in normal pre-order.
626
34
34
1
63
{my ($tree, $print) = @_; # Tree, optional print method
627
34
77
printTree($tree, $print, 1, 0);
628
}
629
630
sub printPostOrder($;$) # Print tree in normal post-order.
631
1
1
1
4
{my ($tree, $print) = @_; # Tree, optional print method
632
1
5
printTree($tree, $print, 0, 0);
633
}
634
635
sub printReversePreOrder($;$) # Print tree in reverse pre-order
636
1
1
1
5
{my ($tree, $print) = @_; # Tree, optional print method
637
1
4
printTree($tree, $print, 1, 1);
638
}
639
640
sub printReversePostOrder($;$) # Print tree in reverse post-order
641
1
1
1
4
{my ($tree, $print) = @_; # Tree, optional print method
642
1
4
printTree($tree, $print, 0, 1);
643
}
644
645
sub print($;$) # Print tree in normal pre-order.
646
33
33
1
74
{my ($tree, $print) = @_; # Tree, optional print method
647
33
435
&printPreOrder(@_);
648
}
649
650
sub brackets($;$$) # Bracketed string representation of a tree.
651
7
7
1
18
{my ($tree, $print, $separator) = @_; # Tree, optional print method, optional child separator
652
7
50
37
my $t = $separator // ''; # Default child separator
653
sub # Print a child
654
62
62
92
{my ($child) = @_; # Child
655
62
1018
my $key = $child->key; # Key
656
62
50
272
my $p = $print ? &$print($key) : $key; # Printed child
657
62
980
my $c = $child->children; # Children of child
658
62
100
406
return $p unless @$c; # Return child immediately if no children to format
659
31
61
join '', $p, '(', join($t, map {__SUB__->($_)} @$c), ')' # String representation
55
130
660
7
43
}->($tree) # Print root
661
}
662
663
sub xml($;$) # Print a tree as as xml.
664
1
1
1
4
{my ($tree, $print) = @_; # Tree, optional print method
665
sub # Print a child
666
12
12
21
{my ($child) = @_; # Child
667
12
202
my $key = $child->key; # Key
668
12
50
57
my $p = $print ? &$print($key) : $key; # Printed child
669
12
193
my $c = $child->children; # Children of child
670
12
100
111
return "<$p/>" unless @$c; # Singleton
671
6
16
join '', "<$p>", (map {__SUB__->($_)} @$c), "$p>" # String representation
11
30
672
1
9
}->($tree) # Print root
673
}
674
675
#D1 Data Structures # Data structures use by this package.
676
677
#D0
678
#-------------------------------------------------------------------------------
679
# Export
680
#-------------------------------------------------------------------------------
681
682
1
1
8356
use Exporter qw(import);
1
7
1
67
683
684
1
1
9
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
1
3
1
891
685
686
@ISA = qw(Exporter);
687
@EXPORT_OK = qw(
688
);
689
%EXPORT_TAGS = (all=>[@EXPORT, @EXPORT_OK]);
690
691
# podDocumentation
692
693
=pod
694
695
=encoding utf-8
696
697
=head1 Name
698
699
Tree::Ops - Tree operations.
700
701
=head1 Synopsis
702
703
Create a tree:
704
705
my $a = Tree::Ops::new 'a', 'A';
706
707
for(1..2)
708
{$a->open ('b', "B$_");
709
$a->single('c', "C$_");
710
$a->close;
711
}
712
$a->single ('d', 'D');
713
$a->single ('e', 'E');
714
715
Print it:
716
717
is_deeply $a->print, <
718
Key Value
719
a A
720
b B1
721
c C1
722
b B2
723
c C2
724
d D
725
e E
726
END
727
728
Navigate through the tree:
729
730
is_deeply $a->lastMost->prev->prev->first->key, 'c';
731
is_deeply $a->first->next->last->parent->first->value, 'C2';
732
733
Traverse the tree:
734
735
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
736
737
Select items from the tree:
738
739
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
740
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
741
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
742
743
Reorganize the tree:
744
745
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
746
is_deeply $a->print, <
747
Key Value
748
a A
749
b B1
750
c C1
751
b B2
752
d D
753
c C2
754
e E
755
END
756
757
=head1 Description
758
759
Tree operations.
760
761
762
Version 20200725.
763
764
765
The following sections describe the methods in each functional area of this
766
module. For an alphabetic listing of all methods by name see L.
767
768
769
770
=head1 Build
771
772
Create a tree. There is no implicit ordering applied to the tree, the relationships between parents and children within the tree are as established by the user and can be reorganized at will using the methods in this module.
773
774
=head2 new($key, $value)
775
776
Create a new child optionally recording the specified key or value.
777
778
Parameter Description
779
1 $key Key
780
2 $value Value
781
782
B
783
784
785
786
my $a = Tree::Ops::new 'a', 'A'; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
787
788
for(1..2)
789
{$a->open ('b', "B$_");
790
$a->single('c', "C$_");
791
ok $a->activeScope->key eq 'b';
792
$a->close;
793
}
794
$a->single ('d', 'D');
795
$a->single ('e', 'E');
796
is_deeply $a->print, <
797
Key Value
798
a A
799
b B1
800
c C1
801
b B2
802
c C2
803
d D
804
e E
805
END
806
807
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
808
809
is_deeply $a->lastMost->prev->prev->first->key, 'c';
810
is_deeply $a->first->next->last->parent->first->value, 'C2';
811
812
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
813
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
814
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
815
816
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
817
is_deeply $a->print, <
818
Key Value
819
a A
820
b B1
821
c C1
822
b B2
823
d D
824
c C2
825
e E
826
END
827
828
829
This is a static method and so should either be imported or invoked as:
830
831
Tree::Ops::new
832
833
834
=head2 activeScope($tree)
835
836
Locate the active scope in a tree.
837
838
Parameter Description
839
1 $tree Tree
840
841
B
842
843
844
my $a = Tree::Ops::new 'a', 'A';
845
for(1..2)
846
{$a->open ('b', "B$_");
847
$a->single('c', "C$_");
848
849
ok $a->activeScope->key eq 'b'; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
850
851
$a->close;
852
}
853
$a->single ('d', 'D');
854
$a->single ('e', 'E');
855
is_deeply $a->print, <
856
Key Value
857
a A
858
b B1
859
c C1
860
b B2
861
c C2
862
d D
863
e E
864
END
865
866
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
867
868
is_deeply $a->lastMost->prev->prev->first->key, 'c';
869
is_deeply $a->first->next->last->parent->first->value, 'C2';
870
871
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
872
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
873
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
874
875
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
876
is_deeply $a->print, <
877
Key Value
878
a A
879
b B1
880
c C1
881
b B2
882
d D
883
c C2
884
e E
885
END
886
887
888
=head2 open($tree, $key, $value)
889
890
Add a child and make it the currently active scope into which new children will be added.
891
892
Parameter Description
893
1 $tree Tree
894
2 $key Key
895
3 $value Value to be recorded in the interior child being opened
896
897
B
898
899
900
my $a = Tree::Ops::new 'a', 'A';
901
for(1..2)
902
903
{$a->open ('b', "B$_"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
904
905
$a->single('c', "C$_");
906
ok $a->activeScope->key eq 'b';
907
$a->close;
908
}
909
$a->single ('d', 'D');
910
$a->single ('e', 'E');
911
is_deeply $a->print, <
912
Key Value
913
a A
914
b B1
915
c C1
916
b B2
917
c C2
918
d D
919
e E
920
END
921
922
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
923
924
is_deeply $a->lastMost->prev->prev->first->key, 'c';
925
is_deeply $a->first->next->last->parent->first->value, 'C2';
926
927
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
928
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
929
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
930
931
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
932
is_deeply $a->print, <
933
Key Value
934
a A
935
b B1
936
c C1
937
b B2
938
d D
939
c C2
940
e E
941
END
942
943
944
=head2 close($tree)
945
946
Close the current scope returning to the previous scope.
947
948
Parameter Description
949
1 $tree Tree
950
951
B
952
953
954
my $a = Tree::Ops::new 'a', 'A';
955
for(1..2)
956
{$a->open ('b', "B$_");
957
$a->single('c', "C$_");
958
ok $a->activeScope->key eq 'b';
959
960
$a->close; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
961
962
}
963
$a->single ('d', 'D');
964
$a->single ('e', 'E');
965
is_deeply $a->print, <
966
Key Value
967
a A
968
b B1
969
c C1
970
b B2
971
c C2
972
d D
973
e E
974
END
975
976
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
977
978
is_deeply $a->lastMost->prev->prev->first->key, 'c';
979
is_deeply $a->first->next->last->parent->first->value, 'C2';
980
981
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
982
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
983
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
984
985
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
986
is_deeply $a->print, <
987
Key Value
988
a A
989
b B1
990
c C1
991
b B2
992
d D
993
c C2
994
e E
995
END
996
997
998
=head2 single($tree, $key, $value)
999
1000
Add one child in the current scope.
1001
1002
Parameter Description
1003
1 $tree Tree
1004
2 $key Key
1005
3 $value Value to be recorded in the child being created
1006
1007
B
1008
1009
1010
my $a = Tree::Ops::new 'a', 'A';
1011
for(1..2)
1012
{$a->open ('b', "B$_");
1013
1014
$a->single('c', "C$_"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1015
1016
ok $a->activeScope->key eq 'b';
1017
$a->close;
1018
}
1019
1020
$a->single ('d', 'D'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1021
1022
1023
$a->single ('e', 'E'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1024
1025
is_deeply $a->print, <
1026
Key Value
1027
a A
1028
b B1
1029
c C1
1030
b B2
1031
c C2
1032
d D
1033
e E
1034
END
1035
1036
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
1037
1038
is_deeply $a->lastMost->prev->prev->first->key, 'c';
1039
is_deeply $a->first->next->last->parent->first->value, 'C2';
1040
1041
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)];
1042
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)];
1043
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)];
1044
1045
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
1046
is_deeply $a->print, <
1047
Key Value
1048
a A
1049
b B1
1050
c C1
1051
b B2
1052
d D
1053
c C2
1054
e E
1055
END
1056
1057
1058
=head2 include($tree, $include)
1059
1060
Include the specified tree in the currently open scope.
1061
1062
Parameter Description
1063
1 $tree Tree being built
1064
2 $include Tree to include
1065
1066
B
1067
1068
1069
my ($i) = fromLetters 'b(cd)';
1070
1071
my $a = Tree::Ops::new 'A';
1072
$a->open ('B');
1073
1074
$a->include($i); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1075
1076
$a->close;
1077
1078
is_deeply $a->print, <
1079
Key Value
1080
A
1081
B
1082
a
1083
b
1084
c
1085
d
1086
END
1087
1088
1089
=head2 fromLetters($letters)
1090
1091
Create a tree from a string of letters returning the children created in alphabetic order - useful for testing.
1092
1093
Parameter Description
1094
1 $letters String of letters and ( ).
1095
1096
B
1097
1098
1099
1100
my ($a) = fromLetters(q(bc(d)e)); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1101
1102
1103
is_deeply $a->print, <
1104
Key Value
1105
a
1106
b
1107
c
1108
d
1109
e
1110
END
1111
1112
1113
=head1 Navigation
1114
1115
Navigate through a tree.
1116
1117
=head2 first($parent)
1118
1119
Get the first child under the specified parent.
1120
1121
Parameter Description
1122
1 $parent Parent
1123
1124
B
1125
1126
1127
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1128
is_deeply $c->parent, $b;
1129
1130
is_deeply $a->first, $b; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1131
1132
is_deeply $a->last, $d;
1133
is_deeply $e->next, $f;
1134
is_deeply $f->prev, $e;
1135
1136
1137
=head2 last($parent)
1138
1139
Get the last child under the specified parent.
1140
1141
Parameter Description
1142
1 $parent Parent
1143
1144
B
1145
1146
1147
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1148
is_deeply $c->parent, $b;
1149
is_deeply $a->first, $b;
1150
1151
is_deeply $a->last, $d; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1152
1153
is_deeply $e->next, $f;
1154
is_deeply $f->prev, $e;
1155
1156
1157
=head2 next($child)
1158
1159
Get the next sibling following the specified child.
1160
1161
Parameter Description
1162
1 $child Child
1163
1164
B
1165
1166
1167
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1168
is_deeply $c->parent, $b;
1169
is_deeply $a->first, $b;
1170
is_deeply $a->last, $d;
1171
1172
is_deeply $e->next, $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1173
1174
is_deeply $f->prev, $e;
1175
1176
1177
=head2 prev($child)
1178
1179
Get the previous sibling of the specified child.
1180
1181
Parameter Description
1182
1 $child Child
1183
1184
B
1185
1186
1187
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1188
is_deeply $c->parent, $b;
1189
is_deeply $a->first, $b;
1190
is_deeply $a->last, $d;
1191
is_deeply $e->next, $f;
1192
1193
is_deeply $f->prev, $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1194
1195
1196
1197
=head2 firstMost($parent)
1198
1199
Return the first most descendant child in the tree starting at this parent or else return B if this parent has no children.
1200
1201
Parameter Description
1202
1 $parent Parent
1203
1204
B
1205
1206
1207
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1208
fromLetters 'b(c)y(x)d(efgh(i(j)))';
1209
1210
is_deeply $a->print, <
1211
Key Value
1212
a
1213
b
1214
c
1215
y
1216
x
1217
d
1218
e
1219
f
1220
g
1221
h
1222
i
1223
j
1224
END
1225
1226
is_deeply $a->xml,
1227
' ';
1228
1229
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1230
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1231
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1232
is_deeply [$a->parents], [$a->parentsPostOrder];
1233
1234
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1235
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1236
1237
ok !$j->parents;
1238
1239
ok $a->lastMost == $j;
1240
ok !$a->prevMost;
1241
ok $j->prevMost == $g;
1242
ok $i->prevMost == $g;
1243
ok $h->prevMost == $g;
1244
ok $g->prevMost == $f;
1245
ok $f->prevMost == $e;
1246
ok $e->prevMost == $x;
1247
ok $d->prevMost == $x;
1248
ok $x->prevMost == $c;
1249
ok $y->prevMost == $c;
1250
ok !$c->prevMost;
1251
ok !$b->prevMost;
1252
ok !$a->prevMost;
1253
1254
1255
ok $a->firstMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1256
1257
ok $a->nextMost == $c;
1258
ok $b->nextMost == $c;
1259
ok $c->nextMost == $x;
1260
ok $y->nextMost == $x;
1261
ok $x->nextMost == $e;
1262
ok $d->nextMost == $e;
1263
ok $e->nextMost == $f;
1264
ok $f->nextMost == $g;
1265
ok $g->nextMost == $j;
1266
ok $h->nextMost == $j;
1267
ok $i->nextMost == $j;
1268
ok !$j->nextMost;
1269
1270
ok $i->topMost == $a;
1271
1272
1273
=head2 nextMost($child)
1274
1275
Return the next child with no children, i.e. the next leaf of the tree, else return B if there is no such child.
1276
1277
Parameter Description
1278
1 $child Current leaf
1279
1280
B
1281
1282
1283
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1284
fromLetters 'b(c)y(x)d(efgh(i(j)))';
1285
1286
is_deeply $a->print, <
1287
Key Value
1288
a
1289
b
1290
c
1291
y
1292
x
1293
d
1294
e
1295
f
1296
g
1297
h
1298
i
1299
j
1300
END
1301
1302
is_deeply $a->xml,
1303
' ';
1304
1305
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1306
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1307
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1308
is_deeply [$a->parents], [$a->parentsPostOrder];
1309
1310
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1311
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1312
1313
ok !$j->parents;
1314
1315
ok $a->lastMost == $j;
1316
ok !$a->prevMost;
1317
ok $j->prevMost == $g;
1318
ok $i->prevMost == $g;
1319
ok $h->prevMost == $g;
1320
ok $g->prevMost == $f;
1321
ok $f->prevMost == $e;
1322
ok $e->prevMost == $x;
1323
ok $d->prevMost == $x;
1324
ok $x->prevMost == $c;
1325
ok $y->prevMost == $c;
1326
ok !$c->prevMost;
1327
ok !$b->prevMost;
1328
ok !$a->prevMost;
1329
1330
ok $a->firstMost == $c;
1331
1332
ok $a->nextMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1333
1334
1335
ok $b->nextMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1336
1337
1338
ok $c->nextMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1339
1340
1341
ok $y->nextMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1342
1343
1344
ok $x->nextMost == $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1345
1346
1347
ok $d->nextMost == $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1348
1349
1350
ok $e->nextMost == $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1351
1352
1353
ok $f->nextMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1354
1355
1356
ok $g->nextMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1357
1358
1359
ok $h->nextMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1360
1361
1362
ok $i->nextMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1363
1364
1365
ok !$j->nextMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1366
1367
1368
ok $i->topMost == $a;
1369
1370
1371
=head2 prevMost($child)
1372
1373
Return the previous child with no children, i.e. the previous leaf of the tree, else return B if there is no such child.
1374
1375
Parameter Description
1376
1 $child Current leaf
1377
1378
B
1379
1380
1381
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1382
fromLetters 'b(c)y(x)d(efgh(i(j)))';
1383
1384
is_deeply $a->print, <
1385
Key Value
1386
a
1387
b
1388
c
1389
y
1390
x
1391
d
1392
e
1393
f
1394
g
1395
h
1396
i
1397
j
1398
END
1399
1400
is_deeply $a->xml,
1401
' ';
1402
1403
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1404
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1405
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1406
is_deeply [$a->parents], [$a->parentsPostOrder];
1407
1408
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1409
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1410
1411
ok !$j->parents;
1412
1413
ok $a->lastMost == $j;
1414
1415
ok !$a->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1416
1417
1418
ok $j->prevMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1419
1420
1421
ok $i->prevMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1422
1423
1424
ok $h->prevMost == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1425
1426
1427
ok $g->prevMost == $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1428
1429
1430
ok $f->prevMost == $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1431
1432
1433
ok $e->prevMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1434
1435
1436
ok $d->prevMost == $x; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1437
1438
1439
ok $x->prevMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1440
1441
1442
ok $y->prevMost == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1443
1444
1445
ok !$c->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1446
1447
1448
ok !$b->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1449
1450
1451
ok !$a->prevMost; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1452
1453
1454
ok $a->firstMost == $c;
1455
ok $a->nextMost == $c;
1456
ok $b->nextMost == $c;
1457
ok $c->nextMost == $x;
1458
ok $y->nextMost == $x;
1459
ok $x->nextMost == $e;
1460
ok $d->nextMost == $e;
1461
ok $e->nextMost == $f;
1462
ok $f->nextMost == $g;
1463
ok $g->nextMost == $j;
1464
ok $h->nextMost == $j;
1465
ok $i->nextMost == $j;
1466
ok !$j->nextMost;
1467
1468
ok $i->topMost == $a;
1469
1470
1471
=head2 lastMost($parent)
1472
1473
Return the last most descendant child in the tree starting at this parent or else return B if this parent has no children.
1474
1475
Parameter Description
1476
1 $parent Parent
1477
1478
B
1479
1480
1481
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1482
fromLetters 'b(c)y(x)d(efgh(i(j)))';
1483
1484
is_deeply $a->print, <
1485
Key Value
1486
a
1487
b
1488
c
1489
y
1490
x
1491
d
1492
e
1493
f
1494
g
1495
h
1496
i
1497
j
1498
END
1499
1500
is_deeply $a->xml,
1501
' ';
1502
1503
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1504
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1505
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1506
is_deeply [$a->parents], [$a->parentsPostOrder];
1507
1508
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1509
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1510
1511
ok !$j->parents;
1512
1513
1514
ok $a->lastMost == $j; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1515
1516
ok !$a->prevMost;
1517
ok $j->prevMost == $g;
1518
ok $i->prevMost == $g;
1519
ok $h->prevMost == $g;
1520
ok $g->prevMost == $f;
1521
ok $f->prevMost == $e;
1522
ok $e->prevMost == $x;
1523
ok $d->prevMost == $x;
1524
ok $x->prevMost == $c;
1525
ok $y->prevMost == $c;
1526
ok !$c->prevMost;
1527
ok !$b->prevMost;
1528
ok !$a->prevMost;
1529
1530
ok $a->firstMost == $c;
1531
ok $a->nextMost == $c;
1532
ok $b->nextMost == $c;
1533
ok $c->nextMost == $x;
1534
ok $y->nextMost == $x;
1535
ok $x->nextMost == $e;
1536
ok $d->nextMost == $e;
1537
ok $e->nextMost == $f;
1538
ok $f->nextMost == $g;
1539
ok $g->nextMost == $j;
1540
ok $h->nextMost == $j;
1541
ok $i->nextMost == $j;
1542
ok !$j->nextMost;
1543
1544
ok $i->topMost == $a;
1545
1546
1547
=head2 topMost($child)
1548
1549
Return the top most parent in the tree containing the specified child.
1550
1551
Parameter Description
1552
1 $child Child
1553
1554
B
1555
1556
1557
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
1558
fromLetters 'b(c)y(x)d(efgh(i(j)))';
1559
1560
is_deeply $a->print, <
1561
Key Value
1562
a
1563
b
1564
c
1565
y
1566
x
1567
d
1568
e
1569
f
1570
g
1571
h
1572
i
1573
j
1574
END
1575
1576
is_deeply $a->xml,
1577
' ';
1578
1579
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
1580
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
1581
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
1582
is_deeply [$a->parents], [$a->parentsPostOrder];
1583
1584
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
1585
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
1586
1587
ok !$j->parents;
1588
1589
ok $a->lastMost == $j;
1590
ok !$a->prevMost;
1591
ok $j->prevMost == $g;
1592
ok $i->prevMost == $g;
1593
ok $h->prevMost == $g;
1594
ok $g->prevMost == $f;
1595
ok $f->prevMost == $e;
1596
ok $e->prevMost == $x;
1597
ok $d->prevMost == $x;
1598
ok $x->prevMost == $c;
1599
ok $y->prevMost == $c;
1600
ok !$c->prevMost;
1601
ok !$b->prevMost;
1602
ok !$a->prevMost;
1603
1604
ok $a->firstMost == $c;
1605
ok $a->nextMost == $c;
1606
ok $b->nextMost == $c;
1607
ok $c->nextMost == $x;
1608
ok $y->nextMost == $x;
1609
ok $x->nextMost == $e;
1610
ok $d->nextMost == $e;
1611
ok $e->nextMost == $f;
1612
ok $f->nextMost == $g;
1613
ok $g->nextMost == $j;
1614
ok $h->nextMost == $j;
1615
ok $i->nextMost == $j;
1616
ok !$j->nextMost;
1617
1618
1619
ok $i->topMost == $a; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1620
1621
1622
1623
=head2 mostRecentCommonAncestor($first, $second)
1624
1625
Find the most recent common ancestor of the specified children.
1626
1627
Parameter Description
1628
1 $first First child
1629
2 $second Second child
1630
1631
B
1632
1633
1634
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k) =
1635
fromLetters 'b(c(d(e))f(g(h)i)j)k';
1636
1637
is_deeply $a->print, <
1638
Key Value
1639
a
1640
b
1641
c
1642
d
1643
e
1644
f
1645
g
1646
h
1647
i
1648
j
1649
k
1650
END
1651
1652
1653
ok $e->mostRecentCommonAncestor($h) == $b; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1654
1655
1656
ok $e->mostRecentCommonAncestor($k) == $a; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1657
1658
1659
1660
=head2 go($parent, @path)
1661
1662
Return the child at the end of the path starting at the specified parent. A path is a list of zero based children numbers. Return B if the path is not valid.
1663
1664
Parameter Description
1665
1 $parent Parent
1666
2 @path List of zero based children numbers
1667
1668
B
1669
1670
1671
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
1672
1673
is_deeply $a->print, <
1674
Key Value
1675
a
1676
b
1677
c
1678
d
1679
e
1680
f
1681
g
1682
h
1683
i
1684
j
1685
END
1686
1687
1688
ok $a->go(0,1,0,1) == $g; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1689
1690
1691
ok $d->go(0,0) == $f; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1692
1693
1694
is_deeply [$e->path], [0,1,0];
1695
is_deeply [$g->pathFrom($d)], [0,1];
1696
1697
is_deeply $b->dup->print, <
1698
Key Value
1699
b
1700
c
1701
d
1702
e
1703
f
1704
g
1705
h
1706
i
1707
END
1708
1709
my $B = $b->transcribe;
1710
1711
$b->by(sub
1712
{my ($c) = @_;
1713
my @path = $c->pathFrom($b);
1714
1715
my $C = $B->go(@path); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1716
1717
is_deeply $c->key, $C->key;
1718
is_deeply $c->{transcribedTo}, $C;
1719
is_deeply $C->{transcribedFrom}, $c;
1720
});
1721
1722
is_deeply $B->print, <
1723
Key Value
1724
b
1725
c
1726
d
1727
e
1728
f
1729
g
1730
h
1731
i
1732
END
1733
1734
1735
=head1 Location
1736
1737
Verify the current location.
1738
1739
=head2 context($child)
1740
1741
Get the context of the current child.
1742
1743
Parameter Description
1744
1 $child Child
1745
1746
B
1747
1748
1749
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $s, $t, $x, $y, $z) =
1750
fromLetters 'b(c)y(x)z(st)d(efgh(i(j))))';
1751
1752
1753
is_deeply [$x->context], [$x, $y, $a]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1754
1755
1756
is_deeply join(' ', $a->by(sub{$_[0]->key})), "c b x y s t z e f g j i h d a";
1757
is_deeply join(' ', map{$_->key} $a->by), "c b x y s t z e f g j i h d a";
1758
1759
is_deeply $a->print, <
1760
Key Value
1761
a
1762
b
1763
c
1764
y
1765
x
1766
z
1767
s
1768
t
1769
d
1770
e
1771
f
1772
g
1773
h
1774
i
1775
j
1776
END
1777
1778
$z->cut;
1779
is_deeply $a->print, <
1780
Key Value
1781
a
1782
b
1783
c
1784
y
1785
x
1786
d
1787
e
1788
f
1789
g
1790
h
1791
i
1792
j
1793
END
1794
1795
1796
=head2 isFirst($child)
1797
1798
Return the specified child if that child is first under its parent, else return B.
1799
1800
Parameter Description
1801
1 $child Child
1802
1803
B
1804
1805
1806
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1807
1808
is_deeply $a->print, <
1809
Key Value
1810
a
1811
b
1812
c
1813
d
1814
e
1815
f
1816
g
1817
h
1818
i
1819
j
1820
END
1821
1822
is_deeply $b->singleChildOfParent, $c;
1823
1824
is_deeply $e->isFirst, $e; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1825
1826
1827
ok !$f->isFirst; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1828
1829
ok !$g->isLast;
1830
is_deeply $h->isLast, $h;
1831
ok $j->empty;
1832
ok !$i->empty;
1833
ok $a->isTop;
1834
ok !$b->isTop;
1835
1836
1837
=head2 isLast($child)
1838
1839
Return the specified child if that child is last under its parent, else return B.
1840
1841
Parameter Description
1842
1 $child Child
1843
1844
B
1845
1846
1847
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1848
1849
is_deeply $a->print, <
1850
Key Value
1851
a
1852
b
1853
c
1854
d
1855
e
1856
f
1857
g
1858
h
1859
i
1860
j
1861
END
1862
1863
is_deeply $b->singleChildOfParent, $c;
1864
is_deeply $e->isFirst, $e;
1865
ok !$f->isFirst;
1866
1867
ok !$g->isLast; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1868
1869
1870
is_deeply $h->isLast, $h; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1871
1872
ok $j->empty;
1873
ok !$i->empty;
1874
ok $a->isTop;
1875
ok !$b->isTop;
1876
1877
1878
=head2 isTop($parent)
1879
1880
Return the specified parent if that parent is the top most parent in the tree.
1881
1882
Parameter Description
1883
1 $parent Parent
1884
1885
B
1886
1887
1888
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1889
1890
is_deeply $a->print, <
1891
Key Value
1892
a
1893
b
1894
c
1895
d
1896
e
1897
f
1898
g
1899
h
1900
i
1901
j
1902
END
1903
1904
is_deeply $b->singleChildOfParent, $c;
1905
is_deeply $e->isFirst, $e;
1906
ok !$f->isFirst;
1907
ok !$g->isLast;
1908
is_deeply $h->isLast, $h;
1909
ok $j->empty;
1910
ok !$i->empty;
1911
1912
ok $a->isTop; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1913
1914
1915
ok !$b->isTop; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1916
1917
1918
1919
=head2 singleChildOfParent($parent)
1920
1921
Return the only child of this parent if the parent has an only child, else B
1922
1923
Parameter Description
1924
1 $parent Parent
1925
1926
B
1927
1928
1929
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1930
1931
is_deeply $a->print, <
1932
Key Value
1933
a
1934
b
1935
c
1936
d
1937
e
1938
f
1939
g
1940
h
1941
i
1942
j
1943
END
1944
1945
1946
is_deeply $b->singleChildOfParent, $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1947
1948
is_deeply $e->isFirst, $e;
1949
ok !$f->isFirst;
1950
ok !$g->isLast;
1951
is_deeply $h->isLast, $h;
1952
ok $j->empty;
1953
ok !$i->empty;
1954
ok $a->isTop;
1955
ok !$b->isTop;
1956
1957
1958
=head2 empty($parent)
1959
1960
Return the specified parent if it has no children else B
1961
1962
Parameter Description
1963
1 $parent Parent
1964
1965
B
1966
1967
1968
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
1969
1970
is_deeply $a->print, <
1971
Key Value
1972
a
1973
b
1974
c
1975
d
1976
e
1977
f
1978
g
1979
h
1980
i
1981
j
1982
END
1983
1984
is_deeply $b->singleChildOfParent, $c;
1985
is_deeply $e->isFirst, $e;
1986
ok !$f->isFirst;
1987
ok !$g->isLast;
1988
is_deeply $h->isLast, $h;
1989
1990
ok $j->empty; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1991
1992
1993
ok !$i->empty; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
1994
1995
ok $a->isTop;
1996
ok !$b->isTop;
1997
1998
1999
=head1 Put
2000
2001
Insert children into a tree.
2002
2003
=head2 putFirst($parent, $child)
2004
2005
Place a new child first under the specified parent and return the child.
2006
2007
Parameter Description
2008
1 $parent Parent
2009
2 $child Child
2010
2011
B
2012
2013
2014
my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2015
2016
is_deeply $a->print, <
2017
Key Value
2018
a
2019
b
2020
c
2021
d
2022
e
2023
END
2024
2025
my $z = $b->putNext(new 'z');
2026
is_deeply $a->print, <
2027
Key Value
2028
a
2029
b
2030
c
2031
z
2032
d
2033
e
2034
END
2035
2036
my $y = $d->putPrev(new 'y');
2037
is_deeply $a->print, <
2038
Key Value
2039
a
2040
b
2041
c
2042
z
2043
y
2044
d
2045
e
2046
END
2047
2048
$z->putLast(new 't');
2049
is_deeply $a->print, <
2050
Key Value
2051
a
2052
b
2053
c
2054
z
2055
t
2056
y
2057
d
2058
e
2059
END
2060
2061
2062
$z->putFirst(new 's'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2063
2064
is_deeply $a->print, <
2065
Key Value
2066
a
2067
b
2068
c
2069
z
2070
s
2071
t
2072
y
2073
d
2074
e
2075
END
2076
2077
2078
=head2 putLast($parent, $child)
2079
2080
Place a new child last under the specified parent and return the child.
2081
2082
Parameter Description
2083
1 $parent Parent
2084
2 $child Child
2085
2086
B
2087
2088
2089
my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2090
2091
is_deeply $a->print, <
2092
Key Value
2093
a
2094
b
2095
c
2096
d
2097
e
2098
END
2099
2100
my $z = $b->putNext(new 'z');
2101
is_deeply $a->print, <
2102
Key Value
2103
a
2104
b
2105
c
2106
z
2107
d
2108
e
2109
END
2110
2111
my $y = $d->putPrev(new 'y');
2112
is_deeply $a->print, <
2113
Key Value
2114
a
2115
b
2116
c
2117
z
2118
y
2119
d
2120
e
2121
END
2122
2123
2124
$z->putLast(new 't'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2125
2126
is_deeply $a->print, <
2127
Key Value
2128
a
2129
b
2130
c
2131
z
2132
t
2133
y
2134
d
2135
e
2136
END
2137
2138
$z->putFirst(new 's');
2139
is_deeply $a->print, <
2140
Key Value
2141
a
2142
b
2143
c
2144
z
2145
s
2146
t
2147
y
2148
d
2149
e
2150
END
2151
2152
2153
=head2 putNext($child, $new)
2154
2155
Place a new child after the specified child.
2156
2157
Parameter Description
2158
1 $child Existing child
2159
2 $new New child
2160
2161
B
2162
2163
2164
my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2165
2166
is_deeply $a->print, <
2167
Key Value
2168
a
2169
b
2170
c
2171
d
2172
e
2173
END
2174
2175
2176
my $z = $b->putNext(new 'z'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2177
2178
is_deeply $a->print, <
2179
Key Value
2180
a
2181
b
2182
c
2183
z
2184
d
2185
e
2186
END
2187
2188
my $y = $d->putPrev(new 'y');
2189
is_deeply $a->print, <
2190
Key Value
2191
a
2192
b
2193
c
2194
z
2195
y
2196
d
2197
e
2198
END
2199
2200
$z->putLast(new 't');
2201
is_deeply $a->print, <
2202
Key Value
2203
a
2204
b
2205
c
2206
z
2207
t
2208
y
2209
d
2210
e
2211
END
2212
2213
$z->putFirst(new 's');
2214
is_deeply $a->print, <
2215
Key Value
2216
a
2217
b
2218
c
2219
z
2220
s
2221
t
2222
y
2223
d
2224
e
2225
END
2226
2227
2228
=head2 putPrev($child, $new)
2229
2230
Place a new child before the specified child.
2231
2232
Parameter Description
2233
1 $child Child
2234
2 $new New child
2235
2236
B
2237
2238
2239
my ($a, $b, $c, $d, $e) = fromLetters 'b(c)d(e)';
2240
2241
is_deeply $a->print, <
2242
Key Value
2243
a
2244
b
2245
c
2246
d
2247
e
2248
END
2249
2250
my $z = $b->putNext(new 'z');
2251
is_deeply $a->print, <
2252
Key Value
2253
a
2254
b
2255
c
2256
z
2257
d
2258
e
2259
END
2260
2261
2262
my $y = $d->putPrev(new 'y'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2263
2264
is_deeply $a->print, <
2265
Key Value
2266
a
2267
b
2268
c
2269
z
2270
y
2271
d
2272
e
2273
END
2274
2275
$z->putLast(new 't');
2276
is_deeply $a->print, <
2277
Key Value
2278
a
2279
b
2280
c
2281
z
2282
t
2283
y
2284
d
2285
e
2286
END
2287
2288
$z->putFirst(new 's');
2289
is_deeply $a->print, <
2290
Key Value
2291
a
2292
b
2293
c
2294
z
2295
s
2296
t
2297
y
2298
d
2299
e
2300
END
2301
2302
2303
=head1 Steps
2304
2305
Move the start or end of a scope forwards or backwards as suggested by Alex Monroe.
2306
2307
=head2 step($parent)
2308
2309
Make the first child of the specified parent the parents previous sibling and return the parent. In effect this moves the start of the parent one step forwards.
2310
2311
Parameter Description
2312
1 $parent Parent
2313
2314
B
2315
2316
2317
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2318
2319
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2320
2321
2322
$d->step; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2323
2324
is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2325
2326
2327
$d->stepBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2328
2329
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2330
2331
2332
$b->stepEnd; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2333
2334
is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2335
2336
2337
$b->stepEndBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2338
2339
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2340
2341
2342
=head2 stepEnd($parent)
2343
2344
Make the next sibling of the specified parent the parents last child and return the parent. In effect this moves the end of the parent one step forwards.
2345
2346
Parameter Description
2347
1 $parent Parent
2348
2349
B
2350
2351
2352
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2353
2354
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2355
2356
$d->step;
2357
is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2358
2359
$d->stepBack;
2360
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2361
2362
2363
$b->stepEnd; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2364
2365
is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2366
2367
2368
$b->stepEndBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2369
2370
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2371
2372
2373
=head2 stepBack()
2374
2375
Make the previous sibling of the specified parent the parents first child and return the parent. In effect this moves the start of the parent one step backwards.
2376
2377
2378
B
2379
2380
2381
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2382
2383
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2384
2385
$d->step;
2386
is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2387
2388
2389
$d->stepBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2390
2391
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2392
2393
$b->stepEnd;
2394
is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2395
2396
$b->stepEndBack;
2397
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2398
2399
2400
=head2 stepEndBack()
2401
2402
Make the last child of the specified parent the parents next sibling and return the parent. In effect this moves the end of the parent one step backwards.
2403
2404
2405
B
2406
2407
2408
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2409
2410
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2411
2412
$d->step;
2413
is_deeply $a->brackets, 'a(b(c)ed(fgh(i(j))))';
2414
2415
$d->stepBack;
2416
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2417
2418
$b->stepEnd;
2419
is_deeply $a->brackets, 'a(b(cd(efgh(i(j)))))';
2420
2421
2422
$b->stepEndBack; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2423
2424
is_deeply $a->brackets, 'a(b(c)d(efgh(i(j))))';
2425
2426
2427
=head1 Edit
2428
2429
Edit a tree in situ.
2430
2431
=head2 cut($child)
2432
2433
Cut out a child and all its content and children, return it ready for reinsertion else where.
2434
2435
Parameter Description
2436
1 $child Child
2437
2438
B
2439
2440
2441
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $s, $t, $x, $y, $z) =
2442
fromLetters 'b(c)y(x)z(st)d(efgh(i(j))))';
2443
2444
is_deeply [$x->context], [$x, $y, $a];
2445
2446
is_deeply join(' ', $a->by(sub{$_[0]->key})), "c b x y s t z e f g j i h d a";
2447
is_deeply join(' ', map{$_->key} $a->by), "c b x y s t z e f g j i h d a";
2448
2449
is_deeply $a->print, <
2450
Key Value
2451
a
2452
b
2453
c
2454
y
2455
x
2456
z
2457
s
2458
t
2459
d
2460
e
2461
f
2462
g
2463
h
2464
i
2465
j
2466
END
2467
2468
2469
$z->cut; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2470
2471
is_deeply $a->print, <
2472
Key Value
2473
a
2474
b
2475
c
2476
y
2477
x
2478
d
2479
e
2480
f
2481
g
2482
h
2483
i
2484
j
2485
END
2486
2487
2488
=head2 dup($parent)
2489
2490
Duplicate a specified parent and all its descendants returning the root of the resulting tree.
2491
2492
Parameter Description
2493
1 $parent Parent
2494
2495
B
2496
2497
2498
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
2499
2500
is_deeply $a->print, <
2501
Key Value
2502
a
2503
b
2504
c
2505
d
2506
e
2507
f
2508
g
2509
h
2510
i
2511
j
2512
END
2513
2514
ok $a->go(0,1,0,1) == $g;
2515
ok $d->go(0,0) == $f;
2516
2517
is_deeply [$e->path], [0,1,0];
2518
is_deeply [$g->pathFrom($d)], [0,1];
2519
2520
2521
is_deeply $b->dup->print, <
2522
2523
Key Value
2524
b
2525
c
2526
d
2527
e
2528
f
2529
g
2530
h
2531
i
2532
END
2533
2534
my $B = $b->transcribe;
2535
2536
$b->by(sub
2537
{my ($c) = @_;
2538
my @path = $c->pathFrom($b);
2539
my $C = $B->go(@path);
2540
is_deeply $c->key, $C->key;
2541
is_deeply $c->{transcribedTo}, $C;
2542
is_deeply $C->{transcribedFrom}, $c;
2543
});
2544
2545
is_deeply $B->print, <
2546
Key Value
2547
b
2548
c
2549
d
2550
e
2551
f
2552
g
2553
h
2554
i
2555
END
2556
2557
2558
=head2 transcribe($parent)
2559
2560
Duplicate a specified parent and all its descendants recording the mapping in a temporary {transcribed} field in the tree being transcribed. Returns the root parent of the tree being duplicated.
2561
2562
Parameter Description
2563
1 $parent Parent
2564
2565
B
2566
2567
2568
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
2569
2570
is_deeply $a->print, <
2571
Key Value
2572
a
2573
b
2574
c
2575
d
2576
e
2577
f
2578
g
2579
h
2580
i
2581
j
2582
END
2583
2584
ok $a->go(0,1,0,1) == $g;
2585
ok $d->go(0,0) == $f;
2586
2587
is_deeply [$e->path], [0,1,0];
2588
is_deeply [$g->pathFrom($d)], [0,1];
2589
2590
is_deeply $b->dup->print, <
2591
Key Value
2592
b
2593
c
2594
d
2595
e
2596
f
2597
g
2598
h
2599
i
2600
END
2601
2602
2603
my $B = $b->transcribe; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2604
2605
2606
$b->by(sub
2607
{my ($c) = @_;
2608
my @path = $c->pathFrom($b);
2609
my $C = $B->go(@path);
2610
is_deeply $c->key, $C->key;
2611
2612
is_deeply $c->{transcribedTo}, $C; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2613
2614
2615
is_deeply $C->{transcribedFrom}, $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2616
2617
});
2618
2619
is_deeply $B->print, <
2620
Key Value
2621
b
2622
c
2623
d
2624
e
2625
f
2626
g
2627
h
2628
i
2629
END
2630
2631
2632
=head2 unwrap($child)
2633
2634
Unwrap the specified child and return that child.
2635
2636
Parameter Description
2637
1 $child Child
2638
2639
B
2640
2641
2642
my ($a, $b, $c, $d, $e, $f, $g) = fromLetters 'b(c(de)f)g';
2643
2644
is_deeply $a->print, <
2645
Key Value
2646
a
2647
b
2648
c
2649
d
2650
e
2651
f
2652
g
2653
END
2654
2655
$c->wrap('z');
2656
2657
is_deeply $a->print, <
2658
Key Value
2659
a
2660
b
2661
z
2662
c
2663
d
2664
e
2665
f
2666
g
2667
END
2668
2669
2670
$c->parent->unwrap; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2671
2672
2673
is_deeply $a->print, <
2674
Key Value
2675
a
2676
b
2677
c
2678
d
2679
e
2680
f
2681
g
2682
END
2683
2684
$c->wrapChildren("Z");
2685
2686
is_deeply $a->print, <
2687
Key Value
2688
a
2689
b
2690
c
2691
Z
2692
d
2693
e
2694
f
2695
g
2696
END
2697
2698
2699
=head2 wrap($child, $key, $value)
2700
2701
Wrap the specified child with a new parent and return the new parent optionally setting its L and L.
2702
2703
Parameter Description
2704
1 $child Child to wrap
2705
2 $key Optional key
2706
3 $value Optional value
2707
2708
B
2709
2710
2711
my ($a, $b, $c, $d, $e, $f, $g) = fromLetters 'b(c(de)f)g';
2712
2713
is_deeply $a->print, <
2714
Key Value
2715
a
2716
b
2717
c
2718
d
2719
e
2720
f
2721
g
2722
END
2723
2724
2725
$c->wrap('z'); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2726
2727
2728
is_deeply $a->print, <
2729
Key Value
2730
a
2731
b
2732
z
2733
c
2734
d
2735
e
2736
f
2737
g
2738
END
2739
2740
2741
$c->parent->unwrap; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2742
2743
2744
is_deeply $a->print, <
2745
Key Value
2746
a
2747
b
2748
c
2749
d
2750
e
2751
f
2752
g
2753
END
2754
2755
2756
$c->wrapChildren("Z"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2757
2758
2759
is_deeply $a->print, <
2760
Key Value
2761
a
2762
b
2763
c
2764
Z
2765
d
2766
e
2767
f
2768
g
2769
END
2770
2771
2772
=head2 wrapChildren($parent, $key, $value)
2773
2774
Wrap the children of the specified parent with a new intermediate parent that becomes the child of the specified parent, optionally setting the L and the L for the new parent. Return the new parent.
2775
2776
Parameter Description
2777
1 $parent Child to wrap
2778
2 $key Optional key for new wrapping parent
2779
3 $value Optional value for new wrapping parent
2780
2781
B
2782
2783
2784
my ($a, $b, $c, $d, $e, $f, $g) = fromLetters 'b(c(de)f)g';
2785
2786
is_deeply $a->print, <
2787
Key Value
2788
a
2789
b
2790
c
2791
d
2792
e
2793
f
2794
g
2795
END
2796
2797
$c->wrap('z');
2798
2799
is_deeply $a->print, <
2800
Key Value
2801
a
2802
b
2803
z
2804
c
2805
d
2806
e
2807
f
2808
g
2809
END
2810
2811
$c->parent->unwrap;
2812
2813
is_deeply $a->print, <
2814
Key Value
2815
a
2816
b
2817
c
2818
d
2819
e
2820
f
2821
g
2822
END
2823
2824
2825
$c->wrapChildren("Z"); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2826
2827
2828
is_deeply $a->print, <
2829
Key Value
2830
a
2831
b
2832
c
2833
Z
2834
d
2835
e
2836
f
2837
g
2838
END
2839
2840
2841
=head2 merge($parent)
2842
2843
Unwrap the children of the specified parent with the whose L fields L that of their parent. Returns the specified parent regardless.
2844
2845
Parameter Description
2846
1 $parent Merging parent
2847
2848
B
2849
2850
2851
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2852
2853
is_deeply $a->print, <
2854
Key Value
2855
a
2856
b
2857
c
2858
d
2859
e
2860
f
2861
g
2862
h
2863
i
2864
j
2865
END
2866
2867
$d->split;
2868
is_deeply $a->print, <
2869
Key Value
2870
a
2871
b
2872
c
2873
d
2874
d
2875
e
2876
d
2877
f
2878
d
2879
g
2880
d
2881
h
2882
i
2883
j
2884
END
2885
2886
2887
$f->parent->mergeLikePrev; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2888
2889
is_deeply $a->print, <
2890
Key Value
2891
a
2892
b
2893
c
2894
d
2895
d
2896
e
2897
f
2898
d
2899
g
2900
d
2901
h
2902
i
2903
j
2904
END
2905
2906
2907
$g->parent->mergeLikeNext; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2908
2909
is_deeply $a->print, <
2910
Key Value
2911
a
2912
b
2913
c
2914
d
2915
d
2916
e
2917
f
2918
d
2919
g
2920
h
2921
i
2922
j
2923
END
2924
2925
2926
$d->merge; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2927
2928
is_deeply $a->print, <
2929
Key Value
2930
a
2931
b
2932
c
2933
d
2934
e
2935
f
2936
g
2937
h
2938
i
2939
j
2940
END
2941
2942
2943
=head2 mergeLikePrev($child)
2944
2945
Merge the preceding sibling of the specified child if that sibling exists and the L data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
2946
2947
Parameter Description
2948
1 $child Child
2949
2950
B
2951
2952
2953
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
2954
2955
is_deeply $a->print, <
2956
Key Value
2957
a
2958
b
2959
c
2960
d
2961
e
2962
f
2963
g
2964
h
2965
i
2966
j
2967
END
2968
2969
$d->split;
2970
is_deeply $a->print, <
2971
Key Value
2972
a
2973
b
2974
c
2975
d
2976
d
2977
e
2978
d
2979
f
2980
d
2981
g
2982
d
2983
h
2984
i
2985
j
2986
END
2987
2988
2989
$f->parent->mergeLikePrev; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
2990
2991
is_deeply $a->print, <
2992
Key Value
2993
a
2994
b
2995
c
2996
d
2997
d
2998
e
2999
f
3000
d
3001
g
3002
d
3003
h
3004
i
3005
j
3006
END
3007
3008
$g->parent->mergeLikeNext;
3009
is_deeply $a->print, <
3010
Key Value
3011
a
3012
b
3013
c
3014
d
3015
d
3016
e
3017
f
3018
d
3019
g
3020
h
3021
i
3022
j
3023
END
3024
3025
$d->merge;
3026
is_deeply $a->print, <
3027
Key Value
3028
a
3029
b
3030
c
3031
d
3032
e
3033
f
3034
g
3035
h
3036
i
3037
j
3038
END
3039
3040
3041
=head2 mergeLikeNext($child)
3042
3043
Merge the following sibling of the specified child if that sibling exists and the L data of the two siblings L. Returns the specified child regardless. From a proposal made by Micaela Monroe.
3044
3045
Parameter Description
3046
1 $child Child
3047
3048
B
3049
3050
3051
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
3052
3053
is_deeply $a->print, <
3054
Key Value
3055
a
3056
b
3057
c
3058
d
3059
e
3060
f
3061
g
3062
h
3063
i
3064
j
3065
END
3066
3067
$d->split;
3068
is_deeply $a->print, <
3069
Key Value
3070
a
3071
b
3072
c
3073
d
3074
d
3075
e
3076
d
3077
f
3078
d
3079
g
3080
d
3081
h
3082
i
3083
j
3084
END
3085
3086
$f->parent->mergeLikePrev;
3087
is_deeply $a->print, <
3088
Key Value
3089
a
3090
b
3091
c
3092
d
3093
d
3094
e
3095
f
3096
d
3097
g
3098
d
3099
h
3100
i
3101
j
3102
END
3103
3104
3105
$g->parent->mergeLikeNext; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3106
3107
is_deeply $a->print, <
3108
Key Value
3109
a
3110
b
3111
c
3112
d
3113
d
3114
e
3115
f
3116
d
3117
g
3118
h
3119
i
3120
j
3121
END
3122
3123
$d->merge;
3124
is_deeply $a->print, <
3125
Key Value
3126
a
3127
b
3128
c
3129
d
3130
e
3131
f
3132
g
3133
h
3134
i
3135
j
3136
END
3137
3138
3139
=head2 split($parent)
3140
3141
Make the specified parent a grandparent of each of its children by interposing a copy of the specified parent between the specified parent and each of its children. Return the specified parent.
3142
3143
Parameter Description
3144
1 $parent Parent to make into a grand parent
3145
3146
B
3147
3148
3149
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(c)d(efgh(i(j)))';
3150
3151
is_deeply $a->print, <
3152
Key Value
3153
a
3154
b
3155
c
3156
d
3157
e
3158
f
3159
g
3160
h
3161
i
3162
j
3163
END
3164
3165
3166
$d->split; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3167
3168
is_deeply $a->print, <
3169
Key Value
3170
a
3171
b
3172
c
3173
d
3174
d
3175
e
3176
d
3177
f
3178
d
3179
g
3180
d
3181
h
3182
i
3183
j
3184
END
3185
3186
$f->parent->mergeLikePrev;
3187
is_deeply $a->print, <
3188
Key Value
3189
a
3190
b
3191
c
3192
d
3193
d
3194
e
3195
f
3196
d
3197
g
3198
d
3199
h
3200
i
3201
j
3202
END
3203
3204
$g->parent->mergeLikeNext;
3205
is_deeply $a->print, <
3206
Key Value
3207
a
3208
b
3209
c
3210
d
3211
d
3212
e
3213
f
3214
d
3215
g
3216
h
3217
i
3218
j
3219
END
3220
3221
$d->merge;
3222
is_deeply $a->print, <
3223
Key Value
3224
a
3225
b
3226
c
3227
d
3228
e
3229
f
3230
g
3231
h
3232
i
3233
j
3234
END
3235
3236
3237
=head1 Traverse
3238
3239
Traverse a tree.
3240
3241
=head2 by($tree, $sub)
3242
3243
Traverse a tree in post-order to process each child with the specified sub and return an array of the results of processing each child. If no sub sub is specified, the children are returned in tree order.
3244
3245
Parameter Description
3246
1 $tree Tree
3247
2 $sub Optional sub to process each child
3248
3249
B
3250
3251
3252
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $s, $t, $x, $y, $z) =
3253
fromLetters 'b(c)y(x)z(st)d(efgh(i(j))))';
3254
3255
is_deeply [$x->context], [$x, $y, $a];
3256
3257
3258
is_deeply join(' ', $a->by(sub{$_[0]->key})), "c b x y s t z e f g j i h d a"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3259
3260
3261
is_deeply join(' ', map{$_->key} $a->by), "c b x y s t z e f g j i h d a"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3262
3263
3264
is_deeply $a->print, <
3265
Key Value
3266
a
3267
b
3268
c
3269
y
3270
x
3271
z
3272
s
3273
t
3274
d
3275
e
3276
f
3277
g
3278
h
3279
i
3280
j
3281
END
3282
3283
$z->cut;
3284
is_deeply $a->print, <
3285
Key Value
3286
a
3287
b
3288
c
3289
y
3290
x
3291
d
3292
e
3293
f
3294
g
3295
h
3296
i
3297
j
3298
END
3299
3300
3301
=head2 select($tree, $select)
3302
3303
Select matching children in a tree in post-order. A child can be selected via named value, array of values, a hash of values, a regular expression or a sub reference.
3304
3305
Parameter Description
3306
1 $tree Tree
3307
2 $select Method to select a child
3308
3309
B
3310
3311
3312
my $a = Tree::Ops::new 'a', 'A';
3313
for(1..2)
3314
{$a->open ('b', "B$_");
3315
$a->single('c', "C$_");
3316
ok $a->activeScope->key eq 'b';
3317
$a->close;
3318
}
3319
$a->single ('d', 'D');
3320
$a->single ('e', 'E');
3321
is_deeply $a->print, <
3322
Key Value
3323
a A
3324
b B1
3325
c C1
3326
b B2
3327
c C2
3328
d D
3329
e E
3330
END
3331
3332
is_deeply [map{$_->value} $a->by], [qw(C1 B1 C2 B2 D E A)];
3333
3334
is_deeply $a->lastMost->prev->prev->first->key, 'c';
3335
is_deeply $a->first->next->last->parent->first->value, 'C2';
3336
3337
3338
is_deeply [map{$_->value} $a->select('b')], [qw(B1 B2)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3339
3340
3341
is_deeply [map{$_->value} $a->select(qr(b|c))], [qw(B1 C1 B2 C2)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3342
3343
3344
is_deeply [map{$_->value} $a->select(sub{$_[0] eq 'd'})], [qw(D)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3345
3346
3347
$a->first->next->stepEnd->stepEnd->first->next->stepBack;
3348
is_deeply $a->print, <
3349
Key Value
3350
a A
3351
b B1
3352
c C1
3353
b B2
3354
d D
3355
c C2
3356
e E
3357
END
3358
3359
3360
=head1 Partitions
3361
3362
Various partitions of the tree
3363
3364
=head2 leaves($tree)
3365
3366
The set of all children without further children, i.e. each leaf of the tree.
3367
3368
Parameter Description
3369
1 $tree Tree
3370
3371
B
3372
3373
3374
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3375
fromLetters 'b(c)y(x)d(efgh(i(j)))';
3376
3377
is_deeply $a->print, <
3378
Key Value
3379
a
3380
b
3381
c
3382
y
3383
x
3384
d
3385
e
3386
f
3387
g
3388
h
3389
i
3390
j
3391
END
3392
3393
is_deeply $a->xml,
3394
' ';
3395
3396
3397
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3398
3399
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3400
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3401
is_deeply [$a->parents], [$a->parentsPostOrder];
3402
3403
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3404
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3405
3406
ok !$j->parents;
3407
3408
ok $a->lastMost == $j;
3409
ok !$a->prevMost;
3410
ok $j->prevMost == $g;
3411
ok $i->prevMost == $g;
3412
ok $h->prevMost == $g;
3413
ok $g->prevMost == $f;
3414
ok $f->prevMost == $e;
3415
ok $e->prevMost == $x;
3416
ok $d->prevMost == $x;
3417
ok $x->prevMost == $c;
3418
ok $y->prevMost == $c;
3419
ok !$c->prevMost;
3420
ok !$b->prevMost;
3421
ok !$a->prevMost;
3422
3423
ok $a->firstMost == $c;
3424
ok $a->nextMost == $c;
3425
ok $b->nextMost == $c;
3426
ok $c->nextMost == $x;
3427
ok $y->nextMost == $x;
3428
ok $x->nextMost == $e;
3429
ok $d->nextMost == $e;
3430
ok $e->nextMost == $f;
3431
ok $f->nextMost == $g;
3432
ok $g->nextMost == $j;
3433
ok $h->nextMost == $j;
3434
ok $i->nextMost == $j;
3435
ok !$j->nextMost;
3436
3437
ok $i->topMost == $a;
3438
3439
3440
=head2 parentsPreOrder($tree)
3441
3442
The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal pre-order.
3443
3444
Parameter Description
3445
1 $tree Tree
3446
3447
B
3448
3449
3450
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3451
fromLetters 'b(c)y(x)d(efgh(i(j)))';
3452
3453
is_deeply $a->print, <
3454
Key Value
3455
a
3456
b
3457
c
3458
y
3459
x
3460
d
3461
e
3462
f
3463
g
3464
h
3465
i
3466
j
3467
END
3468
3469
is_deeply $a->xml,
3470
' ';
3471
3472
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3473
3474
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3475
3476
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3477
is_deeply [$a->parents], [$a->parentsPostOrder];
3478
3479
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3480
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3481
3482
ok !$j->parents;
3483
3484
ok $a->lastMost == $j;
3485
ok !$a->prevMost;
3486
ok $j->prevMost == $g;
3487
ok $i->prevMost == $g;
3488
ok $h->prevMost == $g;
3489
ok $g->prevMost == $f;
3490
ok $f->prevMost == $e;
3491
ok $e->prevMost == $x;
3492
ok $d->prevMost == $x;
3493
ok $x->prevMost == $c;
3494
ok $y->prevMost == $c;
3495
ok !$c->prevMost;
3496
ok !$b->prevMost;
3497
ok !$a->prevMost;
3498
3499
ok $a->firstMost == $c;
3500
ok $a->nextMost == $c;
3501
ok $b->nextMost == $c;
3502
ok $c->nextMost == $x;
3503
ok $y->nextMost == $x;
3504
ok $x->nextMost == $e;
3505
ok $d->nextMost == $e;
3506
ok $e->nextMost == $f;
3507
ok $f->nextMost == $g;
3508
ok $g->nextMost == $j;
3509
ok $h->nextMost == $j;
3510
ok $i->nextMost == $j;
3511
ok !$j->nextMost;
3512
3513
ok $i->topMost == $a;
3514
3515
3516
=head2 parentsPostOrder($tree)
3517
3518
The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
3519
3520
Parameter Description
3521
1 $tree Tree
3522
3523
B
3524
3525
3526
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3527
fromLetters 'b(c)y(x)d(efgh(i(j)))';
3528
3529
is_deeply $a->print, <
3530
Key Value
3531
a
3532
b
3533
c
3534
y
3535
x
3536
d
3537
e
3538
f
3539
g
3540
h
3541
i
3542
j
3543
END
3544
3545
is_deeply $a->xml,
3546
' ';
3547
3548
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3549
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3550
3551
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3552
3553
3554
is_deeply [$a->parents], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3555
3556
3557
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3558
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3559
3560
ok !$j->parents;
3561
3562
ok $a->lastMost == $j;
3563
ok !$a->prevMost;
3564
ok $j->prevMost == $g;
3565
ok $i->prevMost == $g;
3566
ok $h->prevMost == $g;
3567
ok $g->prevMost == $f;
3568
ok $f->prevMost == $e;
3569
ok $e->prevMost == $x;
3570
ok $d->prevMost == $x;
3571
ok $x->prevMost == $c;
3572
ok $y->prevMost == $c;
3573
ok !$c->prevMost;
3574
ok !$b->prevMost;
3575
ok !$a->prevMost;
3576
3577
ok $a->firstMost == $c;
3578
ok $a->nextMost == $c;
3579
ok $b->nextMost == $c;
3580
ok $c->nextMost == $x;
3581
ok $y->nextMost == $x;
3582
ok $x->nextMost == $e;
3583
ok $d->nextMost == $e;
3584
ok $e->nextMost == $f;
3585
ok $f->nextMost == $g;
3586
ok $g->nextMost == $j;
3587
ok $h->nextMost == $j;
3588
ok $i->nextMost == $j;
3589
ok !$j->nextMost;
3590
3591
ok $i->topMost == $a;
3592
3593
3594
=head2 parentsReversePreOrder($tree)
3595
3596
The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse pre-order.
3597
3598
Parameter Description
3599
1 $tree Tree
3600
3601
B
3602
3603
3604
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3605
fromLetters 'b(c)y(x)d(efgh(i(j)))';
3606
3607
is_deeply $a->print, <
3608
Key Value
3609
a
3610
b
3611
c
3612
y
3613
x
3614
d
3615
e
3616
f
3617
g
3618
h
3619
i
3620
j
3621
END
3622
3623
is_deeply $a->xml,
3624
' ';
3625
3626
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3627
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3628
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3629
is_deeply [$a->parents], [$a->parentsPostOrder];
3630
3631
3632
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3633
3634
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
3635
3636
ok !$j->parents;
3637
3638
ok $a->lastMost == $j;
3639
ok !$a->prevMost;
3640
ok $j->prevMost == $g;
3641
ok $i->prevMost == $g;
3642
ok $h->prevMost == $g;
3643
ok $g->prevMost == $f;
3644
ok $f->prevMost == $e;
3645
ok $e->prevMost == $x;
3646
ok $d->prevMost == $x;
3647
ok $x->prevMost == $c;
3648
ok $y->prevMost == $c;
3649
ok !$c->prevMost;
3650
ok !$b->prevMost;
3651
ok !$a->prevMost;
3652
3653
ok $a->firstMost == $c;
3654
ok $a->nextMost == $c;
3655
ok $b->nextMost == $c;
3656
ok $c->nextMost == $x;
3657
ok $y->nextMost == $x;
3658
ok $x->nextMost == $e;
3659
ok $d->nextMost == $e;
3660
ok $e->nextMost == $f;
3661
ok $f->nextMost == $g;
3662
ok $g->nextMost == $j;
3663
ok $h->nextMost == $j;
3664
ok $i->nextMost == $j;
3665
ok !$j->nextMost;
3666
3667
ok $i->topMost == $a;
3668
3669
3670
=head2 parentsReversePostOrder($tree)
3671
3672
The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in reverse post-order.
3673
3674
Parameter Description
3675
1 $tree Tree
3676
3677
B
3678
3679
3680
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3681
fromLetters 'b(c)y(x)d(efgh(i(j)))';
3682
3683
is_deeply $a->print, <
3684
Key Value
3685
a
3686
b
3687
c
3688
y
3689
x
3690
d
3691
e
3692
f
3693
g
3694
h
3695
i
3696
j
3697
END
3698
3699
is_deeply $a->xml,
3700
' ';
3701
3702
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3703
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
3704
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
3705
is_deeply [$a->parents], [$a->parentsPostOrder];
3706
3707
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
3708
3709
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3710
3711
3712
ok !$j->parents;
3713
3714
ok $a->lastMost == $j;
3715
ok !$a->prevMost;
3716
ok $j->prevMost == $g;
3717
ok $i->prevMost == $g;
3718
ok $h->prevMost == $g;
3719
ok $g->prevMost == $f;
3720
ok $f->prevMost == $e;
3721
ok $e->prevMost == $x;
3722
ok $d->prevMost == $x;
3723
ok $x->prevMost == $c;
3724
ok $y->prevMost == $c;
3725
ok !$c->prevMost;
3726
ok !$b->prevMost;
3727
ok !$a->prevMost;
3728
3729
ok $a->firstMost == $c;
3730
ok $a->nextMost == $c;
3731
ok $b->nextMost == $c;
3732
ok $c->nextMost == $x;
3733
ok $y->nextMost == $x;
3734
ok $x->nextMost == $e;
3735
ok $d->nextMost == $e;
3736
ok $e->nextMost == $f;
3737
ok $f->nextMost == $g;
3738
ok $g->nextMost == $j;
3739
ok $h->nextMost == $j;
3740
ok $i->nextMost == $j;
3741
ok !$j->nextMost;
3742
3743
ok $i->topMost == $a;
3744
3745
3746
=head2 parents($tree)
3747
3748
The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in normal post-order.
3749
3750
Parameter Description
3751
1 $tree Tree
3752
3753
B
3754
3755
3756
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
3757
fromLetters 'b(c)y(x)d(efgh(i(j)))';
3758
3759
is_deeply $a->print, <
3760
Key Value
3761
a
3762
b
3763
c
3764
y
3765
x
3766
d
3767
e
3768
f
3769
g
3770
h
3771
i
3772
j
3773
END
3774
3775
is_deeply $a->xml,
3776
' ';
3777
3778
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
3779
3780
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3781
3782
3783
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3784
3785
3786
is_deeply [$a->parents], [$a->parentsPostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3787
3788
3789
3790
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3791
3792
3793
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3794
3795
3796
3797
ok !$j->parents; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3798
3799
3800
ok $a->lastMost == $j;
3801
ok !$a->prevMost;
3802
ok $j->prevMost == $g;
3803
ok $i->prevMost == $g;
3804
ok $h->prevMost == $g;
3805
ok $g->prevMost == $f;
3806
ok $f->prevMost == $e;
3807
ok $e->prevMost == $x;
3808
ok $d->prevMost == $x;
3809
ok $x->prevMost == $c;
3810
ok $y->prevMost == $c;
3811
ok !$c->prevMost;
3812
ok !$b->prevMost;
3813
ok !$a->prevMost;
3814
3815
ok $a->firstMost == $c;
3816
ok $a->nextMost == $c;
3817
ok $b->nextMost == $c;
3818
ok $c->nextMost == $x;
3819
ok $y->nextMost == $x;
3820
ok $x->nextMost == $e;
3821
ok $d->nextMost == $e;
3822
ok $e->nextMost == $f;
3823
ok $f->nextMost == $g;
3824
ok $g->nextMost == $j;
3825
ok $h->nextMost == $j;
3826
ok $i->nextMost == $j;
3827
ok !$j->nextMost;
3828
3829
ok $i->topMost == $a;
3830
3831
3832
=head1 Order
3833
3834
Check the order and relative position of children in a tree.
3835
3836
=head2 above($first, $second)
3837
3838
Return the first child if it is above the second child else return B.
3839
3840
Parameter Description
3841
1 $first First child
3842
2 $second Second child
3843
3844
B
3845
3846
3847
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
3848
fromLetters('b(c(d(efgh(i(j)k)l)m)n');
3849
3850
is_deeply $a->print, <
3851
Key Value
3852
a
3853
b
3854
c
3855
d
3856
e
3857
f
3858
g
3859
h
3860
i
3861
j
3862
k
3863
l
3864
m
3865
n
3866
END
3867
3868
3869
ok $c->above($j) == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3870
3871
3872
ok !$m->above($j); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3873
3874
3875
ok $i->below($b) == $i;
3876
ok !$i->below($n);
3877
3878
ok $n->after($e) == $n;
3879
ok !$k->after($c);
3880
3881
ok $c->before($n) == $c;
3882
ok !$c->before($m);
3883
3884
is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
3885
ok !$d->lineage($m);
3886
3887
3888
=head2 below($first, $second)
3889
3890
Return the first child if it is below the second child else return B.
3891
3892
Parameter Description
3893
1 $first First child
3894
2 $second Second child
3895
3896
B
3897
3898
3899
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
3900
fromLetters('b(c(d(efgh(i(j)k)l)m)n');
3901
3902
is_deeply $a->print, <
3903
Key Value
3904
a
3905
b
3906
c
3907
d
3908
e
3909
f
3910
g
3911
h
3912
i
3913
j
3914
k
3915
l
3916
m
3917
n
3918
END
3919
3920
ok $c->above($j) == $c;
3921
ok !$m->above($j);
3922
3923
3924
ok $i->below($b) == $i; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3925
3926
3927
ok !$i->below($n); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3928
3929
3930
ok $n->after($e) == $n;
3931
ok !$k->after($c);
3932
3933
ok $c->before($n) == $c;
3934
ok !$c->before($m);
3935
3936
is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
3937
ok !$d->lineage($m);
3938
3939
3940
=head2 after($first, $second)
3941
3942
Return the first child if it occurs strictly after the second child in the tree or else B if the first child is L, L or L the second child.
3943
3944
Parameter Description
3945
1 $first First child
3946
2 $second Second child
3947
3948
B
3949
3950
3951
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
3952
fromLetters('b(c(d(efgh(i(j)k)l)m)n');
3953
3954
is_deeply $a->print, <
3955
Key Value
3956
a
3957
b
3958
c
3959
d
3960
e
3961
f
3962
g
3963
h
3964
i
3965
j
3966
k
3967
l
3968
m
3969
n
3970
END
3971
3972
ok $c->above($j) == $c;
3973
ok !$m->above($j);
3974
3975
ok $i->below($b) == $i;
3976
ok !$i->below($n);
3977
3978
3979
ok $n->after($e) == $n; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3980
3981
3982
ok !$k->after($c); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
3983
3984
3985
ok $c->before($n) == $c;
3986
ok !$c->before($m);
3987
3988
is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
3989
ok !$d->lineage($m);
3990
3991
3992
=head2 before($first, $second)
3993
3994
Return the first child if it occurs strictly before the second child in the tree or else B if the first child is L, L or L the second child.
3995
3996
Parameter Description
3997
1 $first First child
3998
2 $second Second child
3999
4000
B
4001
4002
4003
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
4004
fromLetters('b(c(d(efgh(i(j)k)l)m)n');
4005
4006
is_deeply $a->print, <
4007
Key Value
4008
a
4009
b
4010
c
4011
d
4012
e
4013
f
4014
g
4015
h
4016
i
4017
j
4018
k
4019
l
4020
m
4021
n
4022
END
4023
4024
ok $c->above($j) == $c;
4025
ok !$m->above($j);
4026
4027
ok $i->below($b) == $i;
4028
ok !$i->below($n);
4029
4030
ok $n->after($e) == $n;
4031
ok !$k->after($c);
4032
4033
4034
ok $c->before($n) == $c; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4035
4036
4037
ok !$c->before($m); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4038
4039
4040
is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)];
4041
ok !$d->lineage($m);
4042
4043
4044
=head1 Paths
4045
4046
Find paths between nodes
4047
4048
=head2 path($child)
4049
4050
Return the list of zero based child indexes for the path from the root of the tree containing the specified child to the specified child for use by the L method.
4051
4052
Parameter Description
4053
1 $child Child
4054
4055
B
4056
4057
4058
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
4059
4060
is_deeply $a->print, <
4061
Key Value
4062
a
4063
b
4064
c
4065
d
4066
e
4067
f
4068
g
4069
h
4070
i
4071
j
4072
END
4073
4074
ok $a->go(0,1,0,1) == $g;
4075
ok $d->go(0,0) == $f;
4076
4077
4078
is_deeply [$e->path], [0,1,0]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4079
4080
4081
is_deeply [$g->pathFrom($d)], [0,1]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4082
4083
4084
is_deeply $b->dup->print, <
4085
Key Value
4086
b
4087
c
4088
d
4089
e
4090
f
4091
g
4092
h
4093
i
4094
END
4095
4096
my $B = $b->transcribe;
4097
4098
$b->by(sub
4099
{my ($c) = @_;
4100
4101
my @path = $c->pathFrom($b); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4102
4103
4104
my $C = $B->go(@path); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4105
4106
is_deeply $c->key, $C->key;
4107
is_deeply $c->{transcribedTo}, $C;
4108
is_deeply $C->{transcribedFrom}, $c;
4109
});
4110
4111
is_deeply $B->print, <
4112
Key Value
4113
b
4114
c
4115
d
4116
e
4117
f
4118
g
4119
h
4120
i
4121
END
4122
4123
4124
=head2 pathFrom($child, $ancestor)
4125
4126
Return the list of zero based child indexes for the path from the specified ancestor to the specified child for use by the L method else confess if the ancestor is not, in fact, an ancestor.
4127
4128
Parameter Description
4129
1 $child Child
4130
2 $ancestor Ancestor
4131
4132
B
4133
4134
4135
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cd(e(fg)h)i)j';
4136
4137
is_deeply $a->print, <
4138
Key Value
4139
a
4140
b
4141
c
4142
d
4143
e
4144
f
4145
g
4146
h
4147
i
4148
j
4149
END
4150
4151
ok $a->go(0,1,0,1) == $g;
4152
ok $d->go(0,0) == $f;
4153
4154
is_deeply [$e->path], [0,1,0];
4155
4156
is_deeply [$g->pathFrom($d)], [0,1]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4157
4158
4159
is_deeply $b->dup->print, <
4160
Key Value
4161
b
4162
c
4163
d
4164
e
4165
f
4166
g
4167
h
4168
i
4169
END
4170
4171
my $B = $b->transcribe;
4172
4173
$b->by(sub
4174
{my ($c) = @_;
4175
4176
my @path = $c->pathFrom($b); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4177
4178
my $C = $B->go(@path);
4179
is_deeply $c->key, $C->key;
4180
is_deeply $c->{transcribedTo}, $C;
4181
is_deeply $C->{transcribedFrom}, $c;
4182
});
4183
4184
is_deeply $B->print, <
4185
Key Value
4186
b
4187
c
4188
d
4189
e
4190
f
4191
g
4192
h
4193
i
4194
END
4195
4196
4197
=head2 siblingsBefore($child)
4198
4199
Return a list of siblings before the specified child.
4200
4201
Parameter Description
4202
1 $child Child
4203
4204
B
4205
4206
4207
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cde(f)ghi)j';
4208
is_deeply $a->print, <
4209
Key Value
4210
a
4211
b
4212
c
4213
d
4214
e
4215
f
4216
g
4217
h
4218
i
4219
j
4220
END
4221
4222
is_deeply [$d->siblingsStrictlyBetween($h)], [$e, $g];
4223
is_deeply [$d->siblingsAfter], [$e, $g, $h, $i];
4224
4225
is_deeply [$g->siblingsBefore], [$c, $d, $e]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4226
4227
eval {$e->siblingsStrictlyBetween($f)};
4228
ok $@ =~ m(Must be siblings);
4229
4230
4231
=head2 siblingsAfter($child)
4232
4233
Return a list of siblings after the specified child.
4234
4235
Parameter Description
4236
1 $child Child
4237
4238
B
4239
4240
4241
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cde(f)ghi)j';
4242
is_deeply $a->print, <
4243
Key Value
4244
a
4245
b
4246
c
4247
d
4248
e
4249
f
4250
g
4251
h
4252
i
4253
j
4254
END
4255
4256
is_deeply [$d->siblingsStrictlyBetween($h)], [$e, $g];
4257
4258
is_deeply [$d->siblingsAfter], [$e, $g, $h, $i]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4259
4260
is_deeply [$g->siblingsBefore], [$c, $d, $e];
4261
eval {$e->siblingsStrictlyBetween($f)};
4262
ok $@ =~ m(Must be siblings);
4263
4264
4265
=head2 siblingsStrictlyBetween($start, $finish)
4266
4267
Return a list of the siblings strictly between two children of the same parent else return B.
4268
4269
Parameter Description
4270
1 $start Start child
4271
2 $finish Finish child
4272
4273
B
4274
4275
4276
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j) = fromLetters 'b(cde(f)ghi)j';
4277
is_deeply $a->print, <
4278
Key Value
4279
a
4280
b
4281
c
4282
d
4283
e
4284
f
4285
g
4286
h
4287
i
4288
j
4289
END
4290
4291
4292
is_deeply [$d->siblingsStrictlyBetween($h)], [$e, $g]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4293
4294
is_deeply [$d->siblingsAfter], [$e, $g, $h, $i];
4295
is_deeply [$g->siblingsBefore], [$c, $d, $e];
4296
4297
eval {$e->siblingsStrictlyBetween($f)}; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4298
4299
ok $@ =~ m(Must be siblings);
4300
4301
4302
=head2 lineage($child, $ancestor)
4303
4304
Return the path from the specified child to the specified ancestor else return B if the child is not a descendant of the ancestor.
4305
4306
Parameter Description
4307
1 $child Child
4308
2 $ancestor Ancestor
4309
4310
B
4311
4312
4313
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n) =
4314
fromLetters('b(c(d(efgh(i(j)k)l)m)n');
4315
4316
is_deeply $a->print, <
4317
Key Value
4318
a
4319
b
4320
c
4321
d
4322
e
4323
f
4324
g
4325
h
4326
i
4327
j
4328
k
4329
l
4330
m
4331
n
4332
END
4333
4334
ok $c->above($j) == $c;
4335
ok !$m->above($j);
4336
4337
ok $i->below($b) == $i;
4338
ok !$i->below($n);
4339
4340
ok $n->after($e) == $n;
4341
ok !$k->after($c);
4342
4343
ok $c->before($n) == $c;
4344
ok !$c->before($m);
4345
4346
4347
is_deeply [map{$_->key} $j->lineage($d)], [qw(j i h d)]; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4348
4349
4350
ok !$d->lineage($m); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4351
4352
4353
4354
=head2 nextPreOrderPath($start)
4355
4356
Return a list of children visited between the specified child and the next child in pre-order.
4357
4358
Parameter Description
4359
1 $start The child at the start of the path
4360
4361
B
4362
4363
4364
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4365
fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4366
my @p = [$a];
4367
4368
for(1..99)
4369
4370
{my @n = $p[-1][-1]->nextPreOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4371
4372
last unless @n;
4373
push @p, [@n];
4374
}
4375
4376
is_deeply $a->print, <
4377
Key Value
4378
a
4379
b
4380
c
4381
d
4382
e
4383
f
4384
g
4385
h
4386
i
4387
j
4388
k
4389
l
4390
m
4391
n
4392
o
4393
p
4394
q
4395
r
4396
END
4397
4398
my @pre = map{[map{$_->key} @$_]} @p;
4399
is_deeply scalar(@pre), scalar(['a'..'r']->@*);
4400
is_deeply [@pre],
4401
[["a"],
4402
["b"],
4403
["c"],
4404
["d"],
4405
["e"],
4406
["f"],
4407
["g"],
4408
["e", "h"],
4409
["i"],
4410
["j"],
4411
["k"],
4412
["l"],
4413
["j", "m"],
4414
["i", "n"],
4415
["d", "o"],
4416
["p"],
4417
["c", "q"],
4418
["b", "r"]];
4419
4420
4421
=head2 nextPostOrderPath($start)
4422
4423
Return a list of children visited between the specified child and the next child in post-order.
4424
4425
Parameter Description
4426
1 $start The child at the start of the path
4427
4428
B
4429
4430
4431
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4432
fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4433
4434
my @n = $a;
4435
my @p;
4436
for(1..99)
4437
4438
{@n = $n[-1]->nextPostOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4439
4440
last unless @n;
4441
push @p, [@n];
4442
last if $n[-1] == $a;
4443
}
4444
4445
is_deeply $a->print, <
4446
Key Value
4447
a
4448
b
4449
c
4450
d
4451
e
4452
f
4453
g
4454
h
4455
i
4456
j
4457
k
4458
l
4459
m
4460
n
4461
o
4462
p
4463
q
4464
r
4465
END
4466
4467
my @post = map{[map{$_->key} @$_]} @p;
4468
is_deeply scalar(@post), scalar(['a'..'r']->@*);
4469
is_deeply [@post],
4470
[["b" .. "f"],
4471
["g"],
4472
["e"],
4473
["h"],
4474
["i", "j", "k"],
4475
["l"],
4476
["j"],
4477
["m"],
4478
["i"],
4479
["n"],
4480
["d"],
4481
["o"],
4482
["p"],
4483
["c"],
4484
["q"],
4485
["b"],
4486
["r"],
4487
["a"]];
4488
4489
4490
=head2 prevPostOrderPath($start)
4491
4492
Return a list of children visited between the specified child and the previous child in post-order.
4493
4494
Parameter Description
4495
1 $start The child at the start of the path
4496
4497
B
4498
4499
4500
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4501
fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4502
my @p = [$a];
4503
4504
for(1..99)
4505
4506
{my @n = $p[-1][-1]->prevPostOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4507
4508
last unless @n;
4509
push @p, [@n];
4510
}
4511
4512
is_deeply $a->print, <
4513
Key Value
4514
a
4515
b
4516
c
4517
d
4518
e
4519
f
4520
g
4521
h
4522
i
4523
j
4524
k
4525
l
4526
m
4527
n
4528
o
4529
p
4530
q
4531
r
4532
END
4533
4534
my @post = map{[map{$_->key} @$_]} @p;
4535
is_deeply scalar(@post), scalar(['a'..'r']->@*);
4536
is_deeply [@post],
4537
[["a"],
4538
["r"],
4539
["b"],
4540
["q"],
4541
["c"],
4542
["p"],
4543
["o"],
4544
["d"],
4545
["n"],
4546
["i"],
4547
["m"],
4548
["j"],
4549
["l"],
4550
["k"],
4551
["j", "i", "h"],
4552
["e"],
4553
["g"],
4554
["f"]];
4555
4556
4557
=head2 prevPreOrderPath($start)
4558
4559
Return a list of children visited between the specified child and the previous child in pre-order.
4560
4561
Parameter Description
4562
1 $start The child at the start of the path
4563
4564
B
4565
4566
4567
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n, $o, $p, $q, $r) =
4568
fromLetters 'b(c(d(e(fg)hi(j(kl)m)n)op)q)r';
4569
4570
my @n = $a;
4571
my @p;
4572
for(1..99)
4573
4574
{@n = $n[-1]->prevPreOrderPath; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
4575
4576
last unless @n;
4577
push @p, [@n];
4578
last if $n[-1] == $a;
4579
}
4580
4581
is_deeply $a->print, <
4582
Key Value
4583
a
4584
b
4585
c
4586
d
4587
e
4588
f
4589
g
4590
h
4591
i
4592
j
4593
k
4594
l
4595
m
4596
n
4597
o
4598
p
4599
q
4600
r
4601
END
4602
4603
my @pre = map{[map{$_->key} @$_]} @p;
4604
is_deeply scalar(@pre), scalar(['a'..'r']->@*);
4605
is_deeply [@pre],
4606
[["r"],
4607
["b", "q"],
4608
["c", "p"],
4609
["o"],
4610
["d", "n"],
4611
["i", "m"],
4612
["j", "l"],
4613
["k"],
4614
["j"],
4615
["i"],
4616
["h"],
4617
["e", "g"],
4618
["f"],
4619
["e"],
4620
["d"],
4621
["c"],
4622
["b"],
4623
["a"]];
4624
4625
4626
=head1 Print
4627
4628
Print a tree.
4629
4630
=head2 printPreOrder($tree, $print)
4631
4632
Print tree in normal pre-order.
4633
4634
Parameter Description
4635
1 $tree Tree
4636
2 $print Optional print method
4637
4638
B
4639
4640
4641
my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4642
my sub test(@) {join ' ', map{join '', $_->key} @_}
4643
4644
4645
is_deeply $a->printPreOrder, <
4646
4647
Key Value
4648
a
4649
b
4650
c
4651
d
4652
END
4653
4654
is_deeply test($a->nextPreOrderPath), 'b';
4655
is_deeply test($b->nextPreOrderPath), 'c';
4656
is_deeply test($c->nextPreOrderPath), 'b d';
4657
is_deeply test($d->nextPreOrderPath), '';
4658
4659
is_deeply $a->printPostOrder, <
4660
Key Value
4661
c
4662
b
4663
d
4664
a
4665
END
4666
4667
is_deeply test($a->nextPostOrderPath), 'b c';
4668
is_deeply test($c->nextPostOrderPath), 'b';
4669
is_deeply test($b->nextPostOrderPath), 'd';
4670
is_deeply test($d->nextPostOrderPath), 'a';
4671
4672
is_deeply $a->printReversePreOrder, <
4673
Key Value
4674
a
4675
d
4676
b
4677
c
4678
END
4679
is_deeply test($a->prevPreOrderPath), 'd';
4680
is_deeply test($d->prevPreOrderPath), 'b c';
4681
is_deeply test($c->prevPreOrderPath), 'b';
4682
is_deeply test($b->prevPreOrderPath), 'a';
4683
4684
is_deeply $a->printReversePostOrder, <
4685
Key Value
4686
d
4687
c
4688
b
4689
a
4690
END
4691
4692
is_deeply test($a->prevPostOrderPath), 'd';
4693
is_deeply test($d->prevPostOrderPath), 'b';
4694
is_deeply test($b->prevPostOrderPath), 'c';
4695
is_deeply test($c->prevPostOrderPath), '';
4696
4697
4698
=head2 printPostOrder($tree, $print)
4699
4700
Print tree in normal post-order.
4701
4702
Parameter Description
4703
1 $tree Tree
4704
2 $print Optional print method
4705
4706
B
4707
4708
4709
my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4710
my sub test(@) {join ' ', map{join '', $_->key} @_}
4711
4712
is_deeply $a->printPreOrder, <
4713
Key Value
4714
a
4715
b
4716
c
4717
d
4718
END
4719
4720
is_deeply test($a->nextPreOrderPath), 'b';
4721
is_deeply test($b->nextPreOrderPath), 'c';
4722
is_deeply test($c->nextPreOrderPath), 'b d';
4723
is_deeply test($d->nextPreOrderPath), '';
4724
4725
4726
is_deeply $a->printPostOrder, <
4727
4728
Key Value
4729
c
4730
b
4731
d
4732
a
4733
END
4734
4735
is_deeply test($a->nextPostOrderPath), 'b c';
4736
is_deeply test($c->nextPostOrderPath), 'b';
4737
is_deeply test($b->nextPostOrderPath), 'd';
4738
is_deeply test($d->nextPostOrderPath), 'a';
4739
4740
is_deeply $a->printReversePreOrder, <
4741
Key Value
4742
a
4743
d
4744
b
4745
c
4746
END
4747
is_deeply test($a->prevPreOrderPath), 'd';
4748
is_deeply test($d->prevPreOrderPath), 'b c';
4749
is_deeply test($c->prevPreOrderPath), 'b';
4750
is_deeply test($b->prevPreOrderPath), 'a';
4751
4752
is_deeply $a->printReversePostOrder, <
4753
Key Value
4754
d
4755
c
4756
b
4757
a
4758
END
4759
4760
is_deeply test($a->prevPostOrderPath), 'd';
4761
is_deeply test($d->prevPostOrderPath), 'b';
4762
is_deeply test($b->prevPostOrderPath), 'c';
4763
is_deeply test($c->prevPostOrderPath), '';
4764
4765
4766
=head2 printReversePreOrder($tree, $print)
4767
4768
Print tree in reverse pre-order
4769
4770
Parameter Description
4771
1 $tree Tree
4772
2 $print Optional print method
4773
4774
B
4775
4776
4777
my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4778
my sub test(@) {join ' ', map{join '', $_->key} @_}
4779
4780
is_deeply $a->printPreOrder, <
4781
Key Value
4782
a
4783
b
4784
c
4785
d
4786
END
4787
4788
is_deeply test($a->nextPreOrderPath), 'b';
4789
is_deeply test($b->nextPreOrderPath), 'c';
4790
is_deeply test($c->nextPreOrderPath), 'b d';
4791
is_deeply test($d->nextPreOrderPath), '';
4792
4793
is_deeply $a->printPostOrder, <
4794
Key Value
4795
c
4796
b
4797
d
4798
a
4799
END
4800
4801
is_deeply test($a->nextPostOrderPath), 'b c';
4802
is_deeply test($c->nextPostOrderPath), 'b';
4803
is_deeply test($b->nextPostOrderPath), 'd';
4804
is_deeply test($d->nextPostOrderPath), 'a';
4805
4806
4807
is_deeply $a->printReversePreOrder, <
4808
4809
Key Value
4810
a
4811
d
4812
b
4813
c
4814
END
4815
is_deeply test($a->prevPreOrderPath), 'd';
4816
is_deeply test($d->prevPreOrderPath), 'b c';
4817
is_deeply test($c->prevPreOrderPath), 'b';
4818
is_deeply test($b->prevPreOrderPath), 'a';
4819
4820
is_deeply $a->printReversePostOrder, <
4821
Key Value
4822
d
4823
c
4824
b
4825
a
4826
END
4827
4828
is_deeply test($a->prevPostOrderPath), 'd';
4829
is_deeply test($d->prevPostOrderPath), 'b';
4830
is_deeply test($b->prevPostOrderPath), 'c';
4831
is_deeply test($c->prevPostOrderPath), '';
4832
4833
4834
=head2 printReversePostOrder($tree, $print)
4835
4836
Print tree in reverse post-order
4837
4838
Parameter Description
4839
1 $tree Tree
4840
2 $print Optional print method
4841
4842
B
4843
4844
4845
my ($a, $b, $c, $d) = fromLetters 'b(c)d';
4846
my sub test(@) {join ' ', map{join '', $_->key} @_}
4847
4848
is_deeply $a->printPreOrder, <
4849
Key Value
4850
a
4851
b
4852
c
4853
d
4854
END
4855
4856
is_deeply test($a->nextPreOrderPath), 'b';
4857
is_deeply test($b->nextPreOrderPath), 'c';
4858
is_deeply test($c->nextPreOrderPath), 'b d';
4859
is_deeply test($d->nextPreOrderPath), '';
4860
4861
is_deeply $a->printPostOrder, <
4862
Key Value
4863
c
4864
b
4865
d
4866
a
4867
END
4868
4869
is_deeply test($a->nextPostOrderPath), 'b c';
4870
is_deeply test($c->nextPostOrderPath), 'b';
4871
is_deeply test($b->nextPostOrderPath), 'd';
4872
is_deeply test($d->nextPostOrderPath), 'a';
4873
4874
is_deeply $a->printReversePreOrder, <
4875
Key Value
4876
a
4877
d
4878
b
4879
c
4880
END
4881
is_deeply test($a->prevPreOrderPath), 'd';
4882
is_deeply test($d->prevPreOrderPath), 'b c';
4883
is_deeply test($c->prevPreOrderPath), 'b';
4884
is_deeply test($b->prevPreOrderPath), 'a';
4885
4886
4887
is_deeply $a->printReversePostOrder, <
4888
4889
Key Value
4890
d
4891
c
4892
b
4893
a
4894
END
4895
4896
is_deeply test($a->prevPostOrderPath), 'd';
4897
is_deeply test($d->prevPostOrderPath), 'b';
4898
is_deeply test($b->prevPostOrderPath), 'c';
4899
is_deeply test($c->prevPostOrderPath), '';
4900
4901
4902
=head2 print($tree, $print)
4903
4904
Print tree in normal pre-order.
4905
4906
Parameter Description
4907
1 $tree Tree
4908
2 $print Optional print method
4909
4910
B
4911
4912
4913
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
4914
fromLetters 'b(c)y(x)d(efgh(i(j)))';
4915
4916
4917
is_deeply $a->print, <
4918
4919
Key Value
4920
a
4921
b
4922
c
4923
y
4924
x
4925
d
4926
e
4927
f
4928
g
4929
h
4930
i
4931
j
4932
END
4933
4934
is_deeply $a->xml,
4935
' ';
4936
4937
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
4938
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
4939
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
4940
is_deeply [$a->parents], [$a->parentsPostOrder];
4941
4942
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
4943
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
4944
4945
ok !$j->parents;
4946
4947
ok $a->lastMost == $j;
4948
ok !$a->prevMost;
4949
ok $j->prevMost == $g;
4950
ok $i->prevMost == $g;
4951
ok $h->prevMost == $g;
4952
ok $g->prevMost == $f;
4953
ok $f->prevMost == $e;
4954
ok $e->prevMost == $x;
4955
ok $d->prevMost == $x;
4956
ok $x->prevMost == $c;
4957
ok $y->prevMost == $c;
4958
ok !$c->prevMost;
4959
ok !$b->prevMost;
4960
ok !$a->prevMost;
4961
4962
ok $a->firstMost == $c;
4963
ok $a->nextMost == $c;
4964
ok $b->nextMost == $c;
4965
ok $c->nextMost == $x;
4966
ok $y->nextMost == $x;
4967
ok $x->nextMost == $e;
4968
ok $d->nextMost == $e;
4969
ok $e->nextMost == $f;
4970
ok $f->nextMost == $g;
4971
ok $g->nextMost == $j;
4972
ok $h->nextMost == $j;
4973
ok $i->nextMost == $j;
4974
ok !$j->nextMost;
4975
4976
ok $i->topMost == $a;
4977
4978
4979
=head2 brackets($tree, $print, $separator)
4980
4981
Bracketed string representation of a tree.
4982
4983
Parameter Description
4984
1 $tree Tree
4985
2 $print Optional print method
4986
3 $separator Optional child separator
4987
4988
B
4989
4990
4991
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
4992
fromLetters 'b(c)y(x)d(efgh(i(j)))';
4993
4994
is_deeply $a->print, <
4995
Key Value
4996
a
4997
b
4998
c
4999
y
5000
x
5001
d
5002
e
5003
f
5004
g
5005
h
5006
i
5007
j
5008
END
5009
5010
is_deeply $a->xml,
5011
' ';
5012
5013
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
5014
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
5015
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
5016
is_deeply [$a->parents], [$a->parentsPostOrder];
5017
5018
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
5019
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
5020
5021
ok !$j->parents;
5022
5023
ok $a->lastMost == $j;
5024
ok !$a->prevMost;
5025
ok $j->prevMost == $g;
5026
ok $i->prevMost == $g;
5027
ok $h->prevMost == $g;
5028
ok $g->prevMost == $f;
5029
ok $f->prevMost == $e;
5030
ok $e->prevMost == $x;
5031
ok $d->prevMost == $x;
5032
ok $x->prevMost == $c;
5033
ok $y->prevMost == $c;
5034
ok !$c->prevMost;
5035
ok !$b->prevMost;
5036
ok !$a->prevMost;
5037
5038
ok $a->firstMost == $c;
5039
ok $a->nextMost == $c;
5040
ok $b->nextMost == $c;
5041
ok $c->nextMost == $x;
5042
ok $y->nextMost == $x;
5043
ok $x->nextMost == $e;
5044
ok $d->nextMost == $e;
5045
ok $e->nextMost == $f;
5046
ok $f->nextMost == $g;
5047
ok $g->nextMost == $j;
5048
ok $h->nextMost == $j;
5049
ok $i->nextMost == $j;
5050
ok !$j->nextMost;
5051
5052
ok $i->topMost == $a;
5053
5054
5055
=head2 xml($tree, $print)
5056
5057
Print a tree as as xml.
5058
5059
Parameter Description
5060
1 $tree Tree
5061
2 $print Optional print method
5062
5063
B
5064
5065
5066
my ($a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $x, $y) =
5067
fromLetters 'b(c)y(x)d(efgh(i(j)))';
5068
5069
is_deeply $a->print, <
5070
Key Value
5071
a
5072
b
5073
c
5074
y
5075
x
5076
d
5077
e
5078
f
5079
g
5080
h
5081
i
5082
j
5083
END
5084
5085
5086
is_deeply $a->xml, # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
5087
5088
' ';
5089
5090
is_deeply [$c, $x, $e, $f, $g, $j], [$a->leaves];
5091
is_deeply [$a, $b, $y, $d, $h, $i], [$a->parentsPreOrder];
5092
is_deeply [$b, $y, $i, $h, $d, $a], [$a->parentsPostOrder];
5093
is_deeply [$a->parents], [$a->parentsPostOrder];
5094
5095
is_deeply [$a, $d, $h, $i, $y, $b], [$a->parentsReversePreOrder];
5096
is_deeply [$i, $h, $d, $y, $b, $a], [$a->parentsReversePostOrder];
5097
5098
ok !$j->parents;
5099
5100
ok $a->lastMost == $j;
5101
ok !$a->prevMost;
5102
ok $j->prevMost == $g;
5103
ok $i->prevMost == $g;
5104
ok $h->prevMost == $g;
5105
ok $g->prevMost == $f;
5106
ok $f->prevMost == $e;
5107
ok $e->prevMost == $x;
5108
ok $d->prevMost == $x;
5109
ok $x->prevMost == $c;
5110
ok $y->prevMost == $c;
5111
ok !$c->prevMost;
5112
ok !$b->prevMost;
5113
ok !$a->prevMost;
5114
5115
ok $a->firstMost == $c;
5116
ok $a->nextMost == $c;
5117
ok $b->nextMost == $c;
5118
ok $c->nextMost == $x;
5119
ok $y->nextMost == $x;
5120
ok $x->nextMost == $e;
5121
ok $d->nextMost == $e;
5122
ok $e->nextMost == $f;
5123
ok $f->nextMost == $g;
5124
ok $g->nextMost == $j;
5125
ok $h->nextMost == $j;
5126
ok $i->nextMost == $j;
5127
ok !$j->nextMost;
5128
5129
ok $i->topMost == $a;
5130
5131
5132
=head1 Data Structures
5133
5134
Data structures use by this package.
5135
5136
5137
=head2 Tree::Ops Definition
5138
5139
5140
Child in the tree.
5141
5142
5143
5144
5145
=head3 Output fields
5146
5147
5148
=head4 children
5149
5150
Children of this child.
5151
5152
=head4 key
5153
5154
Key for this child - any thing that can be compared with the L operator.
5155
5156
=head4 lastChild
5157
5158
Last active child chain - enables us to find the currently open scope from the start if the tree.
5159
5160
=head4 parent
5161
5162
Parent for this child.
5163
5164
=head4 value
5165
5166
Value for this child.
5167
5168
5169
5170
=head1 Private Methods
5171
5172
=head2 setParentOfChild($child, $parent)
5173
5174
Set the parent of a child and return the child.
5175
5176
Parameter Description
5177
1 $child Child
5178
2 $parent Parent
5179
5180
=head2 indexOfChildInParent($child)
5181
5182
Get the index of a child within the specified parent.
5183
5184
Parameter Description
5185
1 $child Child
5186
5187
=head2 parentsOrdered($tree, $preorder, $reverse)
5188
5189
The set of all parents in the tree, i.e. each non leaf of the tree, i.e the interior of the tree in the specified order.
5190
5191
Parameter Description
5192
1 $tree Tree
5193
2 $preorder Pre-order if true else post-order
5194
3 $reverse Reversed if true
5195
5196
=head2 printTree($tree, $print, $preorder, $reverse)
5197
5198
String representation as a horizontal tree.
5199
5200
Parameter Description
5201
1 $tree Tree
5202
2 $print Optional print method
5203
3 $preorder Pre-order
5204
4 $reverse Reverse
5205
5206
5207
=head1 Index
5208
5209
5210
1 L - Return the first child if it is above the second child else return B.
5211
5212
2 L - Locate the active scope in a tree.
5213
5214
3 L - Return the first child if it occurs strictly after the second child in the tree or else B if the first child is L, L or L the second child.
5215
5216
4 L - Return the first child if it occurs strictly before the second child in the tree or else B if the first child is L, L or L the second child.
5217
5218
5 L - Return the first child if it is below the second child else return B.
5219
5220
6 L - Bracketed string representation of a tree.
5221
5222
7 L - Traverse a tree in post-order to process each child with the specified sub and return an array of the results of processing each child.
5223
5224
8 L - Close the current scope returning to the previous scope.
5225
5226
9 L - Get the context of the current child.
5227
5228
10 L - Cut out a child and all its content and children, return it ready for reinsertion else where.
5229
5230
11 L - Duplicate a specified parent and all its descendants returning the root of the resulting tree.
5231
5232
12 L - Return the specified parent if it has no children else B
5233
5234
13 L - Get the first child under the specified parent.
5235
5236
14 L - Return the first most descendant child in the tree starting at this parent or else return B if this parent has no children.
5237
5238
15 L - Create a tree from a string of letters returning the children created in alphabetic order - useful for testing.
5239
5240
16 L - Return the child at the end of the path starting at the specified parent.
5241
5242
17 L - Include the specified tree in the currently open scope.
5243
5244
18 L - Get the index of a child within the specified parent.
5245
5246
19 L - Return the specified child if that child is first under its parent, else return B.
5247
5248
20 L - Return the specified child if that child is last under its parent, else return B.
5249
5250
21 L - Return the specified parent if that parent is the top most parent in the tree.
5251
5252
22 L - Get the last child under the specified parent.
5253
5254
23 L - Return the last most descendant child in the tree starting at this parent or else return B if this parent has no children.
5255
5256
24 L - The set of all children without further children, i.
5257
5258
25 L - Return the path from the specified child to the specified ancestor else return B if the child is not a descendant of the ancestor.
5259
5260
26 L - Unwrap the children of the specified parent with the whose L fields L that of their parent.
5261
5262
27 L - Merge the following sibling of the specified child if that sibling exists and the L data of the two siblings L.
5263
5264
28 L - Merge the preceding sibling of the specified child if that sibling exists and the L data of the two siblings L.
5265
5266
29 L - Find the most recent common ancestor of the specified children.
5267
5268
30 L - Create a new child optionally recording the specified key or value.
5269
5270
31 L - Get the next sibling following the specified child.
5271
5272
32 L - Return the next child with no children, i.
5273
5274
33 L - Return a list of children visited between the specified child and the next child in post-order.
5275
5276
34 L - Return a list of children visited between the specified child and the next child in pre-order.
5277
5278
35 L - Add a child and make it the currently active scope into which new children will be added.
5279
5280
36 L - The set of all parents in the tree, i.
5281
5282
37 L - The set of all parents in the tree, i.
5283
5284
38 L - The set of all parents in the tree, i.
5285
5286
39 L - The set of all parents in the tree, i.
5287
5288
40 L - The set of all parents in the tree, i.
5289
5290
41 L - The set of all parents in the tree, i.
5291
5292
42 L - Return the list of zero based child indexes for the path from the root of the tree containing the specified child to the specified child for use by the L method.
5293
5294
43 L - Return the list of zero based child indexes for the path from the specified ancestor to the specified child for use by the L method else confess if the ancestor is not, in fact, an ancestor.
5295
5296
44 L - Get the previous sibling of the specified child.
5297
5298
45 L - Return the previous child with no children, i.
5299
5300
46 L - Return a list of children visited between the specified child and the previous child in post-order.
5301
5302
47 L - Return a list of children visited between the specified child and the previous child in pre-order.
5303
5304
48 L - Print tree in normal pre-order.
5305
5306
49 L - Print tree in normal post-order.
5307
5308
50 L - Print tree in normal pre-order.
5309
5310
51 L - Print tree in reverse post-order
5311
5312
52 L - Print tree in reverse pre-order
5313
5314
53 L - String representation as a horizontal tree.
5315
5316
54 L - Place a new child first under the specified parent and return the child.
5317
5318
55 L - Place a new child last under the specified parent and return the child.
5319
5320
56 L - Place a new child after the specified child.
5321
5322
57 L - Place a new child before the specified child.
5323
5324
58 L - Select matching children in a tree in post-order.
5325
5326
59 L - Set the parent of a child and return the child.
5327
5328
60 L - Return a list of siblings after the specified child.
5329
5330
61 L - Return a list of siblings before the specified child.
5331
5332
62 L - Return a list of the siblings strictly between two children of the same parent else return B.
5333
5334
63 L - Add one child in the current scope.
5335
5336
64 L - Return the only child of this parent if the parent has an only child, else B
5337
5338
65 L - Make the specified parent a grandparent of each of its children by interposing a copy of the specified parent between the specified parent and each of its children.
5339
5340
66 L - Make the first child of the specified parent the parents previous sibling and return the parent.
5341
5342
67 L - Make the previous sibling of the specified parent the parents first child and return the parent.
5343
5344
68 L - Make the next sibling of the specified parent the parents last child and return the parent.
5345
5346
69 L - Make the last child of the specified parent the parents next sibling and return the parent.
5347
5348
70 L - Return the top most parent in the tree containing the specified child.
5349
5350
71 L - Duplicate a specified parent and all its descendants recording the mapping in a temporary {transcribed} field in the tree being transcribed.
5351
5352
72 L - Unwrap the specified child and return that child.
5353
5354
73 L - Wrap the specified child with a new parent and return the new parent optionally setting its L and L.
5355
5356
74 L - Wrap the children of the specified parent with a new intermediate parent that becomes the child of the specified parent, optionally setting the L and the L for the new parent.
5357
5358
75 L - Print a tree as as xml.
5359
5360
=head1 Installation
5361
5362
This module is written in 100% Pure Perl and, thus, it is easy to read,
5363
comprehend, use, modify and install via B:
5364
5365
sudo cpan install Tree::Ops
5366
5367
=head1 Author
5368
5369
L
5370
5371
L
5372
5373
=head1 Copyright
5374
5375
Copyright (c) 2016-2019 Philip R Brenan.
5376
5377
This module is free software. It may be used, redistributed and/or modified
5378
under the same terms as Perl itself.
5379
5380
=cut
5381
5382
5383
5384
# Tests and documentation
5385
5386
sub test
5387
1
1
0
9
{my $p = __PACKAGE__;
5388
1
11
binmode($_, ":utf8") for *STDOUT, *STDERR;
5389
1
50
81
return if eval "eof(${p}::DATA)";
5390
1
60
my $s = eval "join('', <${p}::DATA>)";
5391
1
50
10
$@ and die $@;
5392
1
1
8
eval $s;
1
1
3
1
1
39
1
1
5
1
3
1
32
1
5
1
3
1
19
1
869
1
70709
1
10
1
97
5393
1
50
12
$@ and die $@;
5394
1
170
1
5395
}
5396
5397
test unless caller;
5398
5399
1;
5400
# podDocumentation
5401
__DATA__