Setting an instance variable to 'self'? - ruby

I'm creating Conway's Game of Life with two classes: Board & Cell.
Board has access to Cell, but I'm not quite sure exactly how. Can't I place cell.board = self under Cell's initialize method? Why or why not? For sake of example, here's what I think are the relevant parts.
class Board
#omitted variables & methods
def create_cell
cell = Cell.new
cell.board = self
end
end
class Cell
attr_accessor :board
def initialize
end
end
Also, what does cell.board = self do exactly?

There is an error in your code. Instead of cell = Class.new you should do cell = Cell.new.
Yes you can pass the board (self) as a parameter in the Cell's constructor (initialize). In fact is more clean and functional in that way. Check out this code:
class Board
def create_cell
cell = Cell.new(self)
end
end
class Cell
attr_accessor :board
def initialize board
#board = board
end
end
And then some examples of use.
$> b = Board.new
# => #<Board:0x000001021c0298>
$> c1 = b.create_cell
# => #<Cell:0x000001021c27a0 #board=#<Board:0x000001021c0298>>
$> c2 = b.create_cell
# => #<Cell:0x000001021d4270 #board=#<Board:0x000001021c0298>>
$> c2.board == c1.board
# => true
As you can notice, either cell.board = self or using the constructor (initialize), is setting the current board instance into the created cell. So all these cells will point to that board.

cell.board = self sets the cell's board variable to the current board object (that is the board object on which you called the create_cell method.
If you put cell.board = self into Cell's initialize method, you'd get an error that cell is not defined in that scope. If you replaced it with #board = self, you'd set the board variable to the current Cell object (i.e. the one that's being created), not to a Board object. So that wouldn't be what you want.

Yes, you can. You can assign self to anything. You can't assign something else to self, but there's nothing wrong with assigning self to any other accessable variable or passing it to a method.
You could, for example, have written
class Board
#omitted variables & methods
def create_cell
cell = Class.new(self)
end
end
class Cell
attr_accessor :board
def initialize(board)
self.board = board
end
end
Also, what does cell.board = self do exactly?
It just passes a variable to the board= accessor. It's no different from passing any other variable.

Related

In Ruby how does it work when passing "self" when initialising a class?

I've come across the following solution to a problem and am struggling to understand the attach_tail! method. How exactly does this Knot.new(self) work? In a general sense, what does it to do pass self when you create a new instance of the class?
The idea of the programme is more extensive than this and essentially is about the head of a 'knot' moving around a grid and being followed by the tail with more methods inside the class that i havent included.
class Knot
attr_reader :x, :y, :head, :tail
def initialize(head=nil)
#x = #y = 0 # Position at the start is 0,0
#head = head # Head is passed, but the default is nil
#tail = nil # Tail is nil to begin with
end
def attach_tail!
#tail = Knot.new(self) # Tail can then be added
end
def location = [x, y]
end
There is nothing special about self nor about "initializing a class" here.
new is a method like any other method, and the object referenced by self is an object like any other object.
Instead of
Knot.new(self)
It could be
Knot.new(baz)
or
foo.bar(self)
or
foo.bar(baz)
You are simply calling a method on an object and passing another object as an argument. That's it.
It might be easier to see this from the outside.
For demonstration purposes, let's briefly change attr_reader to attr_accessor in order to make all attributes settable from the outside:
class Knot
attr_accessor :x, :y, :head, :tail
# ...
end
With the above and a given knot a:
a = Knot.new
we could attach a tail to a via:
a.tail = Knot.new(a)
Knot.new(a) creates a new knot with a as its head. It then assigns that new knot to the tail of a.
Now, if you wanted to move that assignment from outside the object into the object, a would have to somehow pass itself as an argument to Knot.new. And this is exactly what self does – within instance methods, self refers to the current instance.
Outside a:
a.tail = Knot.new(a)
Inside a:
self.tail = Knot.new(self)
And without the tail= accessor, we'd assign to the instance variable directly:
#tail = Knot.new(self)
Which is exactly what attach_tail! does.

Initializing an array of instance variables

