[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Rubyish inst.var initializations

Victor 'Zverok' Shepelev

11/23/2006 1:04:00 AM

Hi all.

I have some huge class with aspects of functionality spreaded in several
source files.

And in some of those aspects there is the same picture:
---
class MyClass
def push_something(obj)
@something_list ||= []
@something_list << obj
end

def use_something(i)
(@something_list ||= [])[i]
end
end
---

Then I note (through profiler) various push_XXX spend too much time in
checking is @XXX_list initialized. Then I change it:

---
class MyClass
alias :initialize_without_something :initialize

def initialize(*arg)
initialize_without_something(*arg)
@something_list = []
end

def push_something(obj)
@something_list << obj
end

def use_something(i)
@something_list[i]
end
end
---

Now push_XXX and use_XXX work cleaner and faster, but all those initialize
aliases (now I have 5 or 6) don't seem to be very elegant.

Is there better solution?

(to be clear, all those push_XXX are NOT similar - some of them push object
to hashes, others to arrays, params count differ and so on - so, they can't
be generated at once through metaprogramming)

Thanks.

V.


11 Answers

dblack

11/23/2006 1:31:00 AM

0

Victor 'Zverok' Shepelev

11/23/2006 1:36:00 AM

0

From: dblack@rubypal.com [mailto:dblack@rubypal.com] On Behalf Of
dblack@wobblini.net
Sent: Thursday, November 23, 2006 3:31 AM
>On Thu, 23 Nov 2006, Victor "Zverok" Shepelev wrote:
>
>> Hi all.
>>
>> I have some huge class with aspects of functionality spreaded in several
>> source files.
>>
>> And in some of those aspects there is the same picture:
>> ---
>> class MyClass
>> def push_something(obj)
>> @something_list ||= []
>> @something_list << obj
>> end
>>
>> def use_something(i)
>> (@something_list ||= [])[i]
>> end
>> end
>> ---
>>
>> Then I note (through profiler) various push_XXX spend too much time in
>> checking is @XXX_list initialized. Then I change it:
>>
>> ---
>> class MyClass
>> alias :initialize_without_something :initialize
>>
>> def initialize(*arg)
>> initialize_without_something(*arg)
>> @something_list = []
>> end
>>
>> def push_something(obj)
>> @something_list << obj
>> end
>>
>> def use_something(i)
>> @something_list[i]
>> end
>> end
>> ---
>>
>> Now push_XXX and use_XXX work cleaner and faster, but all those
>initialize
>> aliases (now I have 5 or 6) don't seem to be very elegant.
>>
>> Is there better solution?
>>
>> (to be clear, all those push_XXX are NOT similar - some of them push
>object
>> to hashes, others to arrays, params count differ and so on - so, they
>can't
>> be generated at once through metaprogramming)
>
>In your second example, what purpose does initialize_without_something
>serve? I'm wondering why you don't just do:
>
> def initialize
> @something_list = []
> end
>
>and then the other methods.

Because it would hide initialization code defined in other files for the
same class.

class A
def initialize; p 1 end
end

class A
def initialize; p 2 end
end

class A
def initialize; p 3 end
end

A.new # => 3

V.


Joel VanderWerf

11/23/2006 2:26:00 AM

0

Victor "Zverok" Shepelev wrote:
> Hi all.
>
> I have some huge class with aspects of functionality spreaded in several
> source files.
>
> And in some of those aspects there is the same picture:
> ---
> class MyClass
> def push_something(obj)
> @something_list ||= []
> @something_list << obj
> end
>
> def use_something(i)
> (@something_list ||= [])[i]
> end
> end
> ---
>
> Then I note (through profiler) various push_XXX spend too much time in
> checking is @XXX_list initialized. Then I change it:
>
> ---
> class MyClass
> alias :initialize_without_something :initialize
>
> def initialize(*arg)
> initialize_without_something(*arg)
> @something_list = []
> end
>
> def push_something(obj)
> @something_list << obj
> end
>
> def use_something(i)
> @something_list[i]
> end
> end
> ---
>
> Now push_XXX and use_XXX work cleaner and faster, but all those initialize
> aliases (now I have 5 or 6) don't seem to be very elegant.
>
> Is there better solution?
>
> (to be clear, all those push_XXX are NOT similar - some of them push object
> to hashes, others to arrays, params count differ and so on - so, they can't
> be generated at once through metaprogramming)
>
> Thanks.
>
> V.
>

I don't know how this will profile--it has the disadvantage of
generating more singletons than you might want, and there is the extra
overhead of an attr_reader rather than just @something_list.

class MyClass
def method_missing(m,*)
if m == :something_list
@something_list = []
class << self
attr_reader :something_list
end
something_list
else
super
end
end

def push_something(obj)
something_list << obj
end

def use_something(i)
something_list[i]
end
end

mc = MyClass.new

mc.push_something 3
p mc # ==> #<MyClass:0xb7d02114 @something_list=[3]>

Also, the method_missing def would need to know about each such attr,
which seems to contradict your goal of distributing that information to
lots of files. A little metaprogramming would help with that, since with
this approach you only need to metaprogram the accessors, and not all
the push_, pop_ etc. methods. Define a class method that registers
things like "something_list", and the method_missing can lookup in the
registered methods.

If you can tolerate a register method as well as the accessor overhead,
then this gives me another idea, which doesn't generate singletons:

class MyClass
@reg_methods_defined = false
@reg = {}

class << self
attr_reader :reg
def define_reg_methods
unless @reg_methods_defined
@reg.each do |m,|
attr_reader m
end
@reg_methods_defined = true
end
end
end

def initialize
self.class.define_reg_methods

self.class.reg.each do |m, (iv, bl)|
instance_variable_set(iv, bl.call)
end
end

def self.register m, &bl
@reg[m] = ["@#{m}", bl]
end
end

class MyClass
register :something_list do
[]
end

def push_something(obj)
something_list << obj
end

def use_something(i)
something_list[i]
end
end

mc = MyClass.new

mc.push_something 3
p mc # ==> #<MyClass:0xb7d65a7c @something_list=[3]>

Getting this to work for subclasses as well is left as an exercise to
the reader ;)

