How to access instance variable of other object of same class - ruby

This is a job interview problem. I'm supposed to create a data structure for a time in seconds and milliseconds, then create two Time objects, and then write a function that can return the difference between the two Times. This is my code:
class Time
def initialize (sec, milli_sec)
#sec = sec
#milli_sec = milli_sec
end
def difference(time_2)
puts #sec.to_i*1000 + #milli_sec.to_i + time_2.#sec
end
end
time_1 = Time.new('5','30')
time_2 = Time.new('6','40')
time_1.difference(time_2)
This is the error:
syntax error, unexpected tIVAR, expecting '('
I am having a problem accessing the #sec, #milli_sec variables of time_2 passed as time_1.difference(time_2). I think that the syntax is time_2.#sec.to_i or time_2.##sec.to_i, but those return errors. time_2.sec returns uninitialized time, even though it looks like it's been initialized. I would like to know the solution to this problem.

#sec and #milli_sec are instance variables of your Time class. This means that unless you do something else only the instance itself has access to them. Other parts of your code can create an instance, but are only able to access the methods you specify. The point of this is so that you can change the underlying implementation without affecting other parts of your code that are using this class.
There are a variety of ways to do this. You could define the following two methods in your Time class:
def sec
#sec
end
def milli_sec
#milli_sec
end
These methods are public (by default) so you can now do this:
t = Time.new(1, 2)
puts t.sec # prints 1
puts t.milli_sec # prints 2
A more Ruby-ish way would be to add this line to the top of your class:
attr_reader :sec, :milli_sec
Doing this accomplishes the same thing as defining the two methods above. You might also want to look at attr_accessor.
P.S. Time is a poor choice of class name for your own code as it's already defined by Ruby itself.

In ruby, every interface is a group of methods. You can't just obj.#var to access instance variables since they are not methods thus not interfaces. If you want to expose instance variables, then create public methods to access them. attr_reader and attr_accessor are simply handy ways to create those methods.

