Implementing a LinkedList in Ruby - ruby

I'm trying to implement a singly linked LinkedList in Ruby and I'm having some trouble trying to figure out why some of the nodes are disappearing. Here's what I have so far:
class Node
attr_accessor :data, :next
def initialize(data)
#data = data
end
end
class LinkedList
attr_accessor :head
def insert(data)
node = Node.new(data)
if #head.nil?
#head = node
else
travel = #head
unless travel.next.nil?
travel = travel.next
end
travel.next = node
end
print #head
end
def to_string
result = ""
travel = #head
unless travel.nil?
result << "#{travel.data} => "
travel = travel.next
end
result << "END"
result
end
end
And here is a call to this class:
list = LinkedList.new
list.insert(5)
list.insert(6)
list.insert(7)
At the end of insert, I print out #head and I can see that all three nodes are in the list. However, when I make a separate call to to_string, #head only has the first node but everything else is gone. Can anyone point me in the right direction to what's wrong?
Thanks!

There is a problem with the keyword unless. In ruby, it is a conditional statement, just like if. It is not a loop. Just replace them with the keyword until.
About the linked list in general, the point of it is to make insertion in O(1), whereas you are doing it in O(n) (traverse the list and insert the node at the end). Instead, you can just insert the new node at the beginning, it will be ok.
Finally, ruby's convention is to name the to_string method to_s, so it will be called when printing the list.
Also, you have the possibility of making Node an internal class of LinkedList. It will be useful if you want to implement other node-based data structures (deque, ring, tree, etc)

Related

Currently failing to create a class which recursively forms a tree of instances from a hash

This is a problem from a textbook: to take a previously demonstrated tree-generating program and make it generate a tree of instances with names determined by a hash. Code below. There are a number of things I don't understand. First, what is the purpose of the first line? I see that it "defines an instance variable, an accessor, and a setter," but what does that mean? Second, why are we passing around a block for 'puts node.node_name' rather than just putting that in the 'visit' method? Third, that line is the only time that 'node' is referred to in this entire program. It doesn't appear to be defined. So what is it? And finally, what am I doing wrong here? How can I make this work?
-Thanks.
class Day4thing3
attr_accessor :children, :node_name
def initialize (name, children = {})
#children = children
#node_name = name
children.each do
|a,b|
a = Day4thing3.new(a, b)
end
end
def visit_all (&block)
visit &block
children.each_key {|c| c.visit_all &block}
end
def visit(&block)
block.call self
end
children = {'grandpa'=>{'dad'=>{'child1'=>{},'child2'=>{}},'uncle'=>{'cousin1'=>{},'cousin2'=>{}}}}
family = Day4thing3.new("family", children)
family.visit_all {|node| puts node.node_name}
end
output:
family
C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:16:in block in visit_all': undefined methodvisit_all' for "grandpa":String (NoMethodError)
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:16:in each_key'
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:16:invisit_all'
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:36:in <class:Day4thing3>'
from C:/Users/Aidan/Documents/Apps and App Files/Eclipse/eclipse/workspace/Day 4/day4thing3.rb:1:in'
First, what is the purpose of the first line? I see that it "defines an instance variable, an accessor, and a setter," but what does that mean?
attr_accessor :children
is a shortcut, equivalent to
def children
#children
end
def children=(value)
#children = value
end
#children is an instance variable, i.e. a variable that each instance of a class can set individually (John has his children, Jane has her children...). A getter is a method that gets the value of an instance variable. A setter is a method that sets it. After this, you can write
p node.children # uses the getter
node.children = [] # uses the setter
Second, why are we passing around a block for 'puts node.node_name' rather than just putting that in the 'visit' method?
Because that makes visit generic. You can visit all nodes and print them, you can visit all nodes and sum them, etc etc. If you hardcode a single purpose into visit, then you would need to code a new function every time you think of a new thing you would like to do to all nodes.
Third, that line is the only time that 'node' is referred to in this entire program. It doesn't appear to be defined. So what is it?
A block is like a function: it has input parameters and a return value. node happens to be the input parameter of the block { |node| puts node.node_name }. So, its value will be whatever the yield or call passes to the block; in this case, the block is invoked by block.call self, so node inside the block is the value of self inside the #visit method.
And finally, what am I doing wrong here?
Before that... let's talk about naming. Day4thing3 is a horrible class name. By all means, save it in a file called day4thing3.rb if you want; if you want to namespace it, put it into a module named Day4Thing3; but please name the class Node, or Tree, or something meaningful. Because this makes no sense:
A Day4thing3 assumes its children are also a Day4thing3.
but this does:
A Node assumes its children are also a Node.
Anyway - you try to change the hash keys into Node objects, but a is just a parameter - assigning to it will not change the key in the hash. You could use Hash#transform_keys for that. However, you are not using values at all after the constructor, so changing #children into an Array would make more sense.
So when you visit_all, you visit the current node, then visit_all the child nodes. This works on your "family" Node: you print the node name ("family") and then try to visit all the children keys. However, the key is "grandpa", which is a String and not a Node (remember, a = Node.new(a, b) did not change the hash, only the local variable a), so the function fails.
A final point, regarding style: method names should not be separated from the parentheses by a space. It works for definition, it breaks for invocation; using "no space" rule makes it consistent.
So, with all that:
module Day4Thing3
class Node
attr_accessor :children, :node_name
def initialize(name, children={})
#node_name = name
#children = children.map do |child_name, child_children|
Node.new(child_name, child_children)
end
end
def visit_all(&block)
visit &block
children.each { |child| child.visit_all &block }
end
def visit(&block)
block.call self
end
end
children = {'grandpa'=>{'dad'=>{'child1'=>{},'child2'=>{}},'uncle'=>{'cousin1'=>{},'cousin2'=>{}}}}
family = Node.new("family", children)
family.visit_all {|node| puts node.node_name}
end

