rubytree 0.9.5 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/tree.rb CHANGED
@@ -9,7 +9,7 @@
9
9
  # Author:: Anupam Sengupta (anupamsg@gmail.com)
10
10
  #
11
11
 
12
- # Copyright (c) 2006-2015 Anupam Sengupta
12
+ # Copyright (c) 2006-2021 Anupam Sengupta
13
13
  #
14
14
  # All rights reserved.
15
15
  #
@@ -47,7 +47,6 @@ require 'tree/tree_deps'
47
47
  # This module also acts as the namespace for all classes in the *RubyTree*
48
48
  # package.
49
49
  module Tree
50
-
51
50
  # == TreeNode Class Description
52
51
  #
53
52
  # This class models the nodes for an *N-ary* tree data structure. The
@@ -83,6 +82,7 @@ module Tree
83
82
  # {include:file:examples/example_basic.rb}
84
83
  #
85
84
  # @author Anupam Sengupta
85
+ # noinspection RubyTooManyMethodsInspection
86
86
  class TreeNode
87
87
  include Enumerable
88
88
  include Comparable
@@ -108,11 +108,11 @@ module Tree
108
108
  # +content+ attribute for any non-unique node requirements.
109
109
  #
110
110
  # If you want to change the name, you probably want to call +rename+
111
- # instead.
111
+ # instead. Note that +name=+ is a protected method.
112
112
  #
113
113
  # @see content
114
114
  # @see rename
115
- attr_reader :name
115
+ attr_accessor :name
116
116
 
117
117
  # @!attribute [rw] content
118
118
  # Content of this node. Can be +nil+. Note that there is no
@@ -132,7 +132,7 @@ module Tree
132
132
  # @return [Tree::TreeNode] Root of the (sub)tree.
133
133
  def root
134
134
  root = self
135
- root = root.parent while !root.is_root?
135
+ root = root.parent until root.is_root?
136
136
  root
137
137
  end
138
138
 
@@ -176,8 +176,8 @@ module Tree
176
176
  return nil if is_root?
177
177
 
178
178
  parentage_array = []
179
- prev_parent = self.parent
180
- while (prev_parent)
179
+ prev_parent = parent
180
+ while prev_parent
181
181
  parentage_array << prev_parent
182
182
  prev_parent = prev_parent.parent
183
183
  end
@@ -215,18 +215,20 @@ module Tree
215
215
  #
216
216
  # @see #[]
217
217
  def initialize(name, content = nil)
218
- raise ArgumentError, "Node name HAS to be provided!" if name == nil
219
- @name, @content = name, content
218
+ raise ArgumentError, 'Node name HAS to be provided!' if name.nil?
219
+
220
+ @name = name
221
+ @content = content
220
222
 
221
- if name.kind_of?(Integer)
222
- warn StandardWarning,
223
- "Using integer as node name."\
224
- " Semantics of TreeNode[] may not be what you expect!"\
223
+ if name.is_a?(Integer)
224
+ warn StructuredWarnings::StandardWarning,
225
+ 'Using integer as node name.'\
226
+ ' Semantics of TreeNode[] may not be what you expect!'\
225
227
  " #{name} #{content}"
226
228
  end
227
229
 
228
- self.set_as_root!
229
- @children_hash = Hash.new
230
+ set_as_root!
231
+ @children_hash = {}
230
232
  @children = []
231
233
  end
232
234
 
@@ -253,27 +255,26 @@ module Tree
253
255
  # Alias for {Tree::TreeNode#detached_subtree_copy}
254
256
  #
255
257
  # @see Tree::TreeNode#detached_subtree_copy
256
- alias :dup :detached_subtree_copy
258
+ alias dup detached_subtree_copy
257
259
 
258
260
  # Returns a {marshal-dump}[http://ruby-doc.org/core-1.8.7/Marshal.html]
259
- # represention of the (sub)tree rooted at this node.
261
+ # representation of the (sub)tree rooted at this node.
260
262
  #
261
263
  def marshal_dump
262
- self.collect { |node| node.create_dump_rep }
264
+ collect { |node| node.create_dump_rep }
263
265
  end
