| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Forest::Tree::Pure; | 
| 2 | 16 |  |  | 16 |  | 122470 | use Moose; | 
|  | 16 |  |  |  |  | 1318574 |  | 
|  | 16 |  |  |  |  | 125 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | our $VERSION   = '0.10'; | 
| 5 |  |  |  |  |  |  | our $AUTHORITY = 'cpan:STEVAN'; | 
| 6 |  |  |  |  |  |  |  | 
| 7 | 16 |  |  | 16 |  | 139452 | use Scalar::Util 'reftype', 'refaddr'; | 
|  | 16 |  |  |  |  | 45 |  | 
|  | 16 |  |  |  |  | 1382 |  | 
| 8 | 16 |  |  | 16 |  | 103 | use List::Util   'sum', 'max'; | 
|  | 16 |  |  |  |  | 33 |  | 
|  | 16 |  |  |  |  | 53372 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | with qw(MooseX::Clone); | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | has 'node' => ( | 
| 13 |  |  |  |  |  |  | is        => 'ro', | 
| 14 |  |  |  |  |  |  | isa       => 'Item', | 
| 15 |  |  |  |  |  |  | predicate => 'has_node', | 
| 16 |  |  |  |  |  |  | ); | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | has 'uid'  => ( | 
| 19 |  |  |  |  |  |  | is      => 'rw', | 
| 20 |  |  |  |  |  |  | isa     => 'Value', | 
| 21 |  |  |  |  |  |  | lazy    => 1, | 
| 22 |  |  |  |  |  |  | default => sub { (overload::StrVal($_[0]) =~ /\((.*?)\)$/)[0] }, | 
| 23 |  |  |  |  |  |  | ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | has 'children' => ( | 
| 26 |  |  |  |  |  |  | traits    => ['Array'], | 
| 27 |  |  |  |  |  |  | is        => 'ro', | 
| 28 |  |  |  |  |  |  | isa       => 'ArrayRef[Forest::Tree::Pure]', | 
| 29 |  |  |  |  |  |  | lazy      => 1, | 
| 30 |  |  |  |  |  |  | default   => sub { [] }, | 
| 31 |  |  |  |  |  |  | handles   => { | 
| 32 |  |  |  |  |  |  | get_child_at => 'get', | 
| 33 |  |  |  |  |  |  | child_count  => 'count', | 
| 34 |  |  |  |  |  |  | }, | 
| 35 |  |  |  |  |  |  | ); | 
| 36 |  |  |  |  |  |  |  | 
| 37 |  |  |  |  |  |  | has 'size' => ( | 
| 38 |  |  |  |  |  |  | traits => [qw(NoClone)], | 
| 39 |  |  |  |  |  |  | is         => 'ro', | 
| 40 |  |  |  |  |  |  | isa        => 'Int', | 
| 41 |  |  |  |  |  |  | lazy_build => 1, | 
| 42 |  |  |  |  |  |  | ); | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | sub _build_size { | 
| 45 | 44 |  |  | 44 |  | 70 | my $self = shift; | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 44 | 100 |  |  |  | 99 | if ( $self->is_leaf ) { | 
| 48 | 18 |  |  |  |  | 1131 | return 1; | 
| 49 |  |  |  |  |  |  | } else { | 
| 50 | 26 |  |  |  |  | 47 | return 1 + sum map { $_->size } @{ $self->children }; | 
|  | 44 |  |  |  |  | 1591 |  | 
|  | 26 |  |  |  |  | 970 |  | 
| 51 |  |  |  |  |  |  | } | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | has 'height' => ( | 
| 55 |  |  |  |  |  |  | traits => [qw(NoClone)], | 
| 56 |  |  |  |  |  |  | is         => 'ro', | 
| 57 |  |  |  |  |  |  | isa        => 'Int', | 
| 58 |  |  |  |  |  |  | lazy_build => 1, | 
| 59 |  |  |  |  |  |  | ); | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub _build_height { | 
| 62 | 44 |  |  | 44 |  | 79 | my $self = shift; | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 44 | 100 |  |  |  | 116 | if ( $self->is_leaf ) { | 
| 65 | 18 |  |  |  |  | 2863 | return 0; | 
| 66 |  |  |  |  |  |  | } else { | 
| 67 | 26 |  |  |  |  | 56 | return 1 + max map { $_->height } @{ $self->children }; | 
|  | 44 |  |  |  |  | 1532 |  | 
|  | 26 |  |  |  |  | 890 |  | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  | } | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | ## informational | 
| 72 | 114 |  |  | 114 | 1 | 19191 | sub is_leaf { (shift)->child_count == 0 } | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | ## traversal | 
| 75 |  |  |  |  |  |  | sub traverse { | 
| 76 | 6 |  |  | 6 | 1 | 693 | my ($self, @args) = @_; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 6 |  |  |  |  | 12 | $_->visit(@args) for @{ $self->children }; | 
|  | 6 |  |  |  |  | 201 |  | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | sub visit { | 
| 82 | 13 |  |  | 13 | 1 | 28 | my ( $self, $f, @args ) = @_; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | $self->fmap_cont(sub { | 
| 85 | 50 |  |  | 50 |  | 157 | my ( $tree, $cont, @args ) = @_; | 
| 86 | 50 |  |  |  |  | 182 | $tree->$f(@args); | 
| 87 | 50 |  |  |  |  | 131 | $cont->(); | 
| 88 | 13 |  |  |  |  | 100 | }); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub fmap_cont { | 
| 92 | 207 |  |  | 207 | 1 | 845 | my ( $self, @args ) = @_; | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 207 | 100 |  |  |  | 985 | unshift @args, "callback" if @args % 2 == 1; | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 207 |  |  |  |  | 1457 | my %args = ( depth => 0, path => [], index_path => [], @args ); | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 207 |  |  |  |  | 396 | my $f = $args{callback}; | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 207 | 50 |  |  |  | 475 | (defined($f)) | 
| 101 |  |  |  |  |  |  | || confess "Cannot traverse without traversal function"; | 
| 102 | 207 | 50 | 33 |  |  | 1595 | (!ref($f) or reftype($f) eq "CODE") | 
| 103 |  |  |  |  |  |  | || confess "Traversal function must be a CODE reference or method name, not: $f"; | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | $self->$f( | 
| 106 |  |  |  |  |  |  | sub { | 
| 107 | 182 |  |  | 182 |  | 299 | my ( @inner_args ) = @_; | 
| 108 | 182 | 50 |  |  |  | 1373 | unshift @inner_args, "callback" if @inner_args % 2 == 1; | 
| 109 | 182 |  | 33 |  |  | 8114 | my $children = $args{children} || $self->children; | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 182 |  |  |  |  | 604 | my %child_args = ( %args, depth => $args{depth} + 1, path => [ @{ $args{path} }, $self ], parent => $self, @inner_args ); | 
|  | 182 |  |  |  |  | 1196 |  | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 182 |  |  |  |  | 319 | my @index_path  = @{ $args{index_path} }; | 
|  | 182 |  |  |  |  | 684 |  | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 182 |  |  |  |  | 397 | my $i = 0; | 
| 116 | 177 |  |  |  |  | 261 | map { | 
| 117 | 182 |  |  |  |  | 2023 | my $index = $i++; | 
| 118 | 177 |  |  |  |  | 1547 | $_->fmap_cont( | 
| 119 |  |  |  |  |  |  | %child_args, | 
| 120 |  |  |  |  |  |  | index => $index, | 
| 121 |  |  |  |  |  |  | index_path => [ @index_path, $index ], | 
| 122 |  |  |  |  |  |  | ) | 
| 123 |  |  |  |  |  |  | } @$children; | 
| 124 |  |  |  |  |  |  | }, | 
| 125 | 207 |  |  |  |  | 2006 | %args, | 
| 126 |  |  |  |  |  |  | ); | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | sub locate { | 
| 130 | 6 |  |  | 6 | 1 | 19 | my ( $self, @path ) = @_; | 
| 131 |  |  |  |  |  |  |  | 
| 132 | 6 |  |  |  |  | 30 | my @nodes = $self->descend(@path); | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 6 |  |  |  |  | 141 | return $nodes[-1]; | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | sub descend { | 
| 138 | 20 |  |  | 20 | 1 | 47 | my ( $self, @path ) = @_; | 
| 139 |  |  |  |  |  |  |  | 
| 140 | 20 | 100 |  |  |  | 41 | if ( @path ) { | 
| 141 | 14 |  |  |  |  | 27 | my ( $head, @tail ) = @path; | 
| 142 |  |  |  |  |  |  |  | 
| 143 | 14 | 50 |  |  |  | 700 | if ( my $child = $self->get_child_at($head) ) { | 
| 144 | 14 |  |  |  |  | 54 | return ( $self, $child->descend(@tail) ); | 
| 145 |  |  |  |  |  |  | } else { | 
| 146 | 0 |  |  |  |  | 0 | confess "No such child $head"; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  | } else { | 
| 149 | 6 |  |  |  |  | 34 | return $self; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub transform { | 
| 154 | 16 |  |  | 16 | 1 | 58 | my ( $self, $path, $method, @args ) = @_; | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 16 | 100 |  |  |  | 42 | if ( @$path ) { | 
| 157 | 10 |  |  |  |  | 19 | my ( $i, @path ) = @$path; | 
| 158 |  |  |  |  |  |  |  | 
| 159 | 10 |  |  |  |  | 571 | my $targ = $self->get_child_at($i); | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 10 |  |  |  |  | 44 | my $transformed = $targ->transform(\@path, $method, @args); | 
| 162 |  |  |  |  |  |  |  | 
| 163 | 10 | 100 |  |  |  | 10500 | if ( refaddr($transformed) == refaddr($targ) ) { | 
| 164 | 4 |  |  |  |  | 13 | return $self; | 
| 165 |  |  |  |  |  |  | } else { | 
| 166 | 6 |  |  |  |  | 27 | return $self->set_child_at( $i => $transformed ); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | } else { | 
| 169 | 6 |  |  |  |  | 34 | return $self->$method(@args); | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | } | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | sub set_node { | 
| 174 | 0 |  |  | 0 | 1 | 0 | my ( $self, $node ) = @_; | 
| 175 |  |  |  |  |  |  |  | 
| 176 | 0 |  |  |  |  | 0 | $self->clone( node => $node ); | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | sub replace { | 
| 180 | 1 |  |  | 1 | 1 | 3 | my ( $self, $replacement ) = @_; | 
| 181 |  |  |  |  |  |  |  | 
| 182 | 1 |  |  |  |  | 10 | return $replacement; | 
| 183 |  |  |  |  |  |  | } | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | sub add_children { | 
| 186 | 4 |  |  | 4 | 1 | 14 | my ( $self, @additional_children ) = @_; | 
| 187 |  |  |  |  |  |  |  | 
| 188 | 4 |  |  |  |  | 9 | foreach my $child ( @additional_children ) { | 
| 189 | 4 | 0 | 33 |  |  | 145 | (blessed($child) && $child->isa(ref $self)) | 
|  |  | 50 |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | || confess "Child parameter must be a " . ref($self) . " not (" . (defined $child ? $child : 'undef') . ")"; | 
| 191 |  |  |  |  |  |  | } | 
| 192 |  |  |  |  |  |  |  | 
| 193 | 4 |  |  |  |  | 11 | my @children = @{ $self->children }; | 
|  | 4 |  |  |  |  | 161 |  | 
| 194 |  |  |  |  |  |  |  | 
| 195 | 4 |  |  |  |  | 11 | push @children, @additional_children; | 
| 196 |  |  |  |  |  |  |  | 
| 197 | 4 |  |  |  |  | 31 | return $self->clone( children => \@children ); | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | sub add_child { | 
| 201 | 4 |  |  | 4 | 1 | 397 | my ( $self, $child ) = @_; | 
| 202 |  |  |  |  |  |  |  | 
| 203 | 4 |  |  |  |  | 18 | $self->add_children($child); | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | sub set_child_at { | 
| 207 | 6 |  |  | 6 | 1 | 3656 | my ( $self, $index, $child ) = @_; | 
| 208 |  |  |  |  |  |  |  | 
| 209 | 6 | 0 | 33 |  |  | 73 | (blessed($child) && $child->isa(ref $self)) | 
|  |  | 50 |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | || confess "Child parameter must be a " . ref($self) . " not (" . (defined $child ? $child : 'undef') . ")"; | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 6 |  |  |  |  | 17 | my @children = @{ $self->children }; | 
|  | 6 |  |  |  |  | 221 |  | 
| 213 |  |  |  |  |  |  |  | 
| 214 | 6 |  |  |  |  | 13 | $children[$index] = $child; | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 6 |  |  |  |  | 30 | $self->clone( children => \@children ); | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub remove_child_at { | 
| 220 | 2 |  |  | 2 | 1 | 5 | my ( $self, $index ) = @_; | 
| 221 |  |  |  |  |  |  |  | 
| 222 | 2 |  |  |  |  | 4 | my @children = @{ $self->children }; | 
|  | 2 |  |  |  |  | 129 |  | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 2 | 50 |  |  |  | 8 | confess "No child at index '$index'" if @children <= $index; | 
| 225 |  |  |  |  |  |  |  | 
| 226 | 2 |  |  |  |  | 6 | splice @children, $index, 1; | 
| 227 |  |  |  |  |  |  |  | 
| 228 | 2 |  |  |  |  | 15 | $self->clone( children => \@children ); | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | } | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | sub insert_child_at { | 
| 233 | 3 |  |  | 3 | 1 | 8 | my ( $self, $index, $child ) = @_; | 
| 234 |  |  |  |  |  |  |  | 
| 235 | 3 | 0 | 33 |  |  | 40 | (blessed($child) && $child->isa('Forest::Tree::Pure')) | 
|  |  | 50 |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | || confess "Child parameter must be a Forest::Tree::Pure not (" . (defined $child ? $child : 'undef') . ")"; | 
| 237 |  |  |  |  |  |  |  | 
| 238 | 3 |  |  |  |  | 8 | my @children = @{ $self->children }; | 
|  | 3 |  |  |  |  | 147 |  | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 3 | 50 |  |  |  | 11 | confess "'$index' is out of bounds" if @children < $index; | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 3 |  |  |  |  | 9 | splice @children, $index, 0, $child; | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 3 |  |  |  |  | 16 | $self->clone( children => \@children ); | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | sub get_child_index { | 
| 248 | 23 |  |  | 23 | 1 | 48 | my ( $self, $child ) = @_; | 
| 249 |  |  |  |  |  |  |  | 
| 250 | 23 |  |  |  |  | 35 | my $index = 0; | 
| 251 | 23 |  |  |  |  | 29 | foreach my $sibling (@{ $self->children }) { | 
|  | 23 |  |  |  |  | 768 |  | 
| 252 | 37 | 100 |  |  |  | 246 | (refaddr($sibling) eq refaddr($child)) && return $index; | 
| 253 | 14 |  |  |  |  | 20 | $index++; | 
| 254 |  |  |  |  |  |  | } | 
| 255 |  |  |  |  |  |  |  | 
| 256 | 0 |  |  |  |  |  | return; | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | sub reconstruct_with_class { | 
| 260 | 0 |  |  | 0 | 1 |  | my ( $self, $class ) = @_; | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 0 | 0 |  |  |  |  | confess "No class provided" unless defined($class); | 
| 263 |  |  |  |  |  |  |  | 
| 264 | 0 |  |  |  |  |  | return $class->new( | 
| 265 |  |  |  |  |  |  | node => $self->node, | 
| 266 |  |  |  |  |  |  | children => [ | 
| 267 | 0 |  |  |  |  |  | map { $_->reconstruct_with_class($class) } @{ $self->children }, | 
|  | 0 |  |  |  |  |  |  | 
| 268 |  |  |  |  |  |  | ], | 
| 269 |  |  |  |  |  |  | ); | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | sub to_pure_tree { | 
| 273 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 274 |  |  |  |  |  |  |  | 
| 275 | 0 |  |  |  |  |  | return $self; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 |  |  |  |  |  |  | sub to_mutable_tree { | 
| 279 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 0 |  |  |  |  |  | $self->reconstruct_with_class("Forest::Tree"); | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | __PACKAGE__->meta->make_immutable; | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 16 |  |  | 16 |  | 164 | no Moose; 1; | 
|  | 16 |  |  |  |  | 47 |  | 
|  | 16 |  |  |  |  | 152 |  | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | __END__ | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | =pod | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | =head1 NAME | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | Forest::Tree::Pure - An n-ary tree | 
| 295 |  |  |  |  |  |  |  | 
| 296 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | use Forest::Tree; | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | my $t = Forest::Tree::Pure->new( | 
| 301 |  |  |  |  |  |  | node     => 1, | 
| 302 |  |  |  |  |  |  | children => [ | 
| 303 |  |  |  |  |  |  | Forest::Tree::Pure->new( | 
| 304 |  |  |  |  |  |  | node     => 1.1, | 
| 305 |  |  |  |  |  |  | children => [ | 
| 306 |  |  |  |  |  |  | Forest::Tree::Pure->new(node => 1.1.1), | 
| 307 |  |  |  |  |  |  | Forest::Tree::Pure->new(node => 1.1.2), | 
| 308 |  |  |  |  |  |  | Forest::Tree::Pure->new(node => 1.1.3), | 
| 309 |  |  |  |  |  |  | ] | 
| 310 |  |  |  |  |  |  | ), | 
| 311 |  |  |  |  |  |  | Forest::Tree::Pure->new(node => 1.2), | 
| 312 |  |  |  |  |  |  | Forest::Tree::Pure->new( | 
| 313 |  |  |  |  |  |  | node     => 1.3, | 
| 314 |  |  |  |  |  |  | children => [ | 
| 315 |  |  |  |  |  |  | Forest::Tree::Pure->new(node => 1.3.1), | 
| 316 |  |  |  |  |  |  | Forest::Tree::Pure->new(node => 1.3.2), | 
| 317 |  |  |  |  |  |  | ] | 
| 318 |  |  |  |  |  |  | ), | 
| 319 |  |  |  |  |  |  | ] | 
| 320 |  |  |  |  |  |  | ); | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | $t->traverse(sub { | 
| 323 |  |  |  |  |  |  | my $t = shift; | 
| 324 |  |  |  |  |  |  | print(('    ' x $t->depth) . ($t->node || '\undef') . "\n"); | 
| 325 |  |  |  |  |  |  | }); | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 328 |  |  |  |  |  |  |  | 
| 329 |  |  |  |  |  |  | This module is a base class for L<Forest::Tree> providing functionality for | 
| 330 |  |  |  |  |  |  | immutable trees. | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | It can be used independently for trees that require sharing of children between | 
| 333 |  |  |  |  |  |  | parents. | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | There is no single authoritative parent (no upward links at all), and changing | 
| 336 |  |  |  |  |  |  | of data is not supported. | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | This class is appropriate when many tree roots share the same children (e.g. in | 
| 339 |  |  |  |  |  |  | a versioned tree). | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | This class is strictly a DAG, wheras L<Forest::Tree> produces a graph with back references | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | =head1 ATTRIBUTES | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | =over 4 | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | =item I<node> | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | =item I<children> | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | =over 4 | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | =item B<get_child_at ($index)> | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | Return the child at this position. (zero-base index) | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | =item B<child_count> | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | Returns the number of children this tree has | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =back | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =item I<size> | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | =over 4 | 
| 366 |  |  |  |  |  |  |  | 
| 367 |  |  |  |  |  |  | =item B<size> | 
| 368 |  |  |  |  |  |  |  | 
| 369 |  |  |  |  |  |  | =item B<has_size> | 
| 370 |  |  |  |  |  |  |  | 
| 371 |  |  |  |  |  |  | =back | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | =item I<height> | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | =over 4 | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | =item B<height> | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | =item B<has_height> | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | =back | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | =back | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | =head1 METHODS | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | =over 4 | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | =item B<is_leaf> | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | True if the current tree has no children | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | =item B<traverse (\&func)> | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | Takes a reference to a subroutine and traverses the tree applying this subroutine to | 
| 396 |  |  |  |  |  |  | every descendant. (But not the root) | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | =item B<visit (&func)> | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | Traverse the entire tree, including the root. | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | =item B<fmap_cont (&func)> | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | A CPS form of C<visit> that lets you control when and how data flows from the children. | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | It takes a callback in the form: | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | sub { | 
| 409 |  |  |  |  |  |  | my ( $tree, $cont, @args ) = @_; | 
| 410 |  |  |  |  |  |  |  | 
| 411 |  |  |  |  |  |  | ... | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | and C<$cont> is a code ref that when invoked will apply that same function to the children of C<$tree>. | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | This allows you to do things like computing the sum of all the node values in a tree, for instance: | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | use List::Util qw(sum); | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | my $sum = $tree->fmap_cont(sub { | 
| 421 |  |  |  |  |  |  | my ( $tree, $cont ) = @_; | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | return sum( $tree->node, $cont->() ); | 
| 424 |  |  |  |  |  |  | }); | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | And also allows to stop traversal at a given point. | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | =item B<add_children (@children)> | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | =item B<add_child ($child)> | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | Create a new tree node with the children appended. | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | The children must inherit C<Forest::Tree::Pure> | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | Note that this method does B<not> mutate the tree, instead it clones and | 
| 437 |  |  |  |  |  |  | returns a tree with the augmented list of children. | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | =item B<insert_child_at ($index, $child)> | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  | Insert a child at this position. (zero-base index) | 
| 442 |  |  |  |  |  |  |  | 
| 443 |  |  |  |  |  |  | Returns a derived tree with overridden children. | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | =item B<set_child_at ($index, $child)> | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | Replaces the child at C<$index> with C<$child>. | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | =item B<remove_child_at ($index)> | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | Remove the child at this position. (zero-base index) | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | Returns a derived tree with overridden children. | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | =item B<locate (@path)> | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | Find a child using a path of child indexes. These two examples return the same object: | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | $tree->get_child_at(0)->get_child_at(1)->get_child_at(0); | 
| 460 |  |  |  |  |  |  |  | 
| 461 |  |  |  |  |  |  | $tree->locate(0, 1, 0); | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | =item B<descend (@path)> | 
| 464 |  |  |  |  |  |  |  | 
| 465 |  |  |  |  |  |  | Like C<lookup> except that it returns every object in the path, not just the leaf. | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | =item C<transform (\@path, $method, @args)> | 
| 468 |  |  |  |  |  |  |  | 
| 469 |  |  |  |  |  |  | Performs a lookup on C<@path>, applies the method C<$method> with C<@args> to | 
| 470 |  |  |  |  |  |  | the located node, and clones the path to the parent returning a derived tree. | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | This method is also implemented in L<Forest::Tree> by mutating the tree in | 
| 473 |  |  |  |  |  |  | place and returning the original tree, so the same transformations should work | 
| 474 |  |  |  |  |  |  | on both pure trees and mutable ones. | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | This code: | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | my $new = $root->transform([ 1, 3 ], insert_child_at => 3, $new_child); | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | will locate the child at the path C<[ 1, 3 ]>, call C<insert_child_at> on it, | 
| 481 |  |  |  |  |  |  | creating a new version of C<[ 1, 3 ]>, and then return a cloned version of | 
| 482 |  |  |  |  |  |  | C<[ 1 ]> and the root node recursively, such that C<$new> appears to be a | 
| 483 |  |  |  |  |  |  | mutated C<$root>. | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | =item set_node $new | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | Returns a clone of the tree node with the node value changed. | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =item C<replace $arg> | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | Returns the argument. This is useful when used with C<transform>. | 
| 492 |  |  |  |  |  |  |  | 
| 493 |  |  |  |  |  |  | =item B<clone> | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | Provided by L<MooseX::Clone>. | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | Deeply clones the entire tree. | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | Subclasses should use L<MooseX::Clone> traits to specify the correct cloning | 
| 500 |  |  |  |  |  |  | behavior for additional attributes if cloning is used. | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | =item B<reconstruct_with_class $class> | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | Recursively recreates the tree by passing constructor arguments to C<$class>. | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | Does not use C<clone>. | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | =item B<to_mutable_tree> | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | Invokes C<reconstruct_with_class> with L<Forest::Tree> as the argument. | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | =item B<to_pure_tree> | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | Returns the invocant. | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | =item B<get_child_index ($child)> | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | Returns the index of C<$child> in C<children> or undef if it isn't a child of | 
| 519 |  |  |  |  |  |  | the current tree. | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | =back | 
| 522 |  |  |  |  |  |  |  | 
| 523 |  |  |  |  |  |  | =head1 BUGS | 
| 524 |  |  |  |  |  |  |  | 
| 525 |  |  |  |  |  |  | All complex software has bugs lurking in it, and this module is no | 
| 526 |  |  |  |  |  |  | exception. If you find a bug please either email me, or add the bug | 
| 527 |  |  |  |  |  |  | to cpan-RT. | 
| 528 |  |  |  |  |  |  |  | 
| 529 |  |  |  |  |  |  | =head1 AUTHOR | 
| 530 |  |  |  |  |  |  |  | 
| 531 |  |  |  |  |  |  | Yuval Kogman | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | Copyright 2008-2014 Infinity Interactive, Inc. | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | L<http://www.iinteractive.com> | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 540 |  |  |  |  |  |  | it under the same terms as Perl itself. | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | =cut |