Help with Class design

C

Chris Lowis

I'm quite new to object-orientated programming and have a problem with a
class I am trying to design. At the moment I have

#!/usr/local/bin/ruby

require 'mathn'

class MySignal

def initialize(input)
@signal = input
end

def mean()
sum = 0;
@signal.each {|i| sum+=i}
mean = sum/@signal.length
end

def variance()
m = @signal.mean
var = @signal.inject(0) { |var, x| var += (x - m) ** 2 }
var = var/(@signal.length-1)
end
end

I can create a new "signal" with :

a = MySignal.new([1,2,3])

and calculate the mean with :

puts a.mean

But if I try to calculate the variance:

puts a.variance

/signal.rb:18:in `variance': undefined method `mean' for [1, 2,
3]:Array (NoMethodError)

Which makes sense, as @signal is an object with the Array class. I don't
want to add methods to Ruby's array class as later my methods may be too
specific to my problem.

How do I write a class such that methods defined within it can "refer"
to each other, for example "variance" can call and use "mean" ? I am
sure this question arises from my lack of familiarity with
object-orientated programming. In addition to suggestions for the
specific problem above, any pointers to references that could help me
learn more general OO-design would also be appreciated.

Kind regards,

Chris
 
S

Stefano Crocco

Alle marted=C3=AC 20 febbraio 2007, Chris Lowis ha scritto:
I'm quite new to object-orientated programming and have a problem with a
class I am trying to design. At the moment I have

#!/usr/local/bin/ruby

require 'mathn'

class MySignal

def initialize(input)
@signal =3D input
end

def mean()
sum =3D 0;
@signal.each {|i| sum+=3Di}
mean =3D sum/@signal.length
end

def variance()
m =3D @signal.mean
var =3D @signal.inject(0) { |var, x| var +=3D (x - m) ** 2 }
var =3D var/(@signal.length-1)
end
end

I can create a new "signal" with :

a =3D MySignal.new([1,2,3])

and calculate the mean with :

puts a.mean

But if I try to calculate the variance:

puts a.variance

./signal.rb:18:in `variance': undefined method `mean' for [1, 2,
3]:Array (NoMethodError)

Which makes sense, as @signal is an object with the Array class. I don't
want to add methods to Ruby's array class as later my methods may be too
specific to my problem.

How do I write a class such that methods defined within it can "refer"
to each other, for example "variance" can call and use "mean" ? I am
sure this question arises from my lack of familiarity with
object-orientated programming. In addition to suggestions for the
specific problem above, any pointers to references that could help me
learn more general OO-design would also be appreciated.

Kind regards,

Chris

You don't need to do anything special to be able to do what you want. You o=
nly=20
need to replace the line

[email protected]

with

m=3Dmean

which means

m=3Dself.mean

Methods called without an explicit receiver called using self as receiver.=
=20
Since you're inside the definition of an instance method of class MySignal,=
=20
self is an instance of class MySignal, and has a method called mean.

I hope this helps

Stefano
 
R

Robert Klemme

I'm quite new to object-orientated programming and have a problem with a
class I am trying to design. At the moment I have

#!/usr/local/bin/ruby

require 'mathn'

class MySignal

def initialize(input)
@signal = input
end

def mean()
sum = 0;
@signal.each {|i| sum+=i}
mean = sum/@signal.length
end

def variance()
m = @signal.mean
var = @signal.inject(0) { |var, x| var += (x - m) ** 2 }
var = var/(@signal.length-1)
end
end

I can create a new "signal" with :

a = MySignal.new([1,2,3])

and calculate the mean with :

puts a.mean

But if I try to calculate the variance:

puts a.variance

/signal.rb:18:in `variance': undefined method `mean' for [1, 2,
3]:Array (NoMethodError)

Which makes sense, as @signal is an object with the Array class. I don't
want to add methods to Ruby's array class as later my methods may be too
specific to my problem.

How do I write a class such that methods defined within it can "refer"
to each other, for example "variance" can call and use "mean" ? I am
sure this question arises from my lack of familiarity with
object-orientated programming. In addition to suggestions for the
specific problem above, any pointers to references that could help me
learn more general OO-design would also be appreciated.

You just need to change the line to

m = mean

or - if you want to be more verbose -

m = self.mean

i.e. you just picked the wrong receiver for the call.

Kind regards

robert
 
R

Robert Dober

I'm quite new to object-orientated programming and have a problem with a
class I am trying to design. At the moment I have

#!/usr/local/bin/ruby

require 'mathn'

class MySignal

def initialize(input)
@signal = input
end

def mean()
sum = 0;
@signal.each {|i| sum+=i}