264
266
 
265
267
  # Creates a dump representation of this node and returns the same as
266
268
  # a hash.
267
- def create_dump_rep # :nodoc:
268
- { :name => @name,
269
- :parent => (is_root? ? nil : @parent.name),
270
- :content => Marshal.dump(@content)
271
- }
269
+ def create_dump_rep # :nodoc:
270
+ { name: @name,
271
+ parent: (is_root? ? nil : @parent.name),
272
+ content: Marshal.dump(@content) }
272
273
  end
273
274
 
274
275
  protected :create_dump_rep
275
276
 
276
- # Loads a marshalled dump of a tree and returns the root node of the
277
+ # Loads a marshaled dump of a tree and returns the root node of the
277
278
  # reconstructed tree. See the
278
279
  # {Marshal}[http://ruby-doc.org/core-1.8.7/Marshal.html] class for
279
280
  # additional details.
@@ -283,20 +284,20 @@ module Tree
283
284
  # self and makes itself the root.
284
285
  #
285
286
  def marshal_load(dumped_tree_array)
286
- nodes = { }
287
+ nodes = {}
287
288
  dumped_tree_array.each do |node_hash|
288
289
  name = node_hash[:name]
289
290
  parent_name = node_hash[:parent]
290
291
  content = Marshal.load(node_hash[:content])
291
292
 
292
- if parent_name then
293
+ if parent_name
293
294
  nodes[name] = current_node = Tree::TreeNode.new(name, content)
294
295
  nodes[parent_name].add current_node
295
296
  else
296
297
  # This is the root node, hence initialize self.
297
298
  initialize(name, content)
298
299
 
299
- nodes[name] = self # Add self to the list of nodes
300
+ nodes[name] = self # Add self to the list of nodes
300
301
  end
301
302
  end
302
303
  end
@@ -308,11 +309,7 @@ module Tree
308
309
  #
309
310
  # @return [String] A string representation of the node.
310
311
  def to_s
311
- "Node Name: #{@name}" +
312
- " Content: " + (@content.to_s || "<Empty>") +
313
- " Parent: " + (is_root?() ? "<None>" : @parent.name.to_s) +
314
- " Children: #{@children.length}" +
315
- " Total Nodes: #{size()}"
312
+ "Node Name: #{@name} Content: #{@content.to_s || '<Empty>'} Parent: #{is_root? ? '<None>' : @parent.name.to_s} Children: #{@children.length} Total Nodes: #{size}"
316
313
  end
317
314
 
318
315
  # @!group Structure Modification
@@ -354,7 +351,7 @@ module Tree
354
351
  #
355
352
  # -children.size..children.size
356
353
  #
357
- # This is to prevent +nil+ nodes being created as children if a non-existant
354
+ # This is to prevent +nil+ nodes being created as children if a non-existent
358
355
  # position is used.
359
356
  #
360
357
  # If the new node being added has an existing parent node, then it will be
@@ -380,38 +377,37 @@ module Tree
380
377
  # @see #<<
381
378
  def add(child, at_index = -1)
382
379
  # Only handles the immediate child scenario
383
- raise ArgumentError,
384
- "Attempting to add a nil node" unless child
385
- raise ArgumentError,
386
- "Attempting add node to itself" if self.equal?(child)
387
- raise ArgumentError,
388
- "Attempting add root as a child" if child.equal?(root)
389
-
390
- # Lazy mans unique test, won't test if children of child are unique in
380
+ raise ArgumentError, 'Attempting to add a nil node' unless child
381
+
382
+ raise ArgumentError, 'Attempting add node to itself' if equal?(child)
383
+
384
+ raise ArgumentError, 'Attempting add root as a child' if child.equal?(root)
385
+
386
+ # Lazy man's unique test, won't test if children of child are unique in
391
387
  # this tree too.
392
388
  raise "Child #{child.name} already added!"\
393
389
  if @children_hash.include?(child.name)
394
390
 
395
- child.parent.remove! child if child.parent # Detach from the old parent
391
+ child.parent&.remove! child # Detach from the old parent
396
392
 
