Definition of methods: self

  • Thread starter Damaris Fuentes
  • Start date
D

Damaris Fuentes

In Why's poignant guide to Ruby some examples appear where he defines a
class "class LotteryTicket", and then he defines a method "def
self.new_random", or "def LotteryDraw.buy(..)"; that is, before the name
of the method, the name of the class appears (the name or the keyword
"self"). However, in other methods in the other classes, the class name
is not set before the name of the method. Explanations? :D
 
D

Damaris Fuentes

Um...wait...Do I have to set the name of the class before the name of
the method when this method is a class method (except the "initialize")?
 
R

Robert Klemme

2006/7/8 said:
Um...wait...Do I have to set the name of the class before the name of
the method when this method is a class method (except the "initialize")?

Right, but no exception: initialize is an instance method. new is a
class method and after a new instance is allocated initialize is
called on that instance:

$ ruby -e 'set_trace_func lambda {|*a| p a}; Array.new'
["line", "-e", 1, nil, #<Binding:0x100f6938>, false]
["c-call", "-e", 1, :new, #<Binding:0x100f68f0>, Class]
["c-call", "-e", 1, :initialize, #<Binding:0x100f6698>, Array]
["c-return", "-e", 1, :initialize, #<Binding:0x100f6650>, Array]
["c-return", "-e", 1, :new, #<Binding:0x100f6518>, Class]

Note, there is also allocate (class method) that will create an empty
instance of the correct type. So new can be imagined as being
implemented like this:

class Class
def new(*a,&b)
o = allocate
o.initialize(*a,&b)
o
end
end

Kind regards

robert
 
M

Marcin Mielżyński

Damaris said:
Um...wait...Do I have to set the name of the class before the name of
the method when this method is a class method (except the "initialize")?

Yes. this is the class method declaration syntax. Within class body
definition the default receiver is the class itself, so the snippets
beneath are equivalent:

class A
def A.meth
end
end

class A
def self.meth
end
end

If there are more class methods in a class it is easier to switch to
class instance:

class B
class << self

def meth1
end

# more methods
end
end

or even:

class B
instance_eval do

def meth1
end

# more methods
end
end


lopex
 
D

Damaris Fuentes

Ok, thanks for the explanations!
But...now that you mention the concatenator... I will comment another
problem in this entry. (I write all the code, ven though perhaps you
don't need it)

The problem here is that the re- definition ot a class (LotteryDraw)
doesn't seem to see the attributes of the first definition.

I have the class ot the lottery ticket:
***********************
class LotteryTicket
NUMERIC_RANGE = 1..25
attr_reader :picks

def initialize( *picks )
[...]
@picks = picks
end

def score( final )
count = 0
final.picks.each do |note|
count +=1 if picks.include? note
end
count
end

end

*****************************
Now I have the lottery draw, where I store the buyers of the tickets:
**********************
class LotteryDraw
@@tickets = {}
def LotteryDraw.buy( customer, *tickets )
unless @@tickets.has_key?( customer )
@@tickets[customer] = []
end
@@tickets[customer] += tickets
end
end
*******
And NOW, I REDEFINE THE CLASS with the concatenator, adding a new class
method:
********************
class << LotteryDraw
def play
final = LotteryTicket.new_random
winners = {}
@@tickets.each do |buyer, ticket_list|
ticket_list.each do |ticket|
score = ticket.score( final )
next if score.zero?
winners[buyer] ||= []
winners[buyer] << [ ticket, score ]
end
end
@@tickets.clear
winners
end
end
********

And now I execute the following:
************************
LotteryDraw.buy('JuanMi',
LotteryTicket.new( 12, 6, 19 ),
LotteryTicket.new( 5, 1, 3 ),
LotteryTicket.new( 24, 6, 8 ))

LotteryDraw.buy('Da',
LotteryTicket.new( 18, 4, 7 ),
LotteryTicket.new( 21, 25, 3 ),
LotteryTicket.new( 12, 17, 19 ))

LotteryDraw.play.each do |winner, tickets|
puts winner + "won on " + tickets.length + " ticket(s)!"
tickets.each do |ticket, score|
puts "\t" + ticket.picks.join( ', ' ) + ": " + score
end
end
************************************

And an exception arises:
in `play': uninitialized class variable @@tickets in Object.
This exception appears in the trace:
- LotteryDraw.play.each do |winner, tickets|,
- @@tickets.each do |buyer, ticket_list| (in LotteryDraw.play)

What is the problem? Thanks (and sorry if this is very long :()
 
R

Robert Klemme

Damaris said:
Ok, thanks for the explanations!
But...now that you mention the concatenator... I will comment another
problem in this entry. (I write all the code, ven though perhaps you
don't need it)

The problem here is that the re- definition ot a class (LotteryDraw)
doesn't seem to see the attributes of the first definition.

I have the class ot the lottery ticket:
***********************
class LotteryTicket
NUMERIC_RANGE = 1..25
attr_reader :picks

def initialize( *picks )
[...]
@picks = picks
end

def score( final )
count = 0
final.picks.each do |note|
count +=1 if picks.include? note
end
count
end

end

*****************************
Now I have the lottery draw, where I store the buyers of the tickets:
**********************
class LotteryDraw
@@tickets = {}
def LotteryDraw.buy( customer, *tickets )
unless @@tickets.has_key?( customer )
@@tickets[customer] = []
end
@@tickets[customer] += tickets
end
end
*******
And NOW, I REDEFINE THE CLASS with the concatenator, adding a new class
method:
********************
class << LotteryDraw
def play
final = LotteryTicket.new_random
winners = {}
@@tickets.each do |buyer, ticket_list|
ticket_list.each do |ticket|
score = ticket.score( final )
next if score.zero?
winners[buyer] ||= []
winners[buyer] << [ ticket, score ]
end
end
@@tickets.clear
winners
end
end
********

And now I execute the following:
************************
LotteryDraw.buy('JuanMi',
LotteryTicket.new( 12, 6, 19 ),
LotteryTicket.new( 5, 1, 3 ),
LotteryTicket.new( 24, 6, 8 ))

LotteryDraw.buy('Da',
LotteryTicket.new( 18, 4, 7 ),
LotteryTicket.new( 21, 25, 3 ),
LotteryTicket.new( 12, 17, 19 ))

LotteryDraw.play.each do |winner, tickets|
puts winner + "won on " + tickets.length + " ticket(s)!"
tickets.each do |ticket, score|
puts "\t" + ticket.picks.join( ', ' ) + ": " + score
end
end
************************************

And an exception arises:
in `play': uninitialized class variable @@tickets in Object.
This exception appears in the trace:
- LotteryDraw.play.each do |winner, tickets|,
- @@tickets.each do |buyer, ticket_list| (in LotteryDraw.play)

What is the problem? Thanks (and sorry if this is very long :()

Without looking too closely at your code I'm guessing that it's because
your class methods are in the singleton class of LotteryDraw - and so
lookups for class variables are done in *that* hierarchy and not in the
one of LotteryDraw.

I generally recommend to not use class variables because they introduce
all sorts of weirdnesses. I'd rather use a normal instance variable of
the class instance. That's accessible for sure in the singleton method.

Kind regards

robert
 
S

Sean O'Halpin

I agree with Robert - avoid @@class_variables wherever possible. The
problem is the way @@class_variables are looked up - they are shared
within a class hierarchy. Also, class << LotteryDraw does not
introduce a new class definition scope (even though you may think it
looks like it should).

The following examples demonstrate the way lookup for class variables
works in the context you're using:

class A
@@a = 42
end

# add a class variable to Object - without this, the next puts @@a
would cause an error
@@a = 24
class << A
puts @@a # this refers to the toplevel Object @@a, not A's
end

class A
puts @@a # this does refer to A's @@a
end

p @@a

__END__
24
42
24

(By the way, if you had run ruby with -w you would have got the warning:
class variable access from toplevel singleton method
)

Also, the order in which you define @@class_variables matters. Look at
this example:

@@a = 24 # add a class variable to Object
class A
@@a = 42
# because @@a already exists in the class hierarchy (A inherits from Object)
# this assignment updates it
end

class << A
puts @@a # there is only one @@a now
end

class A
puts @@a
end

p @@a

__END__
42
42
42


My advice is steer clear of them!

Regards,

Sean
 
D

Damaris Fuentes

Yes, thank you all, boys. I had set the class<< LotteryDraw as a new
class definition. Now, I've set the class<<LotteryDrwa inside the
LotteryDraw itself and it already works. Thanks. And thanks for the
other comments.
 

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,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top