--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Ara.T.Howard

11/23/2006 2:26:00 AM

0

Ara.T.Howard

11/23/2006 3:42:00 AM

0

Ara.T.Howard

11/23/2006 3:59:00 AM

0

Robert Klemme

11/23/2006 9:26:00 AM

0

On 23.11.2006 02:36, Victor "Zverok" Shepelev wrote:
> From: dblack@rubypal.com [mailto:dblack@rubypal.com] On Behalf Of
> dblack@wobblini.net
> Sent: Thursday, November 23, 2006 3:31 AM
>> On Thu, 23 Nov 2006, Victor "Zverok" Shepelev wrote:
>>
>>> Hi all.
>>>
>>> I have some huge class with aspects of functionality spreaded in several
>>> source files.
>>>
>>> And in some of those aspects there is the same picture:
>>> ---
>>> class MyClass
>>> def push_something(obj)
>>> @something_list ||= []
>>> @something_list << obj
>>> end
>>>
>>> def use_something(i)
>>> (@something_list ||= [])[i]
>>> end
>>> end
>>> ---
>>>
>>> Then I note (through profiler) various push_XXX spend too much time in
>>> checking is @XXX_list initialized. Then I change it:
>>>
>>> ---
>>> class MyClass
>>> alias :initialize_without_something :initialize
>>>
>>> def initialize(*arg)
>>> initialize_without_something(*arg)
>>> @something_list = []
>>> end
>>>
>>> def push_something(obj)
>>> @something_list << obj
>>> end
>>>
>>> def use_something(i)
>>> @something_list[i]
>>> end
>>> end
>>> ---
>>>
>>> Now push_XXX and use_XXX work cleaner and faster, but all those
>> initialize
>>> aliases (now I have 5 or 6) don't seem to be very elegant.
>>>
>>> Is there better solution?
>>>
>>> (to be clear, all those push_XXX are NOT similar - some of them push
>> object
>>> to hashes, others to arrays, params count differ and so on - so, they
>> can't
>>> be generated at once through metaprogramming)
>> In your second example, what purpose does initialize_without_something
>> serve? I'm wondering why you don't just do:
>>
>> def initialize
>> @something_list = []
>> end
>>
>> and then the other methods.
>
> Because it would hide initialization code defined in other files for the
> same class.
>
> class A
> def initialize; p 1 end
> end
>
> class A
> def initialize; p 2 end
> end
>
> class A
> def initialize; p 3 end
> end
>
> A.new # => 3