Ruby, is this Binary Tree Correct?

So im going through the OdinProject and starting on the "BinaryTree" portion. I've written a Binary Tree but only in C++ using pointers. So without pointers it's a little more confusing to me at how the nodes connect:
class Node
attr_accessor :value, :parent, :left, :right
##nodes = 0
def self.nodes
##nodes
end
def initialize(value, parent=nil)
#value = value
#parent = parent
#left = nil
#right = nil
##nodes += 1
end
end
class BinaryTree
def initialize
#root = nil
end
def build_tree(arr)
arr.each {|el| add_child(el,#root)}
#root
end
def add_child(value,node)
if #root.nil?
#root = Node.new(value)
else
if value < node.value
node.left.nil? ? node.left = Node.new(value,node) : add_child(value,node.left)
elsif value > node.value
node.right.nil? ? node.right = Node.new(value,node) : add_child(value,node.right)
end
end
return node
end
end
I've done some basic print functions, and it SEEMS to be working correctly, but I was wondering if this looks correct to others that understand ruby better. (Sidenote: I realize this does not handle duplicate numbers). Am I missing something with the "building" portion of the tree.
The reason im concerned is a see a lot of different "build" implementations. One that starts at the midpoint for instance, or is this just for "Sorted" arrays?
welcome to the ruby world! Beware you might really like it and never come back to another language :)
Here are some comments I can give you:
Testing
First thing when you wonder if your code is correct is to test it with different inputs. You can do it by hand, in the console, or you can write tests that will follow you during the development.
By printing
A quick way of testing what you do is to define the to_s method on your Node class. For instance, this will allow to check your tree is ordered:
class Node
…
def to_s
"#{left} #{value} #{right}".strip
end
end
to_s is the default ruby method used when converting an object to a string. You can for instance do:
#> puts BinaryTree.new.build_tree([5, 9, 1, 6])
1 5 6 9
By testing
For your peace of mind, you can describe some tests that will allow you to write code, and modify it, and check it is still working. I would suggest minitest for doing it easily.
require 'minitest/autorun'
describe BinaryTree do
before do
#constructor = BinaryTree.new
end
describe 'when given an array' do
it 'sorts it' do
#constructor.build_tree([1, 3, 2]).to_s.must_equal '1 2 3'
end
end
describe 'when given an array with duplicates' do
it 'builds the correct tree' do
#constructor.build_tree([1, 3, 2, 3]).to_s.must_equal '1 2 3 3'
end
end
end
You can then run it with ruby -Ilib:test binary_tree.rb if binary_tree.rb is the file where you put your code.
You will see there, as you mentioned, that the duplicate code doesn’t work.
You can make it work by removing a condition in the if…elsif block.
Then you wan work on the code and run the test as often as you like so that you are confident you didn’t break anything.
Each time you have an edge case you are not sure your code handles, just put that in a test.
Counting the nodes
Your Node.nodes method is probably not what you want if it is supposed to count the number of nodes in your binary tree.
It should be in the instance of a Node, not in the class itself: if you have several trees, all nodes are taken into account for each tree.
Ruby things
Some of the things you write could be expressed differently in ruby:
Nil is not necessary
attr_accessor parent is syntactic sugar for
def parent
#parent
end
def parent=(other)
#parent = other
end
In ruby if your instance variable #parent is not declared, it has the value nil by default, and calling node.parent will return nil too.
You don’t need these 2 lines in the initializer:
#left = nil
#right = nil
Return is not necessary
When your last instruction in a method is a return, you don’t need the return keyword. That is what I did with the to_s method above.
Named parameters
I think it is cleaner to have named parameters when they are not obvious.
For instance, calling Node.new(value, node) doesn’t really help understanding what are these 2 parameters for. I get that a node should have a value, but what is this second parameter?
You could define:
def initialize(value, parent: nil)
# Same as before
end
and call it with Node.new(value, parent: node), or Node.new(value)
OO things
Moving methods where they belong
Your BinaryTree#add_child method should be on a node. You are adding a child to the node. Move it there, so you don’t need your second parameter:
add_child(value, node.right) becomes node.right.add_child(value)
class Node
...
def add_child(other_value)
if other_value < value
left.nil? ? self.left = Node.new(other_value, parent: self) : left.add_child(other_value)
else
right.nil? ? self.right = Node.new(other_value, parent: self) : right.add_child(other_value)
end
end
end
class BinaryTree
...
def add_child(value)
if #root.nil?
#root = Node.new(value)
else
#root.add_child(value)
end
#root
end
end
While you are there you could also get rid of build_tree in the BinaryTree class, and move it to the Node.
I hope it will help you in your Ruby Journey.

reversing a linked list in ruby using a stack

I am implementing a reversed linked list using a stack.
Class Stack has methods push and pop, which turn a linked list into a stack from which the last element can be extracted in order to reverse the order.
I've encountered a similar situation here, and I'm trying to add a method to that base case. I'm trying to implement a reverse_list method that makes use of the Stack class's push and pop in order to change the pointer and reverse the linked list.
This is what I tried:
class LinkedListNode
attr_accessor :value, :next_node
def initialize(value, next_node=nil)
#value = value
#next_node = next_node
end
end
def print_values(list_node)
if list_node
print "#{list_node.value} --> "
print_values(list_node.next_node)
else
print "nil\n"
return
end
end
class Stack
attr_reader :data
def initialize
#data = nil
end
def push(value)
#data = LinkedListNode.new(value, #data)
end
def pop
return nil if #data.nil?
returning_value = #data.value
#data = #data.next_element
returning_value
end
end
def reverse_list(list)
stack = Stack.new.push(list.value)
list = list.next_node
while list
stack.push(list.value)
list = list.next_node
end
stack.pop
end
node1 = LinkedListNode.new(37)
node2 = LinkedListNode.new(99, node1)
node3 = LinkedListNode.new(12, node2)
revlist = reverse_list(node3)
print_values(revlist)
# should return 37 --> 99 --> 12 --> nil
I get errors when calling the Stack class in the reverse_list method (undefined method push for <Context::LinkedListNode:0x00000001c5e0a8>).
I'm at a loss as to why I cannot use Stack, push, and pop inside reverse_list. Any tips as to how I could go about implementing reverse_list will be well received.
The following pushes the elements into the stack, and then reverses them:
def reverse_list(list)
stack = Stack.new
while list
stack.push(list.value)
list = list.next_node
end
LinkedListNode.new(stack.pop, stack.data)
end
stack = Stack.new.push(list.value)
In your example, variable stack is an instance of LinkedListNode as push method is returning instance of linked list. So no method error is throwing. You have to create instance of Stack first like stack = Stack.new which woulb be an empty stack and then do push and pop operation using this instance.
Your pop Method within your Stack class is calling for "next_element", but I think you should be referring to the "next_node" variable from the LinkedListNode class. I would re-write your "pop" method this way:
def pop
if #data.nil?
nil
else
returning_value = #data.value
#data = #data.next_node
returning_value
end
end
I would also rewrite the method to reverse the linked list like this:
def reversed_linked_list(list)
stack = Stack.new
while list
stack.push(list.value)
list = list.next_node
end
LinkedListNode.new(stack.pop, stack.data)
end
Iterate through the list adding its contents to a stack until the end of the list. After that, the following algorithm can be applied.
root = stack.pop() # last element
current = root
while not stack.empty(): # move in reverse direction
current.next = stack.pop() # next element in stack is the next node
current = current.next # continue to the next node
current.next = nil # mark end of the list

loop over parents in multi-level tree structure

I have a multi-level tree structure and i'm trying to return an array of ancestors for an object which can have 1-3 ancestors. I have a working method, however it's complex and I would prefer to use a loop, anyone know how I can using ruby?
def ancestors
#a = []
#a.push(parent) if parent.present?
#a.push(#a.last.parent) if #a.last.parent.present?
#a.push(#a.last.parent) if #a.last.parent.present?
return #a
end
Assuming I understand your classes right.. I was thinking something like this
def ancestors
(parent.present? ? [parent, parent.ancestors] :[]).flatten
end
If Parent is present, it returns an array consisting of parent and its ancestors. The flatten is required because each level adds a array layer.
Off topic. return is considered bad style in ruby, and unless you need it, there is no need for this list to be a member variable.
This is a job for recursion.
You need to make a function which calls itself.
Something like this....
def ancestors
if self.parent.present?
ancestors << self.parent.ancestors
else
return self
end
end
It is fairly simple to do this with an iteration as well, you could try
def ancestors
#a = []
anc=parent
while anc.present? do
#a.push anc
anc=anc.parent
end
return #a
end
(not tried as I do not have your data structure)

Ruby nested hash fetching dynamically

I have an issue trying to update a nested Hash using a key.
The nested hash I have is like this:
main_hash = {
"Energy"=>
{"name"=>"Energy", "uri"=>"energy",
"children"=>
{"Renewable Energy"=>{"name"=>"Renewable Energy", "uri"=>"energy/renewable_energy"}}}
,
"Farming"=>
{"name"=>"Farming", "uri"=>"farming",
"children"=>
{"Organic Gas"=>
{"name"=>"Organic Gas", "uri"=>"farming/organic_gas"
"children" =>
{"Gas Oil"=>{"name"=>"Gas Oil", "uri"=>"farming/organic_gas/gas_oil"}}
}}}}
What I would like to do is to update an item from the hash (e.g., I want to add another child to "Organic Gas"). I know I can do this:
main_hash["Farming"]["children"]["Organic Gas"]["children"].merge!(another_hash)
The problem is I need to get that dynamically as it can get quite deeply nested.
So to get to the desired level, I would do this (which does work as above).
main_hash.send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")
It would really be great if I could call "send" method dynamically like below (obviously it won't work).
main_hash.send(:try, 'send(:fetch, "Farming").send(:fetch, "children").send(:fetch, "Organic Gas").send(:fetch, "children")')
I hope it makes it clear what I want to achieve. I have gone through all Ruby Hash's built in function and I can't get the one suited for my need.
Any help would be greatly appreciated.
Cheers.
I'm not sure a Hash is really the best data structure here. You're trying to use it to represent a tree, which is fine and all, but it might be a bit clearer if you just explicitly made it a tree:
class Tree
attr_reader :name, :uri, :children, :parent
def initialize(name, uri, *children)
#children = children
#name, #uri = name, uri
end
def <<(child)
#children << child
end
def find(name)
each_branch.detect {|branch| branch.name == name }
end
def leaf?
#children.empty?
end
# The parameter `i` just decides whether or not to include self.
def each_branch( i=true, &blk )
enum = Enumerator.new do |y|
y.yield self if i
#children.each do |c|
next unless c.is_a? Tree
y.yield c
c.each_branch( false ).each {|b| y.yield b }
end
end
block_given? ? enum.each( &blk ) : enum
end
# This yields each leaf and its parent.
def each_leaf( parent=self, &blk )
enum = Enumerator.new do |y|
#children.each do |c|
if !c.leaf?
c.each_leaf( c ).each do |l,p|
y.yield l, p
end
else y.yield c, parent
end
end
end
block_given? ? enum.each( &blk ) : enum
end
end
(I just borrowed those enumerators from a tree structure I'd made before - the each_leaf method might be helpful too, and you can check for the class not being Tree instead of leaf? returning true if you had a tree structure that could contain other objects, like strings).
Then you could do:
root_tree = Tree.new "Farming", "farming"
root_tree << Tree.new( "Organic Gas", "organic_gas" )
gas = root_tree.find "Organic gas"
gas << Tree.new(...)
I think this is just a case of finding the right data structure for the job. Even if the tree method were a bit less efficient, it's clearer what's going on, and will probably lead to fewer bugs in your dynamic code down the track.
If the problem is that you don't want the original tree to be modified, only copied, then just redefine:
class Tree
attr_accessor :children
def <<(child)
new_tree = self.dup
new_tree.children = #children + [child]
new_tree
end
end
That way, you retain the original tree but return a new tree with the extra child added.
To answer the original question, you can use the XKeys gem to traverse a dynamic path as follows:
require 'xkeys' # on rubygems.org
main_hash.extend XKeys::Hash
path = ['Farming', 'children', 'Organic Gas', 'children']
main_hash[*path].merge!(another_hash)
I would also recommend using :children instead of 'children' to avoid having huge numbers of duplicate strings.

Resources