you could use inject here too
@signal.inject{|s,i|s+i}
you do not need a start value, try this to see why
%w{Hello Brave Gnu World}.inject{|a,b| p [a,b]}
and
%w{Hello Brave Gnu World}.inject{|a,b| p [a,b]; b}
I just learned that recently from Ruby Quiz 113 ;)
http://www.rubyquiz.com/quiz113.html
but be aware that inject is slower
mean = sum/@signal.length
end

def variance()
m = @signal.mean

m = self.mean
(which is equivalent to m = mean unless a local variable exists)
var = @signal.inject(0) { |var, x| var += (x - m) ** 2 }
@signal.inject(0){|v,x| v + (x-m) ** 2 } # the start value is needed
here but assignment is an unnecessary step

again #each might be faster albeit less elegant, if you run into
performance issues
var = var/(@signal.length-1)
maybe you want to rescue ZeroDevision here?
begin ...
rescue ZeroDevisionError
end
end
end

I can create a new "signal" with :

a = MySignal.new([1,2,3])

and calculate the mean with :

puts a.mean

But if I try to calculate the variance:

puts a.variance

./signal.rb:18:in `variance': undefined method `mean' for [1, 2,
3]:Array (NoMethodError)

Which makes sense, as @signal is an object with the Array class. I don't
want to add methods to Ruby's array class as later my methods may be too
specific to my problem.

How do I write a class such that methods defined within it can "refer"
to each other, for example "variance" can call and use "mean" ? I am
sure this question arises from my lack of familiarity with
object-orientated programming. In addition to suggestions for the
specific problem above, any pointers to references that could help me
learn more general OO-design would also be appreciated.

Kind regards,

Chris
HTH
Robert
 
K

Kalman Noel

Chris Lowis:
require 'mathn'

class MySignal

def initialize(input)
@signal = input
end

def mean()
sum = 0;
@signal.each {|i| sum+=i}
mean = sum/@signal.length
end

def variance()
m = @signal.mean
var = @signal.inject(0) { |var, x| var += (x - m) ** 2 }
var = var/(@signal.length-1)
end
end

I suppose that you variance method should read something like

def variance
m = mean
@signal.inject(0) { |sum, x| sum + (x-m)**2 } / @signal.length
end

Note that I prefer not using meaningless assignment in the inject block.

Kalman
 
C

Chris Lowis

Thank you all for your help, knowing exactly what "self" refers to in
each context seems to be the stumbling block for me at the moment. I'll
have to read about this some more, along with the documentation for some
of the Enumerables .
I suppose that you variance method should read something like

def variance
m = mean
@signal.inject(0) { |sum, x| sum + (x-m)**2 } / @signal.length
end

Note that I prefer not using meaningless assignment in the inject block.

And to that end, using the suggestions above, I suppose I could equally
well write :

def variance()
@signal.inject(0) { |var, x| var += (x - mean) ** 2 } /
(@signal.length-1)
end

removing the assignment "m = mean" ?
 
B

benjohn

Thank you all for your help, knowing exactly what "self" refers to in
each context seems to be the stumbling block for me at the moment. I'll
have to read about this some more, along with the documentation for some
of the Enumerables .


And to that end, using the suggestions above, I suppose I could equally
well write :

def variance()
@signal.inject(0) { |var, x| var += (x - mean) ** 2 } /
(@signal.length-1)
end

removing the assignment "m = mean" ?

You can, and I probably would, but you will then be calling the method
"mean" a lot. In fact, for a signal of size n, you will find the mean n
times, which makes your complexity scale n**2.

However, you can make the method "mean" cache, or memoize the value it
calulates:

def mean
@mean ||= calculate_mean
end

def calculate_mean
@signal.inject {|s,x| s+x} / @signal.size
end
 
B

benjohn

And to that end, using the suggestions above, I suppose I could equally
well write :

def variance()
@signal.inject(0) { |var, x| var += (x - mean) ** 2 } /
(@signal.length-1)
end

removing the assignment "m = mean" ?

I wrote about removing the "m=mean" in another post...

I don't think the above will work, and even if it does, I would advise
very strongly against putting an assignment in to your inject block. It
breaks the semantics of what people (well, me anyway) expect inject to
do. What you're trying to do with inject is make a more "functional"
program; you're trying to avoid assigning to things; you're closer to
saying what you want done, rather than how you want it done (declarative
verses imperative style). If you start making assignments, you break
away from that style.
 
C

Chris Lowis

What you're trying to do with inject is make a more "functional"
program; you're trying to avoid assigning to things; you're closer to
saying what you want done, rather than how you want it done (declarative
verses imperative style). If you start making assignments, you break
away from that style.

I think I understand what you mean, but I am so used to programming in
languages where assignments are common place (eg. Matlab) that I am
struggling to adjust to this new way of thinking. For example I have a
method :

