Implement a tree iterator - ruby

I created a very simple node class with a name and an array of nodes. I also created an iterator class with a next method that helps me iterate on each node and child nodes. I need to write the next method, but I don't what is the best way to do it.
class Node
def initialize(name, nodes
#name = name
#nodes = nodes
end
end
class Iterator
def initialize(node)
#node = node
end
def next
???
end
end
Example:
z = Node.new("z", [])
b = Node.new("b", [z])
c = Node.new("c", [])
parent = Node.new("a", [b, c])
iterator = Iterator.new(parent)
str = ''
next = iterator.next
while next do
str += next.name
next = iterator.next
end
str should equal "abzc"
Can anybody help me with this?

If I may suggest a more idiomatic approach:
class Node
attr_accessor :name, :children
def initialize(name, children = [ ])
#name = name
#children = children
end
def traverse(&block)
yield self
#children.each { |child| child.traverse(&block) }
end
end
z = Node.new("z")
b = Node.new("b", [z])
c = Node.new("c")
parent = Node.new("a", [b, c])
str = ''
parent.traverse { |node| str += node.name }
puts str
This has a benefit over btilly's solution (which is also correct) in that it doesn't proliferate Iterator objects and suck up memory- in fact Iterator disappears from the implementation (while still retaining the ability to DO something to each node in succession). This is more idiomatic; more Ruby-esque.

In your iterator, if node has any children, next would be the first of them. If it doesn't, then you need to "back up" to the last sibling that you skipped over. This implies that you need to keep track of the siblings that have been skipped over, so that you can go back to them.

Here is some running code that demonstrates what I think you're looking for.
class Node
attr_accessor :name, :nodes
def initialize(name, nodes)
#name = name
#nodes = nodes
end
end
class Iterator
def initialize(node)
#node = node
end
def each_node
yield #node
for node in #node.nodes do
iterator = Iterator.new(node)
iterator.each_node {|next_node|
yield next_node
}
end
end
end
z = Node.new("z", [])
b = Node.new("b", [z])
c = Node.new("c", [])
parent = Node.new("a", [b, c])
iterator = Iterator.new(parent)
str = ''
iterator.each_node {|node|
str += node.name
}
puts str

I have been able to solve my problem by doing the following. But what I don't like with this approach is that I traverse the nodes during the initialization instead of in the next method...
class Iterator
def initialize(node)
#node = node
#index = -1
#list = []
traverse(#node)
end
def next
#index += 1
#list[#index]
end
private
def traverse(root)
#list[#list.size] = root
if root.nodes
for n in root.nodes do
traverse(n)
end
end
end
end

Related

Ruby define a numeric index in an enumerable class without inheriting from Array or Hash and print it out

I have the following class:
class AEnumerableObject
include Enumerable
def initialize
#original_list = []
end
def [] index
#original_list[index]
end
def []= index, value
#original_list[index] = value
end
def each &block
#original_list.each_with_index do |item, i|
yield item, i
end if block_given?
end
end
If I run inline commands
$ object = AEnumerableObject.new
$ object[0] = 1
$ object[1] = 2
$ object[2] = 3
$ p object
should show
[1, 2, 3]
but it actually shows
#<AEnumerableObject:... #original_list=[1, 2, 3]>
while if I run
$ p object.class
it shows
=> AEnumerableObject
How do I implement an Array like print method?
UPDATED
I implemented the methods #[], #[]= and #each (so now it’s iterable). But how to show it like an Array in line command?
Your problem is that you have not implemented the inspect method. At the moment, the default inspect definition is used in your class. You may want to define it like:
class AEnumerableObject
def inspect
#original_list.inspect
end
end
As you can see on the left column (with all the defined methods in the module) of the documentation, the Enumerate module does not define the methods #[] and #[]=, which must be provided by the class.
Also, the class that includes Enumerate must provide an #each method.
class Test
include Enumerate
def [](i)
# do your stuff
end
def []=(i, v)
# do your stuff
end
def each
# required!
end
end
Implementing the required methods does not create any (let's say) "relation" with the Array class. The only way to create such a "relation" is through inheritance.
Let's make a silly example with some sort of linked list just to see how it works the idea...It is not robust nor well implemented... It is only an idea:
class Node
include Enumerable
attr_reader :data
def initialize(payload, n = nil)
#node = n if n.is_a? Node
#data = payload
end
def [](n)
if n == 0
self
else
#node[n - 1]
end
end
def []=(n, v)
if n == 0
#data = v
else
#node[n - 1] = v
end
end
def each(&block)
block.call(self)
#node.each(&block) if #node
end
def <=>(o)
#data <=> o.data
end
def to_s
"[" + self.map { |e| "#{e.data}" }.join(", ") + "]"
end
end
a = Node.new("Node 0")
b = Node.new("Node 1", a)
c = Node.new("Node 2", b)
d = Node.new("Node 3", c)
d[2] = "Edited"
d.each do |n|
puts n.data
end
# Let's use an enumerable method
s = d.select { |n| n.data == "Node 0" }[0]
puts "#{a.inspect} = #{s.inspect}"
puts a, b, c, d
# => Output:
#
# Node 3
# Node 2
# Edited
# Node 0
# #<Node:0x0000562e121e2440 #data="Node 0"> = #<Node:0x0000562e121e2440 #data="Node 0">
# [Node 0]
# [Edited, Node 0]
# [Node 2, Edited, Node 0]
# [Node 3, Node 2, Edited, Node 0]
As alternatives, you may consider:
# 1. Inheritance
def Test < Array
# do your stuff
end
# 2. Reopen the Array class
def Array
# do your stuff
end
The methods #[] and #[]= for the Array class are defined in the C source of the interpreter.

Linked List ruby, the Node and LInkedlist class doesnt work properly

I have some issues with this code, so, I'm trying to make a linked list but with the first variable I get the next issue:
nodo.rb:34 in 'initialize': wrong number of arguments(1 for 0)
So, the Node class have the actual node and the link, and LinkedList the size and the header.
The problem comes when I try to add a new value but I receive the issue. So I dont know how to fix this problem. I will receive any help you could give me.
class Node
def intialize(data,ref = nil)
#data = data
#refe = refe
end
def get_data
return #data
end
def set_data(newdata)
#dato = newdata
end
def get_ref
return #ref
end
def set_ref(newref)
#ref = newref
end
end
class Linkedlist
def initialize
#size = 0
#header = nil
end
def add_var(value)
#aize = #size + 1
if #header == nil
#header = Node.new(value) #the issue comes here, in the moment when I try to make a new class of Node
else
nodeActual = #header
while nodeActual.get_ref != nil
nodeActual = nodeActual.get_ref
end
nodeActual.set_ref(Node.new(value))
end
end
#def print_list
#end
def get_size
return #size
end
end
list = Linkedlist.new
stop = nil
while stop != -1
a = gets.chomp
if a.to_i == -1
stop = -1
else
list.add_var(a)
end
end
#list.print_list
you have a typo in Node class, rename intialize to initialize (missing i)

inserting in linked lists with ruby

Hi I'm trying to insert a value at a specific index in a linked list in ruby.
Here is my code thus far:
class Node
attr_accessor :data, :pointer, :next
def initialize(data, pointer = nil)
#data = data
#pointer = pointer
end
def next
#data = #pointer
end
end
class LinkedList
attr_accessor :head, :data, :pointer
def initialize(data)
#head = Node.new(data, pointer)
end
def index_at(value_of_index)
current = head
value_of_index.times do
if current.pointer == nil
current = Node.new(nil, nil)
return current = current.data
else
current = current.next
end
end
current.data
end
def insert_at_index(index, value)
current = head
index.times do
current.next
end
current = Node.new(value)
end
end
The problem I've having is with the def insert_at_index method... I can't seem to figure out how to place the new node at the index and value. Any help you can give me would be greatly appreciated.
Say your linked list looks like this:
a -> b -> d -> e
To insert c into the 3rd index, you would move to the second index to get b, create a new node, set b's next to the new node c, and set c's next to the old third index item, d.
This will give you:
a -> b -> c -> d -> e
That said, the code should look like this:
def insert_at_index(index, value)
current = head
# make current b. You may want to put this in a function node_at_index
(index - 1).times do
raise "List not long enough" if current.nil?
current = current.next
end
new_node = Node.new(value) # new node c
new_node.next = current.next # c's next is b's next, d
current.next = new_node # b's next is c
end
From the looks of it there seem to be other issues with your code as well. You're overwriting #data in your next function (you probably meant to just return #pointer), LinkedList#initialize doesn't have pointer defined, etc
You could simplify your Node to look like this:
class Node
attr_accessor :data, :next
def initialize(data, next=nil)
#data = data
#next = next
end
end
Which should work for you.
Here is one solution that worked for me:
class Node
attr_accessor :data, :pointer
alias_method :next, :pointer
def initialize(data, pointer = nil)
#data = data
#pointer = pointer
end
def next
#pointer
end
end
class LinkedList
attr_accessor :head, :data, :pointer
def initialize(data)
#head = Node.new(data, pointer)
end
def index_at(value_of_index)
current = head
value_of_index.times do
if current.pointer == nil
current = Node.new(nil, nil)
return current = current.data
else
current = current.next
end
end
current.data
end
def insert_at_index(index, value)
current = head
(index - 1).times do
if current.pointer != nil
current = current.next
end
end
new_node = Node.new(value)
if current.pointer != nil
new_node.pointer = current.pointer
end
current.pointer = new_node
end
end

Recursion and linked list

I want to see the representation of the linked list. I created a class for creating a linked list:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
def print_values(list_node)
print "#{list_node.value} --> "
if list_node.next_node.nil?
print "nil"
return
else
print_values(list_node.next_node)
end
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
print_values(node3)
end
I get:
practice.rb:24:in `<class:LinkedListNode>': undefined method `print_values' for LinkedListNode:Class (NoMethodError)
Is recursion a method calling itself? Is it possible to have a method call another method to replace recursion? Or is calling another method not allowed?
Is recursion a method calling upon itself?
Yes, generally. For specifics, examples, and exceptions see Wikipedia.
You may be able to understand recursion in your own code if you simplify your method.
The code that you wrote is more akin to procedural code, whereas Ruby typically uses object oriented code that is more akin to this:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
# This is how to write a typical Ruby recursive method.
# Notice the method does something with this object's value,
# then transfers control to the next object's same method name.
def to_s
"#{value} --> " + (next_node ? next_node.to_s : "nil")
end
end
# This code is moved out of your class definition.
# This fixes one of the errors in your original code.
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
print node3.to_s
Is it possible to have a method call on another method to replace recursion?
Yes, generally. For example in your code, you could call a method that uses iteration instead of recursion, something like this example.
Recursion:
def to_s
"#{value} --> " + (next_node ? next_node.to_s : "nil")
end
Iteration:
def to_s
s = ""
node = self
while node != nil
s += "#{value} --> "
node = next_node
end
s += "nil"
end
An algorithm that is implemented using iteration often runs much faster than a similar algorithm that is implemented using recursion.
Some kinds of recursion (including your code here) are much more efficient than other kinds of recursion. Your code is a good example of code that can use efficient tail call optimization.
Your print_values method is an instance method, though it does not use the instance in it.
I believe you meant to use the instance data for your implementation:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
def print_values
print "#{value} --> "
if next_node.nil?
print "nil"
return
else
next_node.print_values
end
end
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
node3.print_values
Another option is to make the method a static method, which does not need an instance, which will look like:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
def self.print_values(list_node)
print "#{list_node.value} --> "
if list_node.next_node.nil?
print "nil"
return
else
print_values(list_node.next_node)
end
end
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
LinkedListNode.print_values(node3)
Both options will be considered recursive, since the method calls itself. There are no rules about recursion (calling another method is allowed), but there are things you need to be aware of, like if you don't have a good stopping condition, your method might "explode" with a stack overflow exception.
You should move the code dealing with class out of the class definition:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
def print_values(list_node)
print "#{list_node.value} --> "
if list_node.next_node.nil?
print "nil"
return
else
print_values(list_node.next_node)
end
end
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
node3.print_values(node3)
The code has an amount of other glitches, like print_values is ment to be a class method, rather than instance method, but the main problem is above. This is how I would turn the code to use class method for printing values:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node = nil)
#value = value
#next_node = next_node
end
def self.print_values list_node
print "#{list_node.value} --> "
if list_node.next_node.nil?
print "nil"
return
else
print_values(list_node.next_node)
end
end
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
LinkedListNode.print_values node3
Hope it helps.
NB The answer provided by #joelparkerhenderson is in fact a way better.

Create class inherirance hierarchy using ruby

I want to using ruby to create class inherirance hierarchy. I have grep the class name and its parent class name like this:
[[B,A], [C,A], [E,D], [F,B] ...]
[B,A] A is B's parent class, and the root class not only one, like A or D.
The elements in the array is just string, like A,B,C...
I want to create a inherirance hierarchy graph like this:
[
A=>[
B=>[F],
C
],
D=>[E]
]
the graph format is not strict, which can demonstrate the hierarchy will be OK.
I have try to using loop to recursively placed the node, but it's too low efficiency. Does anyone can help me or there is some gem to solve this?
All I want to know is the clas inherirance hierarchy, so whatever the way you solve this.
Thanks #Max and #Gabriel de Oliveira for answer my question! I have solve the problem.
It maybe ugly, but it works.
class Node
attr_accessor :name, :parent, :children
def initialize(name)
#name = name
#parent = nil
#children = Set.new
end
def <=>(other)
if other.name == self.name
return 0
end
return nil
end
def ==(other)
if other.name == self.name
return true
end
return false
end
def inspect
desc = ""
desc << "{" if #children.length > 0
desc << %("#{#name}")
if #children.count > 0
desc << ":["
children_arr = #children.to_a
children_arr.to_a.each_index do |index|
desc << ',' if index > 0
desc << children_arr[index].inspect
end
desc << "]"
end
desc << "}" if #children.length > 0
return desc
end
end
str = string_from_file(file_path)
arr = JSON.parse(str)
nodes = {}
# create nodes set
arr.each do |item|
name = item[0]
parent = item[1]
nodes[name] = Node.new(name)
nodes[parent] = Node.new(parent)
end
# bind relationship with nodes
arr.each do |item|
node = nodes[item[0]]
parent = nodes[item[1]]
if !parent.nil?
node.parent = parent
parent.children << node
end
end
# filter the root nodes
roots = []
nodes.each_value do |node|
roots << node if node.parent.nil?
end
puts roots
You can do this with metaprogramming. B = Class.new(A) creates a class B that inherits from A and Object.const_set and Object.const_get can dynamically access constants. The only tricky bit is creating the classes in the right order, but that can be solved with recursion.
# create class klass (and any necessary superclasses) with inheritance specified by tree
def create_class_from_tree(klass, tree)
# nothing to do if the class already exists
return if Object.const_defined? klass
# default superclass
parent = Object
# look for a superclass defined in the tree
if inherit = tree.find { |k, p| k == klass }
# ensure superclass exists
parent = create_class_from_tree(inherit[1], tree)
end
Object.const_set(klass, Class.new(parent))
end
# create all classes with inheritance specified by tree
def create_classes_from_tree(tree)
tree.each { |klass, _| create_class_from_tree(klass, tree) }
end
create_classes_from_tree([['B', 'A'], ['C', 'A'], ['E', 'D'], ['F', 'B']])
('A'..'F').each { |klass| puts "#{klass} < #{Object.const_get(klass).superclass}" }
# A < Object
# B < A
# C < A
# D < Object
# E < D
# F < B
To transform the input array into a nested hash you could implement something along these lines:
def process(input)
return {} if input.empty?
base_classes, rest = input.partition {|value| value.is_a? Class }
ret_val = {}
base_classes.each do |klass|
ret_val[klass], rest = rest.partition {|value| value.last == klass }
ret_val[klass] = ret_val[klass].map(&:first)
end
ret_val.each {|klass, classes| ret_val[klass] = process(classes + rest) }
end
Then given all clases are defined and your data array is sanitized (for example it doesn't contain an invalid element such as [X, Z]):
data = [A, [B, A], [C, A], D, [E, D], [F, B]]
process(data) #=> {A=>{B=>{F=>{}}, C=>{}}, D=>{E=>{}}}

Resources