397
393
  if insertion_range.include?(at_index)
398
394
  @children.insert(at_index, child)
399
395
  else
400
- raise "Attempting to insert a child at a non-existent location"\
396
+ raise 'Attempting to insert a child at a non-existent location'\
401
397
  " (#{at_index}) "\
402
- "when only positions from "\
398
+ 'when only positions from '\
403
399
  "#{insertion_range.min} to #{insertion_range.max} exist."
404
400
  end
405
401
 
406
- @children_hash[child.name] = child
402
+ @children_hash[child.name] = child
407
403
  child.parent = self
408
- return child
404
+ child
409
405
  end
410
406
 
411
407
  # Return a range of valid insertion positions. Used in the #add method.
412
408
  def insertion_range
413
409
  max = @children.size
414
- min = -(max+1)
410
+ min = -(max + 1)
415
411
  min..max
416
412
  end
417
413
 
@@ -427,7 +423,7 @@ module Tree
427
423
  old_name = @name
428
424
 
429
425
  if is_root?
430
- self.name=(new_name)
426
+ self.name = new_name
431
427
  else
432
428
  @parent.rename_child old_name, new_name
433
429
  end
@@ -444,20 +440,10 @@ module Tree
444
440
  # pass a String (Integer names may cause *surprises*)
445
441
  def rename_child(old_name, new_name)
446
442
  raise ArgumentError, "Invalid child name specified: #{old_name}"\
447
- unless @children_hash.has_key?(old_name)
443
+ unless @children_hash.key?(old_name)
448
444
 
449
445
  @children_hash[new_name] = @children_hash.delete(old_name)
450
- @children_hash[new_name].name=(new_name)
451
- end
452
-
453
- # Protected method to set the name of this node.
454
- # This method should *NOT* be invoked by client code.
455
- #
456
- # @param [Object] new_name The node Name to set.
457
- #
458
- # @return [Object] The new name.
459
- def name=(new_name)
460
- @name = new_name
446
+ @children_hash[new_name].name = new_name
461
447
  end
462
448
 
463
449
  # Replaces the specified child node with another child node on this node.
@@ -472,7 +458,7 @@ module Tree
472
458
  old_child = remove! old_child
473
459
  add new_child, child_index
474
460
 
475
- return old_child
461
+ old_child
476
462
  end
477
463
 
478
464
  # Replaces the node with another node
@@ -513,7 +499,7 @@ module Tree
513
499
  # @param [Tree::TreeNode] parent The parent node.
514
500
  #
515
501
  # @return [Tree::TreeNode] The parent node.
516
- def parent=(parent) # :nodoc:
502
+ def parent=(parent) # :nodoc:
517
503
  @parent = parent
518
504
  @node_depth = nil
519
505
  end
@@ -552,7 +538,7 @@ module Tree
552
538
  # Protected method which sets this node as a root node.
553
539
  #
554
540
  # @return +nil+.
555
- def set_as_root! # :nodoc:
541
+ def set_as_root! # :nodoc:
556
542
  self.parent = nil
557
543
  end
558
544
 
@@ -563,7 +549,7 @@ module Tree
563
549
  # The nodes become immutable after this operation. In effect, the entire tree's
564
550
  # structure and contents become _read-only_ and cannot be changed.
565
551
  def freeze_tree!
566
- each {|node| node.freeze}
552
+ each { |node| node.freeze }
567
553
  end
568
554
 
569
555
  # @!endgroup
@@ -603,16 +589,15 @@ module Tree
603
589
  #
604
590
  # @see #add
605
591
  # @see #initialize
606
- def [](name_or_index, num_as_name=false)
607
- raise ArgumentError,
608
- "Name_or_index needs to be provided!" if name_or_index == nil
592
+ def [](name_or_index, num_as_name = false)
593
+ raise ArgumentError, 'Name_or_index needs to be provided!' if name_or_index.nil?
609
594
 
610
- if name_or_index.kind_of?(Integer) and not num_as_name
595
+ if name_or_index.is_a?(Integer) && !num_as_name
611
596
  @children[name_or_index]