def diff()
result = []
@signal.each_index do |i|
result << @signal[i+1] - @signal unless i == (@signal.length -
1)
end
result
end

which takes a @signal object and returns an array of the differences
between each successive element of signal. But now I cannot write

a = MySignal.new([1,2,3])
a.diff.variance

because diff has returned an object of class "array" and the variance
method is not defined . How do I write "diff" in someway that it gives
me a new "signal" instead of an array ?

Thank you all for your help !

Chris
 
O

Olivier Renaud

I think I understand what you mean, but I am so used to programming in
languages where assignments are common place (eg. Matlab) that I am
struggling to adjust to this new way of thinking. For example I have a
method :

def diff()
result = []
@signal.each_index do |i|
result << @signal[i+1] - @signal unless i == (@signal.length -
1)
end
result
end

which takes a @signal object and returns an array of the differences
between each successive element of signal. But now I cannot write

a = MySignal.new([1,2,3])
a.diff.variance

because diff has returned an object of class "array" and the variance
method is not defined . How do I write "diff" in someway that it gives
me a new "signal" instead of an array ?

Thank you all for your help !

Chris


You want to return a new Signal so... just do it !

def diff()
result = []
@signal.each_index do |i|
result << @signal[i+1] - @signal unless i == (@signal.length-1)
end
return MySignal.new(result)
end

Maybe I missed something in your question, or maybe you would like your class
MySignal to act more like an Array. If so, there are many solutions (in order
of preference) :

* Use the Forwardable module to give access to specific methods from Array
* Use the Delegate lib to delegate all unknown methods to Array
* Make your class a subclass of Array (but delegation is almost always better)
* Reopen Array and add extra features (but this should be reserved for some
specific needs, not in your case)

I hope I answered your question :)
 
R

Robert Dober

What you're trying to do with inject is make a more "functional"
program; you're trying to avoid assigning to things; you're closer to
saying what you want done, rather than how you want it done (declarative
verses imperative style). If you start making assignments, you break
away from that style.

I think I understand what you mean, but I am so used to programming in
languages where assignments are common place (eg. Matlab) that I am
struggling to adjust to this new way of thinking. For example I have a
method :

def diff()
result = []
@signal.each_index do |i|
result << @signal[i+1] - @signal unless i == (@signal.length -
1)
end
result
end

which takes a @signal object and returns an array of the differences
between each successive element of signal. But now I cannot write

a = MySignal.new([1,2,3])
a.diff.variance

because diff has returned an object of class "array" and the variance
method is not defined . How do I write "diff" in someway that it gives
me a new "signal" instead of an array ?
def diff
r= []
@signal.inject{|a,b| r << (b-a); b}
Signal.new( r )
end
HTH
Robert
 
D

dblack

Hi --

Thank you all for your help, knowing exactly what "self" refers to in
each context seems to be the stumbling block for me at the moment. I'll
have to read about this some more, along with the documentation for some
of the Enumerables .


And to that end, using the suggestions above, I suppose I could equally
well write :

def variance()
@signal.inject(0) { |var, x| var += (x - mean) ** 2 } /
(@signal.length-1)
end

removing the assignment "m = mean" ?

The disadvantage of that is that it will call the method mean once for
each iteration. So you're better off storing it in a local variable.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
B

benjohn

What you're trying to do with inject is make a more "functional"
program; you're trying to avoid assigning to things; you're closer to
saying what you want done, rather than how you want it done
(declarative
verses imperative style). If you start making assignments, you break
away from that style.

I think I understand what you mean, but I am so used to programming in
languages where assignments are common place (eg. Matlab) that I am
struggling to adjust to this new way of thinking. For example I have a
method :

def diff()
result = []
@signal.each_index do |i|
result << @signal[i+1] - @signal unless i == (@signal.length -
1)
end
result
end

which takes a @signal object and returns an array of the differences
between each successive element of signal. But now I cannot write

a = MySignal.new([1,2,3])
a.diff.variance

because diff has returned an object of class "array" and the variance
method is not defined . How do I write "diff" in someway that it gives
me a new "signal" instead of an array ?


Get it to wrap the array in to a signal, and return that.

So, at the end of the method you'll do something like:

...
end
Signal.new(result)
end

Cheers,
Benj
 
C

Chris Lowis

Signal.new(result)

Thanks to all who gave the solution, it seems obvious now I've had it
pointed out to me ! I guess the question for me now is whether
signal.diff is still a signal or not, and if not, perhaps I need a
different kind of class to represent these objects.

Can you recommend books/tutorials for any (computer) language that deal
with modelling a non-trivial system using objects ? A lot of the
examples I have seen up to now are quite simple .

Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top