Frankly, I believe this is not a too good idea. There should be one
main place (i.e. file) responsible for this class's definition and all
other places should only add to that class. Changing initialize's
signature would certainly be not a good idea either.

If you feel, you have to initialize additional fields, then this is a
cleaner solution - and also more modular:

class Foo
def initialize(a,b,c)
super
@a=a
@b=b
end
end

module Mixin
def initialize(*a,&b)
super
@foo = []
end
end

Then you can safely do in another place:

class Foo
include Mixin
end

and construction will still work ok plus you can use that module in
several places.

Another clean solution:

class Foo
def use_sth() @something ||= [] end
def push_sth(x) something << x end
end

Btw, you can make your push_something more efficient by using a single
statement:

def push_sth(x)
(@sth ||= []) << x
end

Kind regards

robert

Daniel Schierbeck

11/23/2006 1:36:00 PM

0

On Thu, 2006-11-23 at 10:04 +0900, Victor "Zverok" Shepelev wrote:
> Hi all.
>
> I have some huge class with aspects of functionality spreaded in several
> source files.
>
> And in some of those aspects there is the same picture:
> ---
> class MyClass
> def push_something(obj)
> @something_list ||= []
> @something_list << obj
> end
>
> def use_something(i)
> (@something_list ||= [])[i]
> end
> end
> ---
>
> Then I note (through profiler) various push_XXX spend too much time in
> checking is @XXX_list initialized. Then I change it:
>
> ---
> class MyClass
> alias :initialize_without_something :initialize
>
> def initialize(*arg)
> initialize_without_something(*arg)
> @something_list = []
> end
>
> def push_something(obj)
> @something_list << obj
> end
>
> def use_something(i)
> @something_list[i]
> end
> end
> ---
>
> Now push_XXX and use_XXX work cleaner and faster, but all those initialize
> aliases (now I have 5 or 6) don't seem to be very elegant.
>
> Is there better solution?
>
> (to be clear, all those push_XXX are NOT similar - some of them push object
> to hashes, others to arrays, params count differ and so on - so, they can't
> be generated at once through metaprogramming)

class MyClass
def something_list
@something_list ||= []
end

def push_something(obj)
something_list << obj
end
end

Optionally you could make #something_list private if you don't wish to
expose it to the outside.


Cheers,
Daniel


Joel VanderWerf

11/23/2006 6:55:00 PM

0

Daniel Schierbeck wrote:
> On Thu, 2006-11-23 at 10:04 +0900, Victor "Zverok" Shepelev wrote:
...
>> Then I note (through profiler) various push_XXX spend too much time in
>> checking is @XXX_list initialized. Then I change it:
...

Sounds like Victor was interested in avoiding the ||= in each access.
The following still does that check, though it is nice clean code, and
preferable unless profiling shows it to be a bottleneck:

> class MyClass
> def something_list
> @something_list ||= []
> end
>
> def push_something(obj)
> something_list << obj
> end
> end


--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Victor 'Zverok' Shepelev

11/24/2006 3:07:00 AM

0



>-----Original Message-----
>From: Joel VanderWerf [mailto:vjoel@path.berkeley.edu]
>Sent: Thursday, November 23, 2006 8:55 PM
>To: ruby-talk ML
>Subject: Re: Rubyish inst.var initializations
>
>Daniel Schierbeck wrote:
>> On Thu, 2006-11-23 at 10:04 +0900, Victor "Zverok" Shepelev wrote:
>...
>>> Then I note (through profiler) various push_XXX spend too much time in
>>> checking is @XXX_list initialized. Then I change it:
>...
>
>Sounds like Victor was interested in avoiding the ||= in each access.
>The following still does that check, though it is nice clean code, and
>preferable unless profiling shows it to be a bottleneck:
>
>> class MyClass
>> def something_list
>> @something_list ||= []
>> end
>>
>> def push_something(obj)
>> something_list << obj
>> end
>> end

Right. And Ara's solution is completely great (I've already said this
yesterday, but don't see those mail in list - interesting, why?).

V.