612
597
  else
613
- if num_as_name and not name_or_index.kind_of?(Integer)
614
- warn StandardWarning,
615
- "Redundant use of the `num_as_name` flag for non-integer node name"
598
+ if num_as_name && !name_or_index.is_a?(Integer)
599
+ warn StructuredWarnings::StandardWarning,
600
+ 'Redundant use of the `num_as_name` flag for non-integer node name'
616
601
  end
617
602
  @children_hash[name_or_index]
618
603
  end
@@ -624,6 +609,7 @@ module Tree
624
609
  # The traversal is *depth-first* and from *left-to-right* in pre-ordered
625
610
  # sequence.
626
611
  #
612
+ # @param [Object] block
627
613
  # @yieldparam node [Tree::TreeNode] Each node.
628
614
  #
629
615
  # @see #preordered_each
@@ -631,22 +617,22 @@ module Tree
631
617
  #
632
618
  # @return [Tree::TreeNode] this node, if a block if given
633
619
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
634
- def each(&block) # :yields: node
635
-
636
- return self.to_enum unless block_given?
620
+ # noinspection RubyUnusedLocalVariable
621
+ def each # :yields: node
622
+ return to_enum unless block_given?
637
623
 
638
- node_stack = [self] # Start with this node
624
+ node_stack = [self] # Start with this node
639
625
 
640
626
  until node_stack.empty?
641
- current = node_stack.shift # Pop the top-most node
642
- if current # Might be 'nil' (esp. for binary trees)
643
- yield current # and process it
644
- # Stack children of the current node at top of the stack
645
- node_stack = current.children.concat(node_stack)
646
- end
627
+ current = node_stack.shift # Pop the top-most node
628
+ next unless current # Might be 'nil' (esp. for binary trees)
629
+
630
+ yield current # and process it
631
+ # Stack children of the current node at top of the stack
632
+ node_stack = current.children.concat(node_stack)
647
633
  end
648
634
 
649
- return self if block_given?
635
+ self if block_given?
650
636
  end
651
637
 
652
638
  # Traverses the (sub)tree rooted at this node in pre-ordered sequence.
@@ -659,48 +645,51 @@ module Tree
659
645
  #
660
646
  # @return [Tree::TreeNode] this node, if a block if given
661
647
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
662
- def preordered_each(&block) # :yields: node
648
+ def preordered_each(&block) # :yields: node
663
649
  each(&block)
664
650
  end
665
651
 
666
652
  # Traverses the (sub)tree rooted at this node in post-ordered sequence.
667
653
  #
654
+ # @param [Object] block
668
655
  # @yieldparam node [Tree::TreeNode] Each node.
669
656
  #
670
657
  # @see #preordered_each
671
658
  # @see #breadth_each
672
659
  # @return [Tree::TreeNode] this node, if a block if given
673
660
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
674
- def postordered_each(&block)
675
- return self.to_enum(:postordered_each) unless block_given?
661
+ # noinspection RubyUnusedLocalVariable
662
+ def postordered_each
663
+ return to_enum(:postordered_each) unless block_given?
676
664
 
677
665
  # Using a marked node in order to skip adding the children of nodes that
678
666
  # have already been visited. This allows the stack depth to be controlled,
679
667
  # and also allows stateful backtracking.
680
- markednode = Struct.new(:node, :visited)
681
- node_stack = [markednode.new(self, false)] # Start with self
668
+ marked_node = Struct.new(:node, :visited)
669
+ node_stack = [marked_node.new(self, false)] # Start with self
682
670
 
683
671
  until node_stack.empty?
684
672
  peek_node = node_stack[0]
685
- if peek_node.node.has_children? and not peek_node.visited
673
+ if peek_node.node.has_children? && !peek_node.visited
686
674
  peek_node.visited = true
687
675
  # Add the children to the stack. Use the marking structure.
688
676
  marked_children =
689
- peek_node.node.children.map {|node| markednode.new(node, false)}
677
+ peek_node.node.children.map { |node| marked_node.new(node, false) }
690
678
  node_stack = marked_children.concat(node_stack)
