Is this use of Ruby wrong for a binary search tree? - ruby

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.

Related

Ruby - Using nil parameters to support different types of an class

In Ruby, say I have a class called Song. I would like to know how correcty to write the initialize so that it supports 2 different types of. For example:
song_full = Song.new(fromSomeCloudStorage)
song_preview = Song.new(fromLocalStorage)
Then say I have a Song class, where I always want to assign the #time_stamp, but then depending on whether there is cloud_storage or not, assign #cloud_store_spec
def initialize(cloud_storage = nil, time_stamp = nil, local_storage = nil)
#time_stamp = time_stamp || (Time.now.to_f * 1000).to_i.to_s
#cloud_store_spec = cloud_storage
end
I'm thinking of using nil as I have done, however would the code know which is cloud_storage and which is local_storage. Is it actually possible?
Any help appreciated?
First off, use keyword arguments to allow you to pass the relevant data to the initializer (up to you if you want to include time_stamp as a keyword or regular arg):
def initialize(cloud_storage: nil, local_storage: nil, time_stamp: nil)
#time_stamp = time_stamp || (Time.now.to_f * 1000).to_i.to_s
#cloud_store_spec = cloud_storage
end
This will make things a lot clearer when calling the class, allowing:
song_full = Song.new(cloud_storage: fromSomeCloudStorage)
song_preview = Song.new(local_storage: fromLocalStorage)
In terms of how your code will know whether the code is from cloud or local, if you mean the class's instances, you can just check for the presence of #cloud_store_spec, something like:
def cloud_storage?
#cloud_store_spec.present?
end
Then, from anywhere else in your code, you can call:
song_full = Song.new(cloud_storage: fromSomeCloudStorage)
song_full.cloud_storage? # => true
song_preview = Song.new(local_storage: fromLocalStorage)
song_preview.cloud_storage? # => false
Hope that helps and I'm reading you right :) Let me know how you get on or if you've any questions.
Update for Ruby 1.9.3
As keyword arguments were introduced in Ruby 2.0, for 1.9.3 you can use an options hash:
def initialize(options = {}) # again, timestamp can be a separate arg if you'd prefer
#time_stamp = options[:time_stamp] || (Time.now.to_f * 1000).to_i.to_s
#cloud_store_spec = options[:cloud_storage]
end
The rest of the code will remain the same.
If CloudStorage or LocalStorage can be considered as classes:
class LocalStorage
def initialize; p 'LocalStorage class initialized'; end
end
class CloudStorage
def initialize; p 'CloudStorage class initialized'; end
def play; p "...playing song"; end
end
Maybe you could consider to hard code a Hash, (let apart time_stamp):
class Song
attr_reader :store_spec
def initialize(kind = nil)
storage_kind = {cloud: CloudStorage, preview: LocalStorage}
#store_spec = storage_kind[kind].new
end
def storage_kind
#store_spec.class
end
def play
#store_spec.play
end
end
So you can call for example:
song = Song.new(:cloud) #=> "CloudStorage class initialized"
song.storage_kind #=> CloudStorage
song.play #=> "...playing song"

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.

Implementing a LinkedList in 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)

Ruby wrong number of arguments in class instantitation

I am trying to make a battleship game in Ruby, but when I try to create an instance of the game board, I get "wrong number of arguments, 0 for 1". I dont see where I am going wrong as the initialize definition clearly accepts arguments.
class Board
attr_reader :grid, :default_grid
def intitalize(grid = self.class.default_grid, random = false)
#grid = grid
make_random_board if random
end
def self.default_grid
grid = Array.new(10){Array.new(10)}
end
def count
grid.flatten.count{|x| x == :s}
end
def self.random
self.new(self.default_grid, true)
end
def empty?(position = nil)
return true if position.nil?
else
false
end
def full?
grid.flatten.none?(&:nil?)
end
def place_random_ship
if full?
raise "error, board is full"
end
space = [rand(grid.length),rand(grid.length)]
until empty?(space)
space = [rand(grid.length),rand(grid.length)]
end
self[space] = :s
end
def make_random_board(count = 10)
count.times do
place_random_ship
end
end
end
emptygrid = Array.new(2){Array.new(2)}
myGame = Board.new(emptygrid)
You have a typo in your code. You should be using initialize instead of intitalize
And i believe the error you might have been getting would be ArgumentError: wrong number of arguments (1 for 0)
Which is because of your typo, the default class initialize method was used, which doesn't take in any arguments.
And something unrelated that i noticed in your code. You have defined a method named count and use variables named count. This is a code smell and i would suggest naming the method differently, because down the line, this might cause some bugs, that you might find hard to debug.

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