I'm making a game with a Board class Cell class. The Board class needs to be initialized with a unique instance variable for each Cell. I can hardcode it so that it works, but it seems inelegant and doesn't allow the size of the board to be chosen by the user at runtime. Here's what I have:
class Board
def initialize
#cell_1 = Cell.new(1)
#cell_2 = Cell.new(2)
#cell_3 = Cell.new(3)
#cell_4 = Cell.new(4)
#cell_5 = Cell.new(5)
#cell_6 = Cell.new(6)
#cell_7 = Cell.new(7)
#cell_8 = Cell.new(8)
#cell_9 = Cell.new(0)
#cells = [#cell_1, #cell_2, #cell_3,
#cell_4, #cell_5, #cell_6,
#cell_7, #cell_8, #cell_9]
end
end
I think I could use a loop to create a hash with unique key names pointing to unique Cell objects, but I don't know how I could make unique instance variables with a loop.
If you don't need to create each instance variables (#cell_1, #cell_2, ...), you can use Enumerable#map:
#cells = [*1..8, 0].map { |i| Cell.new(i) }
If you really need to refer every instance variable by name you can do something like this.
class Board
def initialize
#cells = (1..9).to_a.map { |i| Cell.new(i) }
end
def method_missing(method, *args, &block)
if method =~ /^cell_[1-9][0-9]*$/
index = method[/\d+/].to_i
#cells[index-1]
else
super
end
end
end
In this way you can call:
board = Board.new
board.cell_1 #=> first cell
Of course I'd use the solution proposed by #falsetru.

How to generate a random name in Ruby