691
679
  next
692
680
  else
693
- yield node_stack.shift.node # Pop and yield the current node
681
+ yield node_stack.shift.node # Pop and yield the current node
694
682
  end
695
683
  end
696
684
 
697
- return self if block_given?
685
+ self if block_given?
698
686
  end
699
687
 
700
688
  # Performs breadth-first traversal of the (sub)tree rooted at this node. The
701
689
  # traversal at a given level is from *left-to-right*. this node itself is
702
690
  # the first node to be traversed.
703
691
  #
692
+ # @param [Object] block
704
693
  # @yieldparam node [Tree::TreeNode] Each node.
705
694
  #
706
695
  # @see #preordered_each
@@ -708,10 +697,11 @@ module Tree
708
697
  #
709
698
  # @return [Tree::TreeNode] this node, if a block if given
710
699
  # @return [Enumerator] an enumerator on this tree, if a block is *not* given
711
- def breadth_each(&block)
712
- return self.to_enum(:breadth_each) unless block_given?
700
+ # noinspection RubyUnusedLocalVariable
701
+ def breadth_each
702
+ return to_enum(:breadth_each) unless block_given?
713
703
 
714
- node_queue = [self] # Create a queue with self as the initial entry
704
+ node_queue = [self] # Create a queue with self as the initial entry
715
705
 
716
706
  # Use a queue to do breadth traversal
717
707
  until node_queue.empty?
@@ -721,7 +711,7 @@ module Tree
721
711
  node_to_traverse.children { |child| node_queue.push child }
722
712
  end
723
713
 
724
- return self if block_given?
714
+ self if block_given?
725
715
  end
726
716
 
727
717
  # An array of all the immediate children of this node. The child
@@ -736,12 +726,12 @@ module Tree
736
726
  #
737
727
  # @return [Array<Tree::TreeNode>] An array of the child nodes, if no block
738
728
  # is given.
739
- def children
729
+ def children(&block)
740
730
  if block_given?
741
- @children.each {|child| yield child}
742
- return self
731
+ @children.each(&block)
732
+ self
743
733
  else
744
- return @children.clone
734
+ @children.clone
745
735
  end
746
736
  end
747
737
 
@@ -751,6 +741,7 @@ module Tree
751
741
  # May yield this node as well if this is a leaf node.
752
742
  # Leaf traversal is *depth-first* and *left-to-right*.
753
743
  #
744
+ # @param [Object] block
754
745
  # @yieldparam node [Tree::TreeNode] Each leaf node.
755
746
  #
756
747
  # @see #each
@@ -758,12 +749,35 @@ module Tree
758
749
  #
759
750
  # @return [Tree::TreeNode] this node, if a block if given
760
751
  # @return [Array<Tree::TreeNode>] An array of the leaf nodes
761
- def each_leaf &block
752
+ # noinspection RubyUnusedLocalVariable
753
+ def each_leaf
762
754
  if block_given?
763
- self.each { |node| yield(node) if node.is_leaf? }
764
- return self
755
+ each { |node| yield(node) if node.is_leaf? }
756
+ self
765
757
  else
766
- self.select { |node| node.is_leaf?}
758
+ self.select { |node| node.is_leaf? }
759
+ end
760
+ end
761
+
762
+ # Yields every level of the (sub)tree rooted at this node to the
763
+ # specified block.
764
+ #
765
+ # Will yield this node as well since it is considered the first level.
766
+ #
767
+ # @yieldparam level [Array<Tree::TreeNode>] All nodes in the level
768
+ #
769
+ # @return [Tree::TreeNode] this node, if a block if given
770
+ # @return [Enumerator] an enumerator on this tree, if a block is *not* given
771
+ def each_level
772
+ if block_given?
773
+ level = [self]
774
+ until level.empty?
775
+ yield level
776
+ level = level.map(&:children).flatten
777
+ end
778
+ self
779
+ else
780
+ each
767
781
  end
768
782
  end
769
783
 