Here is my final solution:
class Moment
def initialize (sec, milli_sec)
#sec = sec
#milli_sec = milli_sec
end
def sec
#sec
end
def milli_sec
#milli_sec
end
def difference(time_2)
return ((#sec.to_i*1000 + #milli_sec.to_i) - (time_2.sec.to_i*1000 + time_2.milli_sec.to_i)).abs
end
end
time_1 = Moment.new('1','300')
time_2 = Moment.new('11','20')
time_1.difference(time_2)

The same objective can be achieved in much lesser lines of code using approach as below:-
class MyTime
attr_accessor :seconds, :milli_seconds
def initialize(entity={})
#seconds = entity['seconds']
#milli_seconds = entity['milli_seconds']
end
def difference(obj)
ms= ((#seconds.to_i*1000+#milli_seconds.to_i)-(obj.seconds.to_i*1000+obj.milli_seconds.to_i))
secs = ms/1000
msecs = ms%1000
return MyTime.new({'seconds'=> secs,
'milli_seconds'=> msecs})
end
end
time_1 = MyTime.new({'seconds'=> '20',
'milli_seconds'=> '100'})
time_2 = MyTime.new({'seconds'=> '10',`enter code here`
'milli_seconds'=> '20'})
diff_Obj = time_1.difference(time_2)
puts "Difference is : #{diff_Obj.seconds} Seconds #{diff_Obj.milli_seconds} milliseconds"

Related

Making a Yhatzee game, array won't show up on screen

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

Ruby Instance Methods and Variables

There is something that i don't understand about ruby class instance variable or methods**.
So i have this code that keeps on giving me this error and i cant understand
Looks ruby thinks that i am trying to call for Float.in_celsius but I want to make this call within my class instance.
#-----------------------------------
def ftoc(fr)
fr = fr.to_f
if (fr == 32)
c = 0
elsif (fr == 212)
c = 100
else
c = (fr-32.0)*(5.0/9.0)
end
return c
end
def ctof (cl)
cl = cl.to_f
f = (cl*(9.0/5.0))+32.0
return f
end
#-----------------------------------
class Temperature
attr_accessor :in_celsius, :in_fahrenheit
#class metods
def self.from_celsius(cel)
puts "from celsious\n"
puts "cel: #{cel}\n"
#in_fahrenheit = cel
#in_celsius = ctof(cel)
puts "==============================\n"
return #in_celsius
end
def self.in_celsius
#in_celsius
end
end
puts "==============================\n"
puts Temperature.from_celsius(50).in_celsius
puts Temperature.from_celsius(50).in_fahrenheit
and Error is
test.rb:54: in '<main>' : undefined method 'in_celsius' for 122.0:float (noMethod Error)
enter code here
You have a fundamental misunderstanding of how classes work in Ruby. Right now all of your variables and methods are defined at class level. That means that everything you do in the methods is acting directly on the class itself. Instead, you should create instances of Temperature.
class Temperature
# special method called when creating a new instance
def initialize celsius
#in_celsius = celsius
#in_fahrenheit = celsius * 9 / 5.0 + 32
end
def self.from_celsius celsius
new celsius # built in method to create an instance, passes argument to initialize
end
# we defined initialize using celsius, so here we must convert
def self.from_fahrenheit fahrenheit
new((fahrenheit - 32) * 5 / 9.0)
end
private_class_method :new # people must use from_celsius or from_fahrenheit
# make instance variables readable outside the class
attr_accessor :in_celsius, :in_fahrenheit
end
Temperature.from_celsius(50).in_celsius
This code isn't perfect (from_fahrenheit does a redundant conversion) but it should give you the idea of how to redesign your class.
from_celsius is returning a float which doesn't have an in_celsius method. You need it to return an instance of Temperature which would have that method.
Got to say your intent is a tad confusing, unless you have some other uses for the class Temperature, so it's bit hard to say which way you should go.
Let's see the code puts Temperature.from_celsius(50).in_celsius in details:
Call to singleton method ::from_celsius of Temperature class. That is ok (with some stranges), and t returns as instance of Float class because of result of #ctof method.
Call to instance method #in_celsius of object, which is returned from the previous method. Since it was the Float, the ruby interpreter searches for its instance method #in_celsius, hasn't find it out, and throws the NoMethodError exception.
How to fix.
Since you treat ::from_celsius as a constructor of Temperature class, I believe, that you shell to pass the floating value into the new method, and return created object. You will have class code as follows:
def initialize( value )
#in_fahrenheit = cel
#in_celsius = ctof(cel)
end
def self.from_celsius(cel)
puts "from celsious\n"
puts "cel: #{cel}\n"
temp = Temperature.new( cel )
puts "==============================\n"
return temp
end

Passing parent class initialized variables to an instance of a child class

(working in Ruby)
First off, I apologize in advance. I'm not a programmer by training, I've simply come to it by dint of convenience and a changing world. What I needed to do involved programming, and I got tired of waiting around for others to do my programming for me. As such, I've missed some basic concepts along the way, and when I ask these questions I sometimes make a fool of myself by consequence.
Let's say I want to define a species/job relationship in classes. I want to define a superclass "BlackAnt" and have subclasses "Worker" "Fighter" and "Queen"
To me, intuitively, this looks something like this:
class BlackAnt
#legs = 6
end
class Worker < BlackAnt
#jaws = 'small'
end
but if I then try
ant1 = Worker.new
puts ant1.legs
I get an error. If I amend class BlackAnt to:
class BlackAnt
attr_accessor :legs
#legs = 6
end
ant1.legs returns 'nil'
I've tried the method outlined here: http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
and this allows Worker.legs to return '6', but... alas:
ant1 = Worker.new
Worker.legs => '6'
ant1.legs => 'nil'
At a guess, the values of those parent variables are not being initialized each time a new child is spawned.
I feel I'm being silly about this somehow, and the reply is no doubt going to make me curse the day I discovered caffeine-driven all-nighters. What I need to do is arrange things so that I can create objects like so:
ant1 = Worker.new
ant2 = Queen.new
ant3 = Fighter.new
and have them each acquire the appropriate quantity of legs, along with whatever special characteristics were assigned in the child class. The worker/queen/fighter classifications will be bound by namespaces such that the actual calls will be:
ant1 = AntBlack::Worker.new
ant2 = AntRed::Worker.new
ant3 = AntRed::Queen.new
etc.
I'd like to then be able to check the quantity of legs of an individual ant using:
ant1.legs #=> 6
I may be going around my elbow to get to my thumb. If so, feel free to offer alternate suggestions for ways to achieve the same result, I will greatly appreciate the insights.
///updated re:response///
class AntRed
attr_accessor :legs
def initialize
#legs = 6
end
end
class Worker < AntRed
#strength = 1
end
result:
irb(main):009:0> ant1 = Worker.new
#=> #<Worker:0x87616ac #strength=1>
irb(main):010:0> ant1.legs
#=> nil
Define your BlackAnt class as follows:
class BlackAnt
attr_accessor :legs
def initialize
#legs = 6
end
end
And your Worker class as follows:
class Worker < BlackAnt
attr_accessor :strength
def initialize
super
#strength = 1
end
end
The way you currently define the BlackAnt class, you are storing legs as a class level instance variable.
At a guess, the values of those parent variables are not being
initialized each time a new child is spawned.
In Ruby instance variables belong to a single object. Look at point 7) in my answer starting
To address your "If true that they are variables"
in Why are symbols in Ruby not thought of as a type of variable?
I don't understand why you get irb(main):010:0> ant1.legs #=> nil. For me it works (at least as script).
class AntRed
attr_accessor :legs
def initialize
#legs = 6
end
end
class Worker < AntRed
end
ant1 = Worker.new
print 'ant1.legs : '; puts ant1.legs
Execution :
$ ruby -w test.rb
ant1.legs : 6
you mean you want a class's instance varieble which has been initialed!
so you can do it like this:
class BlackAnt
attr_accessor :legs
#legs = 6
def initialize
#legs = self.class.instance_variable_get(:#legs)
end
end
newObj = BlackAnt.new
newObj.legs

When are class variables actually useful?

If you look around you'll find some comparisons of class variables to class instance variables, and the best I ever hear of class variables are "that might be what you want", but I've never heard anybody say that is what they want, or when the situation of sharing a variable across an entire inheritance tree might be more useful.
So in practice, when is a class variable a better choice than a class instance variable?
Another example(you want unique name for every object)
class Foobar
##uniqueId = 0
def initialize
##uniqueId += 1
end
def unique_name
"Foobar_" + ##uniqueId.to_s
end
end
a = Foobar.new
puts a.unique_name
a = Foobar.new
puts a.unique_name
This will output
Foobar_1
Foobar_2
Edit: Singleton pattern is also good example for static variables link
The easiest example would be that you use class variable to store some overview states of that class or shared states of all instances.
class Foobar
##total = 0
def initialize
##total += 1
end
class << self
def all
##total
end
end
end
5.times { Foobar.new }
Foobar.all #=> 5
So here ##total will show you how many instances have been created as a sum.
Rails' ActiveRecord#FinderMethods#all simply returns the array's size. But if all your instances of a class are not wrapped in a array, using class variable to return "all" is also a solution.

Refactoring to no block parameters in a Ruby internal DSL

I've got a little DSL that looks like this:
ActivityLogger.log do
activity('27-06-2012') do
eat do |act|
act.duration = 15
act.priority = 5
end
end
end
I want to refactor it so it loses the block params in the innermost block, so it looks like this:
ActivityLogger.log do
activity('27-06-2012') do
eat do
duration = 15
priority = 5
end
end
end
The #eat method instantiates a Log object:
def eat(&block)
#logs << Log.new(Eat, &block)
end
Log's constructor yields self in the last line:
def initialize(activity, &block)
#activity = activity
yield self
end
To my mind, that is where the problem is. I've tried both using instance_eval in the #eat method (see link#2 below) and removing the yield statement from the Log's constructor entirely (link#3), but none of these approaches work (the Log object gets created, but doesn't get its #duration and #priority methods set).
Here are the links:
1) A working DSL with block parameters
2) Non-working DSL, first refactoring attempt
3) Non-working DSL, second refactoring attempt
Thanks!
duration = 15 doesn't call the setter method as you expect but defines a local variable duration. You have to either call the setter explicitly via self.duration = 15 or implement your setter like
def duration(value)
#duration = value
end
and call duration 15.

Resources