Forum: Ruby How to reference an instance within class initialization method.

A6e25480861413fa4fa712f14aea5ada?d=identicon&s=25 Roger Atkins (rba2124)
on 2014-07-22 06:57
Attachment: ClassSquare.rb (962 Bytes)
Attachment: MakeSq.rb (266 Bytes)
In trying to learn Ruby, I started a program to create squares.

In defining class Square, I want each new instance to have all the
properties of a square, including its grid-like rows and columns. I
wanted the initialize method to make each new instance an array of
arrays from which I can form rows, columns, sides and diagonals. The
grid could then be used for various applications including magic
squares. However, I don't know how to reference the instance within the
initialize method so that the instance will be an array of arrays by
virtue of being an instance of class Square. Instead, I tried to create
instance methods. The methods work except mksq, which is supposed to
create the array of arrays. Result = undefined method 'mksq' for
[]:square.

My code is in the attached files, with excerpts below. I would
appreciate any insight as to why the mksq method doesn't work.

I would also appreciate any insight as to whether there is a way to
reference the instance within the class methods.

# excerpt from 'ClassSquare.rb'

  def mksq
    @square = []
    0.upto(@ord-1) do |j|
      @square[j].push(Array.new)
      0.upto(@ord-1) do |i|
        @square[j][i].push(0)
      end
    end
    @square
  end


# 'MakeSq.rb'

require_relative 'ClassSquare.rb'

sq = Square.new
puts "Class = #{sq.class}"
puts "Ord = #{sq.ord}; magic constant = #{sq.m}"
puts "Area = #{sq.area} square units"
print "\nValues = #{sq.values}\n"
sq.push(sq.mksq)
0.upto(sq.ord-1) do |i|
  puts sq[i]
end
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2014-07-23 00:00
For a start I wouldn't inherit Array. I made that mistake when trying to
build a spreadsheet 2D array class, and ended up starting over again
with the Array hidden inside the class, not as a superclass.

Don't use "gets" in your initialize code. Make that part of the user
interface in the rest of your program, but not integral to the class.
What if you wanted to create a Square without interacting with the user?

Keep it simple. Start with the value which is your matrix (2d array),
and then let the rest of your methods and attributes look to that core.

class Square
  attr_accessor :value
  def initialize( width )
    self.value = Array.new(width) { Array.new(width) }
  end
end

Things like "area" shouldn't be an attribute, they should be a
calculation done on the value. This makes it possible to change the
outcome of all the calculations when you change the dimensions of your
square, without having to update each instance variable.

class Square
  def area
    value.length**2
  end
end

I hope this helps.
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2014-07-23 00:14
You can have a look around my source code if you want. It might give you
some ideas on creating a structure of your own:
https://github.com/VirtuosoJoel/RubyExcel
A6e25480861413fa4fa712f14aea5ada?d=identicon&s=25 Roger Atkins (rba2124)
on 2014-07-23 06:37
Joel:
Thanks. I tried using self, but couldn't get it to work. I'll play with
your code and see if I can figure out where the value method is defined
and how it works, and how self works. I think your approach works
because self would be the class, whereas I was trying to use it as a
reference to the instance. If I understand how it works, it incorporates
the array of arrays property into the class, eliminating the need to
refer to the instance.

I see your point about the potential need to have a square defined for a
specific purpose within an application without user involvement. A chess
board would be an example. It would need to be an 8x8 grid and therefore
would not require user input. I guess what you are saying is that I
should pass order as a parameter to the initialize method and have the
user input code in the body of the program. I'll work on that. Thanks
again.

BTW, any thoughts as to why method mksq didn't work whereas my other
instance methods performed as expected?
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2014-07-23 21:57
Unless there's some code you're not sharing, you never defined mksq
inside the class Square, so "sq.mksq" wouldn't exist as a method. What
error / unexpected result do you get?
14b5582046b4e7b24ab69b7886a35868?d=identicon&s=25 Joel Pearson (virtuoso)
on 2014-07-23 22:14
Roger Atkins wrote in post #1153215:
> I'll play with
> your code and see if I can figure out where the value method is defined
> and how it works, and how self works.

"self.value" is similar to "@value", except that it uses the method
created by the line "attr_accessor :value"

When I write "attr_accessor :value", Ruby defines these methods behind
the scenes:

class Square
  def value
    @value
  end
  def value=(obj)
    @value=obj
  end
end

There's a reason I don't just use the instance variable. Say later on
you decide to do a bit of preprocessing before returning the value, such
as ensuring that the array is a square before returning it (padding out
empty parts, maybe).
If you use @value, you have to change every place that it is used. If
you use the method Square#value, then you only have to redefine that one
method, and the rest of your code doesn't need to be altered.



"self" is a very useful word in Ruby once you start using Classes to
their full potential. Here's an example:

class Square
  def do_something
    puts 1
  end
  def do_something_else
    puts 2
  end
end

sq.do_something
sq.do_something_else

If you tried to chain those two methods together into:
sq.do_something.do_something_else
You'd get an error, because "puts" returns nil, and
nil.do_something_else isn't defined.

However, try this:

class Square
  def do_something
    puts 1
    self
  end
  def do_something_else
    puts 2
    self
  end
end

You can now chain the methods together, because what sq.do_something
returns when it's finished is sq. Each Instance of Square will return
itself when calling that method; not the Class Square, but that specific
Instance of that Class. What's what "self" refers to when you're in an
Instance method.


Holding your 2D Array in an instance variable is very useful because you
don't need to hold all the straights and diagonals as well, all they do
is point to parts of @value. Rather than hold each of them in a
variable, just make them into methods that return parts of @value, or do
some calculation on it.
A6e25480861413fa4fa712f14aea5ada?d=identicon&s=25 Roger Atkins (rba2124)
on 2014-07-24 01:28
Joel:

After analyzing your code some more, I realized that self.value doesn't
call a method. The concept of calling a variable is new to me. I had
tried things like self.push (back when Array was a super class) but
couldn't get it to work.

I re-read the Pickaxe material regarding class Array and regarding self
in light of your code. Now I have a better understanding of what the
Pickaxe tech-speak was trying to tell me. I was able to code a solution
that accomplishes my objective. Thanks again.
A6e25480861413fa4fa712f14aea5ada?d=identicon&s=25 Roger Atkins (rba2124)
on 2014-07-24 01:54
Joel Pearson wrote in post #1153356:
> Unless there's some code you're not sharing, you never defined mksq
> inside the class Square, so "sq.mksq" wouldn't exist as a method. What
> error / unexpected result do you get?

I apologize if my code files were not attached. They were listed as
attachments when I submitted the original post. I have since modified
the files substantially based on your advice, so I can't attach the same
material I thought I attached the first time. If the files were attached
and you looked in 'ClassSquare.rb' and there was no mksq method, I must
not have saved the file properly. As indicated in the original post,
mksq was as follows:

# excerpt from 'ClassSquare.rb'

  def mksq
    @square = []
    0.upto(@ord-1) do |j|
      @square[j].push(Array.new)
      0.upto(@ord-1) do |i|
        @square[j][i].push(0)
      end
    end
    @square
  end

It may be moot now that I have achieved the intended result using your
initialization code.
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.