@@ -776,7 +790,7 @@ module Tree
776
790
  #
777
791
  # @return [Tree::TreeNode] The first child, or +nil+ if none is present.
778
792
  def first_child
779
- children.first
793
+ @children.first
780
794
  end
781
795
 
782
796
  # Last child of this node.
@@ -784,7 +798,7 @@ module Tree
784
798
  #
785
799
  # @return [Tree::TreeNode] The last child, or +nil+ if none is present.
786
800
  def last_child
787
- children.last
801
+ @children.last
788
802
  end
789
803
 
790
804
  # @!group Navigating the Sibling Nodes
@@ -858,12 +872,14 @@ module Tree
858
872
  def siblings
859
873
  if block_given?
860
874
  parent.children.each { |sibling| yield sibling if sibling != self }
861
- return self
875
+ self
862
876
  else
863
877
  return [] if is_root?
878
+
864
879
  siblings = []
865
- parent.children {|my_sibling|
866
- siblings << my_sibling if my_sibling != self}
880
+ parent.children do |my_sibling|
881
+ siblings << my_sibling if my_sibling != self
882
+ end
867
883
  siblings
868
884
  end
869
885
  end
@@ -892,8 +908,8 @@ module Tree
892
908
  def next_sibling
893
909
  return nil if is_root?
894
910
 
895
- myidx = parent.children.index(self)
896
- parent.children.at(myidx + 1) if myidx
911
+ idx = parent.children.index(self)
912
+ parent.children.at(idx + 1) if idx
897
913
  end
898
914
 
899
915
  # Previous sibling of this node.
@@ -909,23 +925,25 @@ module Tree
909
925
  def previous_sibling
910
926
  return nil if is_root?
911
927
 
912
- myidx = parent.children.index(self)
913
- parent.children.at(myidx - 1) if myidx && myidx > 0
928
+ idx = parent.children.index(self)
929
+ parent.children.at(idx - 1) if idx && idx > 0
914
930
  end
915
931
 
916
932
  # @!endgroup
917
933
 
918
- # Provides a comparision operation for the nodes.
934
+ # Provides a comparison operation for the nodes.
919
935
  #
920
- # Comparision is based on the natural ordering of the node name objects.
936
+ # Comparison is based on the natural ordering of the node name objects.
921
937
  #
922
938
  # @param [Tree::TreeNode] other The other node to compare against.
923
939
  #
924
940
  # @return [Integer] +1 if this node is a 'successor', 0 if equal and -1 if
925
- # this node is a 'predecessor'.
941
+ # this node is a 'predecessor'. Returns 'nil' if the other
942
+ # object is not a 'Tree::TreeNode'.
926
943
  def <=>(other)
927
- return +1 if other == nil
928
- self.name <=> other.name
944
+ return nil if other.nil? || other.class != Tree::TreeNode
945
+
946
+ name <=> other.name
929
947
  end
930
948
 
931
949
  # Pretty prints the (sub)tree rooted at this node.
@@ -934,9 +952,10 @@ module Tree
934
952
  # @param [Integer] max_depth optional maximum depth at which the printing
935
953
  # with stop.
936
954
  # @param [Proc] block optional block to use for rendering
937
- def print_tree(level = 0, max_depth = nil,
955
+ def print_tree(level = node_depth, max_depth = nil,
938
956
  block = lambda { |node, prefix|
939
- puts "#{prefix} #{node.name}" })
957
+ puts "#{prefix} #{node.name}"
958
+ })
940
959
  prefix = ''
941
960
 
942
961
  if is_root?
@@ -954,10 +973,11 @@ module Tree
954
973
  # Exit if the max level is defined, and reached.
955
974
  return unless max_depth.nil? || level < max_depth
956
975
 
957
- children { |child|
958
- child.print_tree(level + 1,
959
- max_depth, block) if child } # Child might be 'nil'
976
+ # Child might be 'nil'
977
+ children do |child|
978
+ child&.print_tree(level + 1,
979
+ max_depth, block)
980
+ end
960
981
  end
961
-
962
982
  end
963
983
  end