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
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
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.
Ok so I just started learning ruby and I'm making a Yhatzee game, now this is where I'm currently at:
class Yhatzee
def dices
#dices.to_a= [
dice1=rand(1..6),
dice2=rand(1..6),
dice3=rand(1..6),
dice4=rand(1..6),
dice5=rand(1..6)
]
end
def roll_dice
#dices.to_a.each do |dice|
puts dice
end
end
end
x = Yhatzee.new
puts x.roll_dice
Now the reason i typed .to_a after the array is i kept getting a "uninitialized variable #dices" error, and that seemed to fix it, i have no idea why.
anyways on to my question, i currently don't get any errors but my program still won't print anything to the screen. I expected it to print out the value of each dice in the array... any idea what I'm doing wrong? It seems to work when i do it in a procedural style without using classes or methods so i assumed it might work if i made the 'dices' method public. But no luck.
There are a few issues here. Firstly #dices is nil because it is not set anywhere. Thus when you call #dices.to_a you will get []. Also the dices method will not work either because nil does not have a to_a= method and the local variables you are assigning in the array will be ignored.
It seems a little reading is in order but I would do something like the following: (Not the whole game just refactor of your code)
class Yhatzee
def dice
#dice = Array.new(5){rand(1..6)}
end
def roll_dice
puts dice
end
end
x = Yhatzee.new
puts x.roll_dice
There are alot of additional considerations that need to be made here but this should at least get you started. Small Example of how I would recommend expanding your logic: (I did not handle many scenarios here so don't copy paste. Just wanted to give you a more in depth look)
require 'forwardable'
module Yahtzee
module Display
def show_with_index(arr)
print arr.each_index.to_a
print "\n"
print arr
end
end
class Roll
include Display
extend Forwardable
def_delegator :#dice, :values_at
attr_reader :dice
def initialize(dice=5)
#dice = Array.new(dice){rand(1..6)}
end
def show
show_with_index(#dice)
end
end
class Turn
class << self
def start
t = Turn.new
t.show
t
end
end
attr_reader :rolls
include Display
def initialize
#roll = Roll.new
#rolls = 1
#kept = []
end
def show
#roll.show
end
def roll_again
if available_rolls_and_dice
#rolls += 1
#roll = Roll.new(5-#kept.count)
puts "Hand => #{#kept.inspect}"
show
else
puts "No Rolls left" if #rolls == 3
puts "Remove a Die to keep rolling" if #kept.count == 5
show_hand
end
end
def keep(*indices)
#kept += #roll.values_at(*indices)
end
def show_hand
show_with_index(#kept)
end
def remove(*indices)
indices.each do |idx|
#kept.delete_at(idx)
end
show_hand
end
private
def available_rolls_and_dice
#rolls < 3 && #kept.count < 5
end
end
end
The main problem with this code is that you are trying to use the #dices instance variable inside of the roll_dice method, however you are not defining the instance variable anywhere (anywhere that is being used). You have created the dices method but you are not actually instantiating it anywhere. I have outlined a fix below:
class Yhatzee
def initialize
create_dices
end
def roll_dice
#dices.each do |dice|
puts dice
end
end
private
def create_dices
#dices = Array.new(5){rand(1..6)}
end
end
x = Yhatzee.new
x.roll_dice
I have done some simple refactoring:
Created an initialize method, which creates the #dice instance variable on the class initialization.
Made the 'dices' method more descriptive and changed the method visibility to private so only the class itself is able to create the #dice.
Cleaned up the creation of the dices inside of the #dice instance variable
I have omitted the .to_a from the roll_dice method, now that we create the variable from within the class and we know that it is an array and it will be unless we explicitly redefine it.
UPDATE
Although I cleaned up the implementation of the class, it was kindly pointed out by #engineersmnky that I oversaw that the roll would return the same results each time I called the roll_dice function, I have therefore written two functions which will achieve this, one that defines an instance variable for later use and one that literally just returns the results.
class Yhatzee
def roll_dice
#dice = Array.new(5){rand(1..6)} # You will have access to this in other methods defined on the class
#dice.each {|dice| puts dice }
end
def roll_dice_two
Array.new(5){rand(1..6)}.each {|dice| puts dice } # This will return the results but will not be stored for later use
end
end
x = Yhatzee.new
x.roll_dice
x.roll_dice # Will now return a new result
I have been tasked to create an unbeatable tic tac toe opponent for a coding challenge, and I would like to create a Ruby gem to do so, which has proper test coverage and decent Object Oriented Design (OOD).
Having never made my own gem before, and being a new student of proper OOD principles, I found a good blog post that walks through exactly what I need : http://codequizzes.wordpress.com/2013/10/25/creating-a-tic-tac-toe-game-with-ruby/
In defining the Cell class, the following code is given as an example:
module TicTacToe
class Cell
attr_accessor :value
def initialize (value = "")
#value = value
end
end
end
It seems to me, though, that given the simplicity of this initialization, we could just as easily do this:
module TicTacToe
class Cell
attr_accessor :value
def initialize
#value = ""
end
end
end
So what's the argument for doing it the first way over the second?
EDIT
Okay, I feel a bit silly now; reading the blog post a bit closer, it clearly says
The Cell class is wrapped in a TicTacToe module to follow Ruby gem conventions and prevent >class name collisions when gems are included in other projects. If Cell is initialized >without any arguments, the cell’s value will be the empty string, but Cell can also be >initialized with an argument. After a cell is instantiated, its value cannot be updated.
However, I am still confused about the last sentence, "After a cell is instantiated, its value cannot be updated."
I would think that is incorrect in this example, as to my understanding, the attr_accessor method makes value both readable and writable -- as it is writable, couldn't I update it by saying
move = Cell.new
move.value = X
First case:
def initialize (value = "")
#value = value
end
will set the #value to be empty string if no parameter is passed to initialize. If a parameter is passed to initialize, value will be set to that parameter.
Second case:
def initialize
#value = ""
end
will always set #value to be the empty string and will not accept parameters.
Example:
If we have
module TicTacToe
class Cell
attr_accessor :value
def initialize (value = "")
#value = value
end
end
end
c = TicTacToe::Cell.new("hello")
puts c.value
the code would print hello.
Using the same code above but changing the last two lines to
c = TicTacToe::Cell.new
puts c.value
the code prints nothing (well except the empty string).
Now if we change our code to the second way:
module TicTacToe
class Cell
attr_accessor :value
def initialize
#value = ""
end
end
end
c = TicTacToe::Cell.new
puts c.value
this will output the empty string again. However, this time if we try changing the last 2 lines to:
c = TicTacToe::Cell.new("hello")
puts c.value
we get an error because the initializer is not expecting an argument. Thus, we cannot instantiate it with any #value besides the empty string.
In regards to your edit:
Yes, you can still change #value. If you would like to prevent this, make it only readable by changing
attr_accessor :value
to
attr_reader :value
This site actually does a pretty good job of explaiing the pros and cons of doing this both of the ways you've specified above: http://www.rubyist.net/~slagell/ruby/objinitialization.html
For your first example, passing 'value' in as "" as an argument allows you to override this default, whereas your second example forces the '#value' variable to always be "", regardless of changing conditions.
Let me know if this helps.
just for exercise I implemented a queue class with ruby. The elements in the queue are nodes, which are implemented in another class. These nodes hold a certain value and can also hold information on their successor when added to the queue. The node class looks as follows:
class Node
attr_accessor :value, :successor
def initialize(value)
#value = value
#successor = nil
end
def set_successor(successor)
#successor = successor
end
def get_successor()
#successor
end
def get_value()
#value
end
def to_s()
#value.to_s
end
end
After creating objects, such as object1 = Node.new(12) and object2 = Node.new("Foo"), I can use the get_value method to obtain the objects values, which is exactly what the method is supposed to do.
puts object1.get_value
==> 12
puts object2.get_value
==> Foo
Yet, when just using puts object1 or puts object2, still the objects values, 12 and Foo are
displayed. Here, I would expect a more cryptic message, telling me that this is an object of the class node, which is what the objects are.
puts object1.class
==> Node
So, does anybody know a hint, why the objects value is displayed, when actually the whole object is supposed to be shown?
puts calls the to_s method on objects before printing them. You have defined to_s in your class to call to_s of the #value. Therefore, instances of your class have the same string representation as the value they hold.
If you would write
class Node
# ...
def to_s
"Node<#{#value.to_s}>"
end
end
You would get
node = Node.new(42)
puts node
# Node<42>
As a side note: your Ruby code is not very idiomatic, here are some observations:
omit the empty parentheses ()
no need to write getters and setters, that's what attr_accessor is for
instance variables are automatically set to nil
outside of the constructor, better use the getter value instead of instance var #value
malformed indentation
Here's an equivalent implementation:
class Node
attr_accessor :value, :successor
def initialize(value)
#value = value
end
def to_s
value.to_s
end
end
In action:
node = Node.new(42)
node.value = 123
succ = Node.new(124)
node.successor = succ
puts node
# 123
puts node.successor
# 124
In case you didn't want a setter for value, use attr_reader instead of attr_accessor.