@@ -127,14 +127,13 @@ class TreeNode
127127 # Root node for the (sub)tree to which this node belongs.
128128 # A root node's root is itself.
129129 #
130- # @note This walks the parent chain each call; if this is on a hot path,
131- # consider caching the result at the caller.
132- #
133130 # @return [Tree::TreeNode] Root of the (sub)tree.
134131 def root
135- root = self
136- root = root . parent until root . root?
137- root
132+ return @root_cache if @root_cache
133+
134+ root_node = self
135+ root_node = root_node . parent until root_node . root?
136+ @root_cache = root_node
138137 end
139138
140139 # @!attribute [r] root?
@@ -232,9 +231,9 @@ def initialize(name, content = nil)
232231 @name = name
233232 @content = content
234233
235- set_as_root!
236234 @children_hash = { }
237235 @children = [ ]
236+ set_as_root!
238237 end
239238
240239 # Returns a copy of this node, with its parent and children links removed.
@@ -422,6 +421,7 @@ def add(child, at_index = -1)
422421
423422 @children_hash [ child . name ] = child
424423 child . parent = self
424+ invalidate_size_cache_upwards!
425425 child
426426 end
427427
@@ -514,6 +514,7 @@ def remove!(child)
514514 @children_hash . delete ( child . name )
515515 @children . delete ( child )
516516 child . set_as_root!
517+ invalidate_size_cache_upwards!
517518 child
518519 end
519520
@@ -526,6 +527,8 @@ def remove!(child)
526527 def parent = ( parent ) # :nodoc:
527528 @parent = parent
528529 @node_depth = nil
530+ clear_root_cache!
531+ invalidate_size_cache_upwards!
529532 end
530533
531534 protected :parent= , :name=
@@ -559,6 +562,7 @@ def remove_all!
559562
560563 @children_hash . clear
561564 @children . clear
565+ invalidate_size_cache_upwards!
562566 self
563567 end
564568
@@ -571,6 +575,25 @@ def set_as_root! # :nodoc:
571575
572576 protected :set_as_root!
573577
578+ def clear_root_cache!
579+ @root_cache = nil
580+ return unless @children
581+
582+ children_array . each do |child |
583+ child &.__send__ ( :clear_root_cache! )
584+ end
585+ end
586+
587+ def invalidate_size_cache_upwards!
588+ node = self
589+ while node
590+ node . instance_variable_set ( :@node_size , nil )
591+ node = node . parent
592+ end
593+ end
594+
595+ private :clear_root_cache! , :invalidate_size_cache_upwards!
596+
574597 # Freezes all nodes in the (sub)tree rooted at this node.
575598 #
576599 # The nodes become immutable after this operation. In effect, the entire tree's
@@ -642,7 +665,7 @@ def each # :yields: node
642665
643666 yield current # and process it
644667 # Stack children of the current node at top of the stack
645- current . children . reverse_each do |child |
668+ current . send ( :children_array ) . reverse_each do |child |
646669 node_stack << child if child
647670 end
648671 end
@@ -676,25 +699,22 @@ def preordered_each(&block) # :yields: node
676699 def postordered_each
677700 return to_enum ( :postordered_each ) unless block_given?
678701
679- # Using a marked node in order to skip adding the children of nodes that
680- # have already been visited. This allows the stack depth to be controlled,
681- # and also allows stateful backtracking.
682- marked_node = Struct . new ( :node , :visited )
683- node_stack = [ marked_node . new ( self , false ) ] # Start with self
702+ node_stack = [ self ] # Start with self
703+ visited = [ false ]
684704
685705 until node_stack . empty?
686- peek_node = node_stack [ 0 ]
687- if peek_node . node . children? && !peek_node . visited
688- peek_node . visited = true
689- # Add the children to the stack. Use the marking structure.
690- marked_children =
691- peek_node . node . children . filter_map do |node |
692- marked_node . new ( node , false ) if node
693- end
694- node_stack = marked_children . concat ( node_stack )
695- next
706+ peek_node = node_stack [ -1 ]
707+ if peek_node . children? && !visited [ -1 ]
708+ visited [ -1 ] = true
709+ peek_node . send ( :children_array ) . reverse_each do |child |
710+ next unless child
711+
712+ node_stack << child
713+ visited << false
714+ end
696715 else
697- yield node_stack . shift . node # Pop and yield the current node
716+ yield node_stack . pop
717+ visited . pop
698718 end
699719 end
700720
@@ -727,7 +747,7 @@ def breadth_each
727747
728748 yield node_to_traverse
729749 # Enqueue the children from left to right.
730- node_to_traverse . children . each do |child |
750+ node_to_traverse . send ( :children_array ) . each do |child |
731751 node_queue << child if child
732752 end
733753 end
@@ -756,6 +776,12 @@ def children(&block)
756776 end
757777 end
758778
779+ def children_array
780+ @children
781+ end
782+
783+ private :children_array
784+
759785 # Yields every leaf node of the (sub)tree rooted at this node to the
760786 # specified block.
761787 #
@@ -793,7 +819,7 @@ def each_level
793819 level = [ self ]
794820 until level . empty?
795821 yield level
796- level = level . flat_map ( & :children ) . filter_map { |child | child }
822+ level = level . flat_map { | node | node . send ( :children_array ) } . filter_map { |child | child }
797823 end
798824 self
799825 else
@@ -810,15 +836,15 @@ def each_level
810836 #
811837 # @return [Tree::TreeNode] The first child, or +nil+ if none is present.
812838 def first_child
813- @children . first
839+ children_array . first
814840 end
815841
816842 # Last child of this node.
817843 # Will be +nil+ if no children are present.
818844 #
819845 # @return [Tree::TreeNode] The last child, or +nil+ if none is present.
820846 def last_child
821- @children . last
847+ children_array . last
822848 end
823849
824850 # @!group Navigating the Sibling Nodes
@@ -836,7 +862,7 @@ def last_child
836862 # @see #first_sibling?
837863 # @see #last_sibling
838864 def first_sibling
839- root? ? self : parent . children . first
865+ root? ? self : parent . send ( :children_array ) . first
840866 end
841867
842868 # Returns +true+ if this node is the first sibling at its level.
@@ -864,7 +890,7 @@ def first_sibling?
864890 # @see #last_sibling?
865891 # @see #first_sibling
866892 def last_sibling
867- root? ? self : parent . children . last
893+ root? ? self : parent . send ( :children_array ) . last
868894 end
869895
870896 # Returns +true+ if this node is the last sibling at its level.
@@ -897,7 +923,9 @@ def last_sibling?
897923 # @see #last_sibling
898924 def siblings
899925 if block_given?
900- parent . children . each do |sibling |
926+ return self if root?
927+
928+ parent . send ( :children_array ) . each do |sibling |
901929 next unless sibling
902930 next if sibling == self
903931
@@ -908,7 +936,7 @@ def siblings
908936 return [ ] if root?
909937
910938 siblings = [ ]
911- parent . children do |my_sibling |
939+ parent . send ( :children_array ) . each do |my_sibling |
912940 next unless my_sibling
913941 siblings << my_sibling if my_sibling != self
914942 end
@@ -924,7 +952,7 @@ def siblings
924952 #
925953 # @see #siblings
926954 def only_child?
927- root? ? true : parent . children . size == 1
955+ root? ? true : parent . send ( :children_array ) . count { | child | child } == 1
928956 end
929957
930958 alias is_only_child? only_child? # @todo: Aliased for eventual replacement
@@ -942,8 +970,9 @@ def only_child?
942970 def next_sibling
943971 return nil if root?
944972
945- idx = parent . children . index ( self )
946- parent . children . at ( idx + 1 ) if idx
973+ siblings = parent . send ( :children_array )
974+ idx = siblings . index ( self )
975+ siblings . at ( idx + 1 ) if idx
947976 end
948977
949978 # Previous sibling of this node.
@@ -959,8 +988,9 @@ def next_sibling
959988 def previous_sibling
960989 return nil if root?
961990
962- idx = parent . children . index ( self )
963- parent . children . at ( idx - 1 ) if idx &.positive?
991+ siblings = parent . send ( :children_array )
992+ idx = siblings . index ( self )
993+ siblings . at ( idx - 1 ) if idx &.positive?
964994 end
965995
966996 # @!endgroup
0 commit comments