Copy a class so that the old class is not modified when I modify the new one in Ruby - ruby

I have some a ConnectFour class and I have a method:
def apply_move!(column_number, symbol, row_number = 0)
# Some gravity logic
#board[row_number][column_number] = symbol
end
That modifies the class in place.
I tried writing a wrapper around it that returns a board and does not change the original, so that Function Programming techniques could be used.
My attempt is:
def apply_move(column_number, symbol)
dummy = ConnectFour.new(#board)
dummy.apply_move!(column_number, symbol)
dummy
end
But the trouble is that modifying dummy also modifies the original class itself! How could I modify dummy and dummy only?
More code and context
You probably are interested in:
class CpuWinIfPossible < Player
def decide_move(board)
(0...6).find do |move|
board.apply_move(move, self.symbol).is_won?(self.symbol)
end || (0...6).to_a.sample
end
end
Here I loop and execute apply_move to my board, as you can see from my definition above, apply_move should not change the board, but the board shows 7 (+ 1) moves after this code is run: it looks like this:
Player X: Where would you like to play (Number from 1 to 7) ?
2
O O
OXOOOO
The winner is O
The constructor
class ConnectFour
attr_accessor :board
def initialize(board)
#board = board
end

I think it is related to the current status of #board, if you change your ConnectFour#initialize to looks like following I think it should be work
def initialize(board)
#board = board.dup
end
If you are using rails you can use deep_dup
def initialize(board)
#board = board.deep_dup
end

A hack to solve my problem is:
def initialize(board)
#board = board.map(&:dup)
end
But as #JörgWMittag noted I should really make my code more OO to fit with Ruby style and this workaround will not be needed anymore.

You could make a deep copy of the class:
new_class = Marshal.load(Marshal.dump(ConnectFour))
and go from there (dummy = new_class.new(#board), etc.).

Related

Ruby class, arguments being passed around methods, instance variable?

I'm having this problem which I think it might be a code smell, I have a class that receives an argument in its initialiser and contains one public and several private methods - everything normal. Example:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
foo_1(examples)
end
private
def foo_1(examples)
foo_2(examples) ? 1 : 2
end
def foo_2(examples)
examples.size > #a
end
end
My problem here, is 'examples' that is received by the public method being carried around over and over the private methods, it doesn't look pretty and it seems like a code smell, what's the best approach here? Make it an instance variable inside the public method?
Thanks
Yes, this may be considered a code smell if the number of private methods accepting examples is bigger than 1-2.
One thing to consider would be to extract a class to represent the rule here.
For example:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
size_rule_applies?(examples) ? 1 : 2
end
private
def size_rule_applies?(examples)
SizeRule.new(#a, examples).apply?
end
class SizeRule
def initialize(a, examples)
#a = a
#examples = examples
end
def apply?
#examples.size > #a
end
end
end
I wouldn't make the examples an instance variable of the Foo class as there's a risk it would persist in memory between the calls to that object. I've seen bugs like that.
If examples changes dynamically then making it instance variable is not an option. You'll either need to instantiate Foo for every examples or you'll end up having mutable Foo where examples itself is changing, which is also not good.
Your code looks fine. The only thing that concerns me is that one method depends on another. It's usually not a big deal, but this would look better to me:
def apply?(examples)
foo_1(examples) && foo_2(examples)
end
Another option is to use block:
class Foo
def initialize(a)
#a = a
end
def apply?(examples)
foo_1 { examples }
end
private
def foo_1
v = foo_2 { yield.size }
v ? 1 : 2
end
def foo_2
yield > #a
end
end
Since the class Foo does nothing else presently than just evaluate .apply?(examples) I will advice examples be added to the initializer and made an instance variable. This is simpler, more efficient and more obvious.
class Foo
def initialize(a, examples)
#a = a
#examples = examples
end
def apply?
foo_1
end
private
def foo_1
foo_2 ? 1 : 2
end
def foo_2
#examples.size > #a
end
end

Why can't self be replaced in Ruby?

EDIT: For those criticizing my intentions with replacing self, you are free to click the back button, continue developing with your own opinions, and leave me to develop with mine :)
I was wondering if there is a way to completely remove the object that self references and replace it with a new instance.
Example:
def refresh_from_server!
self = newly_fetched_object_from_server
end
I don't want to return the new object.
It seems like I would have to build my own copying interface and call self.copy_from(other_object) but maybe someone has a cool ruby bit to share that works better!
--EDIT
Since some people seem unclear on the question, I want instance.my_method! to completely replace instance with a new instance of that class
For example lets imagine we have a class
class Counter
attr_accessor :count
def initialize
count = 0
end
def reset!
# This is what I want to achieve.
# Obviously in this case it would be trivial to write `self.count = 0`
# Or return a new value
# But I have a much more complex object in real life
# which copying would not be trivial
# All I'm looking for is a bit of stylistic sugar to make my code look cooler
# If it doesn't exist, I would love to know why
self = Counter.new
end
def up
count += 1
end
end
No, you can't replace self. You can only change some/all of its state, but the object reference will remain the same.
Why would you want to do this, anyway? If you just want to piggyback on your initialization logic (as it seems to me to be the case), some refactoring will help: just call a shared method from both places.
class Counter
attr_accessor :count
def initialize
init_state
end
def reset!
init_state
end
def up
self.count += 1
end
private
def init_state
self.count = 0
end
end
As already noted by others, self can't be replaced from enclosed instance. If replacement of instance with a new instance is required, it need to be done from outside, like in a class factory which registers its class instances.
Bellow is a simplest example using a delegator, demonstrating what I mean. SimpleDelegator represents a simple wrapper around Counter instance:
require 'delegate'
class Counter
attr_accessor :count
def initialize
#count = 0
end
end
class CounterDecorator < SimpleDelegator
def reset!
__setobj__(__getobj__.class.new)
end
end
c = CounterDecorator.new(Counter.new)
p c.__getobj__.object_id
c.count = 123
p c.count
c.reset!
p c.__getobj__.object_id
p c.count
# produces following output
20131160
123
20130900
0
Though the question is old, it is still visited. I will attempt to elaborate more on the "why" in "Why can't self be replaced in Ruby?".
usage of self in which context
https://web.archive.org/web/20191217060940/https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
There are various contexts in which self can be used. You question uses it in the context of an instance method, so I will focus on that.
E.g. this context:
class SomeClass
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
a = SomeClass.new
a.some_method
# prints : SomeClass - 47013616336320 - #<SomeClass:0x000055846bcd7b80>
Note that there are other usages of self: e.g. where it reference the Class object in scope of a class definition. E.g.
class SomeClass
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
# prints : Class - 47102719314940 - SomeClass
the intended effect of replacing self
Below code a demonstration of what you expected / wished (as I understand it):
class Counter
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
def refresh!
self = Counter.new # not possible
# results in error : "Can't change the value of self"
end
end
a = Counter.new
a.some_method
# prints : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish a to point to a different object
But what about other references? E.g. assuming you wanted:
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish both a and b to point to the same (new) object
If stated as such it gives a hint on the why not.
why we can't replace self
The short answer is that it is simply not something that the language / interpreter offers. As to the reasoning: in a way #matthewd answers that in this answer:
All ruby variable references are essentially pointers (but not
pointers-to-pointers), in C parlance.
You can mutate an object (assuming it's not immutable), and all
variables that reference it will thus be pointing at the same (now
mutated) object. But the only way to change which object a variable is
referring to is with direct assignment to that variable -- and each
variable is a separate reference; you can't alias a single reference
with two names.
In short: there may be other references to that object in variables that are not in the scope of the instance method. These cannot be manipulated by that instance method.
a way to achieve the intended effect
If you want this effect and only want to touch the code of Counter you might move all methods and state to an inner class Counter::Inner and make Counter behave like a decoupled reference. The only 'state' of Counter would be the reference to the Counter::Inner object and Counter can delegate all calls it receives to that reference in a method_missing method. In case of your refresh! you can replace the reference in Counter same as you now intent to replace self. All outside code will now use indirectly the new Counter:Inner instance.
class Counter
class Inner
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
def initialize(*args)
#reference = Inner.new(*args)
end
def method_missing(method_id, *args)
#reference.send(method_id, *args)
end
def refresh!
#reference = Inner.new
end
end
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238242100 - #<Counter::Inner:0x0000557a00203e68>
a.refresh!
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238240000 - #<Counter::Inner:0x0000557a00202e00>
Just one more answer for the archives :-) I hope this gives useful insights to future visitors.

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

Passing values to Ruby Classes

I'm making a small application in visualruby and the code looks something like this:
class Base
def initialize(x)
#var = x
#test = 0
end
def button1__clicked(*argv)
Derived.new(#var).show
end
end
class Derived < Base
def initialize(x)
super(x)
end
def show()
load_glade(__FILE__)
set_glade_all()
show_window()
end
def button1__clicked(*argv)
#do something to test
destory_window()
end
end
The idea here is that Base is a window. When I click on a button inside the Base window, a new Derived window shows up. What I want to do is change #test inside of Derived's button1_clicked function and then return that value back to Base after the Derived window is destroyed.
Don't know if this is a more visualruby related question or a basic ruby programming concept question.
You're not passing a value to a class, but a value to an instance of a class, which is just an object.
What you need to do is have a link between the one and the other so that a message can be sent back from Derived to Base.
Perhaps like this:
class Base
attr_accessible :test
def initialize(x)
#var = x
#test = 0
end
def button1__clicked(*argv)
Derived.new(#var, self).show
end
end
This passes in a reference to the Base instance in question, and also makes the test attribute editable by others.
This can then be used:
class Derived < Base
def initialize(x, base)
#base = base
super(x)
end
def button1__clicked(*argv)
#base.test = 1 # or whatever new value is required
destory_window()
end
end
Capture a reference to it in #base and later exercise that.

What is the difference between def func(var) and def func=(var)?

In a class definition, what is the difference between these two methods?
def func(var)
...
end
def func=(var)
...
end
Is there any, or is one of them not valid?
Both of them are valid method definitions. But the second one is defining a 'setter' method - you can call this method with the following syntax:
obj.func = 123
This statement will be translated into
obj.func=(123)
You can take a look at this answer where I explain this syntax in a bit more detail.
To explain some things about reader/writer AKA getter/setter methods in Ruby:
Ruby doesn't force us to use = in the method definition for a setter. We get to choose whether the method has one.
Consider this:
class Foo
# automagically creates:
# .v
# .v=
attr_accessor :v
def initialize(v)
puts "inside initialize(#{ v })"
#v = v
end
def setter(v)
puts "inside setter(#{ v })"
#v = v
end
def setter=(v)
puts "inside setter=(#{ v })"
#v = v
end
end
f = Foo.new(1)
puts f.v
f.setter(2)
puts f.v
f.setter = 3
puts f.v
f.setter=(4)
puts f.v
f.v = 5
puts f.v
f.v=(6)
puts f.v
Running the code outputs:
inside initialize(1)
1
inside setter(2)
2
inside setter=(3)
3
inside setter=(4)
4
5
6
The = is simply another letter in the method name because Ruby is smart enough to know if it sees f.setter = 3 it should use the setter=(v) method.
Ruby doesn't force using = to set a variable, you can decide if it makes more sense to you when you define the method. It is idiomatic that we use = because it helps make a setter look like an assignment, removing the urge to name all the setters something like set_v(v).
These are defining the getter and setter methods if you will. Say you have a Person class with a phone attribute.
class Person
def phone
#phone
end
def phone=(number)
#phone = number
end
end
Now you could change the phone attribute (managed internally in the #phone) by simply setting the property which will invoke the phone= method.
john = Person.new
john.phone = "123-456-7890"
It looks like a property assignment on the outside. Other characters that you can stack at the end of a method name are ? for boolean getters, ! for destructive operations. Again, these are just conventions and you're free to use these three characters as you want. However, code simply looks more natural with these symbols around. For example,
question.closed?
document.destroy!

Resources