I need to make a program in ruby to generate a robot name like KU765 or NG274 style
and to store them and check it to avoid repetition.
I also need to make a "reset" method to delete all stored names and start again.
This program is not working for some reason. I hope somebody helps me to find the mistake.
Thanks a lot.
class Robot
attr_accessor :named , :stored_names , :rl
def self.name
new.name
end
##rl = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def name
named = ""
named << ##rl[rand(26).to_i]
named << ##rl[rand(26).to_i]
named << rand(100..999).to_s
named.save_name
named.check_name
end
def save_name
stored_names = []
stored_names << named
end
def check_name
stored_names.uniq!
end
def reset
stored_names = Array.new
end
end
Here's another way to construct the Robot class that you may wish to consider. (My answers are not normally this long or detailed, but I wrote this in part to clarify aspects of Ruby's object model in my own mind. I hope it might help others do the same.)
Code
PREFACE = ('A'..'Z').to_a << ?_
SUFFIX = ('0'..'9').to_a
PREFACE_SIZE = 2
SUFFIX_SIZE = 3
class Robot
def self.reset() #bots = [] end
reset
def self.new() (#bots << super).last end
def self.bots() #bots end
def self.delete(bot) #bots.delete(bot) end
def self.bot_names() #bots.map { |b| b.name } end
attr_reader :name
def initialize() #name = add_name end
private
def add_name
loop do
#name = gen_name
return #name unless self.class.bot_names.include?(#name)
end
end
def gen_name
PREFACE.sample(PREFACE_SIZE).join << SUFFIX.sample(SUFFIX_SIZE).join
end
end
Example
Robot.bots #=> []
robbie = Robot.new #=> #<Robot:0x000001019f4988 #name="AP436">
robbie.name #=> "AP436"
Robot.bots #=> [#<Robot:0x000001019f4988 #name="AP436">]
r2d2 = Robot.new #=> #<Robot:0x000001019cd450 #name="KL628">
r2d2.name #=> "KL628"
Robot.bots #=> [#<Robot:0x000001019f4988 #name="AP436">,
# #<Robot:0x000001019cd450 #name="KL628">]
Robot.bot_names #=> ["AP436", "KL628"]
Robot.delete(robbie) #=> #<Robot:0x000001019f4988 #name="AP436">
Robot.bots #=> [#<Robot:0x000001019cd450 #name="KL628">]
Robot.bot_names #=> ["KL628"]
Robot.reset #=> []
c3po = Robot.new #=> #<Robot:0x000001018ff8c0 #name="VO975">
Robot.bots #=> [#<Robot:0x000001018ff8c0 #name="VO975">]
Explanation
When the class is parsed, the class method reset is first created, then the line reset is executed. As self => Robot when that occurs, the class method reset is executed, initializing #bots to an empty array.
The responsibility for saving and modifying a list of instances of Robot lies with the class. This list is held in the class instance variable #bots.
Instance of Robot are created by invoking Robot::new, which allocates memory and then invokes the (private) instance method initialize. Where is new? Since we have not defined it as a class method in Robot, there are two possibilities: it is inherited from one of Robot's ancestors (Robot.ancestors => [Robot, Object, Kernel, BasicObject]) or it is an instance method of the class Class, as that is the class for which Robot is an instance (i.e., Robot.class => Class) Let's find out which: Class.instance_method(:new) => #<UnboundMethod: Class#new> (or Class.instance_methods.include?(:new) => true), Object.method(:new) => #<Method: Class#new>. It's both! But that makes sense, because all classes are instances of Class, including Robot's superclass, Object. #<Method: Class#new> returned by Object.method(:new) shows new is defined in Class (which can alternatively be seen with Robot.method(:new).owner => Class. Very cool, eh? If you didn't know this already, and can follow what I've said in this paragraph, you've just learned the essence of Ruby's object model!
Suppose we add the class method new, shown below, to Robot. super invokes the class method Object::new (which is the instance method Class#new), passing any arguments of new (here there aren't any). Object::new returns the instance that it creates, which Robot::new in turn returns. Therefore, this method would simply be a conduit and and have no effect on the results.
def self.new
super
end
We can make a small change to the above method to add a copy of the instance that is created by Object::new to the array #bots:
def self.new
instance = super
#bots << instance
instance
end
I have written this a little more compactly as:
def self.new
(#bots << super).last
end
I've used the method Array#sample to randomly draw PREFACE_SIZE characters from PREFACE and SUFFIX_SIZE characters from SUFFIX_SIZE. sample samples without replacement, so you will not get, for example, "UU112". If you want to sample with replacement, replace the method gen_name with the following:
def gen_name
str = PREFACE_SIZE.times.with_object('') { |_,s| s << PREFACE.sample }
SUFFIX_SIZE.times { str << SUFFIX.sample }
str
end
I have created a class method bots to return the value of the class instance variable #bots. This could alternatively be done by defining a read accessor for #bots on Robots' singleton class:
class << self
attr_reader :name
end
When Robot.reset is invoked, what happens to all the instances of Robot it had contained? Will they be left to wander the forest, rejected and homeless? In languages like C you need to release their memory before casting them aside. In Ruby and many other modern languages that's not necessary (and generally can't be done). Ruby's "garbage collection" keeps track of all objects, and kills off (after releasing memory) any that are no longer referenced by any other object. Nice, eh?
The task itself is not hard, but I don't like the way your code is organised. This is what I would do in the first stage:
class Robot
class Name < String
class << self
def sign
"#{[*?A..?Z].sample}#{[*?A..?Z].sample}"
end
def number
"#{rand 1..9}#{rand 0..9}#{rand 0..9}"
end
def new
super << sign << number
end
end
end
end
And then:
Robot::Name.new
When constructing a list of names it is easy to check that they are unique. This is how I'd go about it:
class Robot
class Names < Array
def self.generate n
new.tap { |array| n.times do array.add_random_name end }
end
def add_random_name
name = Name.new
include?( name ) ? add_random_name : self << name
end
end
end
And then:
Robot::Names.generate 7
def save_name
stored_names = []
stored_names << named
end
Every time, you create a name, and call save_name you delete all previously created names, by assigning an empty array to stored_names
EDIT:
There were a few more errors, let me first post a working solution:
class Robot
attr_accessor :named , :stored_names , :rl
def initialize()
#stored_names = []
end
##rl = "_ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars.to_a
def name
#named = ""
#named << ##rl.sample
#named << ##rl.sample
#named << rand(100..999).to_s
save_name
check_name
end
def save_name
#stored_names << #named
end
def check_name
#stored_names.uniq!
end
def reset
#stored_names = Array.new
end
end
To access the members of your object, you have to prefix them with #.
You called save_name and check_name on #named, which is a string and doesn't provide these methods
#stored_names must be initialized to an empty array, before you can push elements into it with <<. This is normally done in the class's constructor initialize()
I understand this isn't efficient, but this will work.
letters = [*'A'..'Z'] =>
numbers = [*100..999]
names = []
2.times{names.push(letters.shuffle.first)} => Randomizing array and choosing element
names.push(numbers.shuffle.first)
names.join => Creates a String object out of the array elements
This isn't pretty, but it gets the job done.
This is how I automate Cary's approach with my y_support/name_magic:
require 'y_support/all'
class Robot
★ NameMagic
def name_yourself
begin
self.name = "#{[*?A..?Z].sample( 2 ).join}#{rand 100..999}"
rescue NameError; retry end
end
end
3.times { Robot.new.name_yourself }
Robot.instances #=> [PR489, NS761, OE663]
Robot.forget "PR489"
Robot.instances #=> [NS761, OE663]
Robot.forget_all_instances
Robot.instances #=> []
Robot.new.name_yourself
Robot.instances #=> [IB573]

Why can't I puts an instance variable of a Ruby class if it contains the value of a variable in a module?

When I try to run this code, nothing or nil shows up. I can't seem to understand why, since I thought classes that include modules can access its instance/class variables. I can print out the value just fine if I don't use garbtest and just use the garb= method to assign it a different value. It works fine without assigning it another value since I initialized it to 16 too. Is there something about the instance/class variables in the module Test that makes it equal to nil? Furthermore, when I try to assign garb to #myg + ##vit it says there is no such method for the nil class. I think this further confirms my suspicion that those variables are somehow nil. Thank you.
module Test
RED = "rose"
BLUE = "ivy"
#myg = 9
##vit = 24.6
end
class Xy
include Test;
def initialize(n)
#garb = n
end
attr_accessor :garb;
def garbTest
#garb = #myg;
end
def exo
return 50;
end
end
ryu = Xy.new(16);
ryu.garbTest;
puts "#{ryu.garb}";
Because #myg is not shared variable. It is private property of module Test, thus while you included Test, #myg didn't come into Xy due to the mixin, it wouldn't come also by default. But, "Why nil?" - Because, instance variable, class variables are like that. Before initialising/defining them, if you attempt to use them, it will simply give you nil.
Small program to prove myself and Ruby :-
module Test
#x = 10
##y = 11
end
class Foo
include Test
end
Foo.instance_variable_defined?(:#x) # => false
Test.instance_variable_defined?(:#x) # => true
Foo.class_variable_defined?(:##y) # => true
Test.class_variable_defined?(:##y) # => true
You can define reader method inside Test singleton class, and then you can use it. Look below
module Test
class << self
attr_reader :myg
end
RED = "rose"
BLUE = "ivy"
#myg = 9
##vit = 24.6
end
class Xy
include Test
def initialize(n)
#garb = n
end
attr_accessor :garb
def garbTest
#garb = Test.myg
end
def exo
return 50
end
end
ryu = Xy.new(16)
ryu.garbTest # => 9

using attributes of different objects in different objects methods

what i need is basically to use variable from one file, in the method. let me explain
lets say we have
class Game
attr_accessor :number, :object
end
where number is just some number and object is object of some other class defined by me, lets name it Player class. now we make another file, which requires class Game, and which goes like this:
require './Game.rb'
require './Player.rb'
myGame = Game.new
myGame.number = 1
myGame.object = Player.new
and now the big moment. in method defined in Player class, i would like to use myGame.number attribute. eg like this
class Player
attr_accessor :some_var
def method
#some_var = myGame.number
end
end
How can i achieve this?
Your player should have a reference to the game is playing. For instance
class Game
attr_accessor :number
attr_reader :my_player
def my_player=(player)
player.my_game = self
#my_player = player
end
end
class Player
attr_accessor :some_var, :my_game
def method
#some_var = #my_game.number if #my_game
end
end
myGame = Game.new
myGame.number = 1
myGame.my_player = Player.new()
myGame.my_player.method
puts myGame.my_player.some_var
Alternatively to toch's answer, you can keep a reference to the game object when you set the player accessor. Instead of using automatic accessors you can use the get_ set_ accessor syntax to have custom code in the accessor, which would set the reference on the rvalue.

Resources