rubytree 0.9.5 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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