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.
Related
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
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)
I just started working with Ruby classes and am writing a binary tree class. However, when I go to print the value of a particular node, it prints the hexadecimal memory address as opposed to the actual value. I looked around on the web quite a bit but all I see is puts and print, which are what I am trying. How do you print the actual value and not the address?
class Node
attr_accessor :value,:left,:right
def initialize(newValue)
#value = newValue
#left = nil
#right = nil
end
# In my binary tree class after a value has been inserted into the tree....
current_node = #root
puts current_node.value
When I run the output I get BinaryTree::NumericTreeItem:0x007fa101125eb8
Thank you for your time and I apologize for the trivial question. I'm sure it's a simple fix. Haven't been able to find anything else online.
You can override the to_s method in a class to control what gets printed out in situations like this. I'll borrow the example from the previous answer:
class NumericTreeItem
attr_accessor :numericValue
def initialize(newValue)
#numericValue = newValue
end
def to_s
"NumericTreeItem with value of #{#numericValue}"
end
end
Now when you do this line:
puts current_node.value
you would see something like:
NumericTreeItem with value of 5
be displayed.
The value instance variable in your Node contains an instance of a class called NumericTreeItem. You don't show what that class is in your question, but let's pretend for a moment that is is defined like so:
class NumericTreeItem
attr_accessor :numericValue
def initialize(newValue)
#numericValue = newValue
end
end
Then to print the value in the node:
puts current_node.value.numericValue
I tried to write a quick implementation for a binary search tree in Ruby, which is a data structure I commonly write when learning a new programming language.
I get stack too deep errors when I run it on my computer. I wonder if it is an issue with my code or perhaps how I'm running it?
class Node
attr_accessor :data, :left, :right
def initialize(d)
#data = d
#left = Node.new(nil)
#right = Node.new(nil)
end
end
class BST
attr_accessor :root
def initialize
#root = nil
end
def add_recursive(r, d)
if r == nil
r = Node.new(d)
else
add_recursive(r.right, d) if d > r.data
add_recursive(r.left, d) if d < r.data
end
end
def add(darg)
add_recursive(#root, darg)
end
def pr(r)
if (r.left != nil)
pr(r.left)
end
print "#{r.data}"
if (r.right != nil)
pr(r.right)
end
end
end
bb = BST.new
bb.add(100)
bb.add(0)
bb.add(-100)
bb.pr(bb.root)``
I was wondering what am I doing wrong in this implementation because I did indeed run a few simple tests and my use in accessing the data variable was giving me problems. Thanks for any help
You've got more than one problem here, but your infinite recursion is occurring with your first Node.new(nil) call in Node#initialize. Another problem is that you never update #root in BST after you initialize it to nil. In add_recursive your assignment to r has no effect on #root.
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.