Rubyish inst.var initializations

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.

Hi –

On Thu, 23 Nov 2006, Victor “Zverok” Shepelev wrote:

@something_list << obj

@something_list << obj

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.

David

From: [email protected] [mailto:[email protected]] On Behalf Of
[email protected]
Sent: Thursday, November 23, 2006 3:31 AM

def push_something(obj)
Then I note (through profiler) various push_XXX spend too much time in

Now push_XXX and use_XXX work cleaner and faster, but all those

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.

Victor “Zverok” Shepelev wrote:

@something_list << obj

@something_list << obj

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 :wink:

On Thu, 23 Nov 2006, Victor “Zverok” Shepelev wrote:

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)

i think metaprogramming is perfect: define access to the ivars
(containers) to
initialize the ivar and then redefine the method in-place for direct
access thereafter :

 harp:~ > cat a.rb
 class Module
   def init_attr a, &init
     ivar = "@#{ a }"
     this = self
     define_method a do
       begin
         instance_variable_set ivar, instance_eval(&init)
       ensure
         this.module_eval{ define_method(a){ instance_variable_get 

ivar } }
end
end
end
end

 class C
   init_attr(:list){ Array.new }
   def push(val) list.push val end
 end

 obj = C.new
 p obj.list
 obj.push 42
 p obj.list


 harp:~ > ruby a.rb
 []
 [42]

to clean it up pull ‘init_attr’ into it’s own module and extend only
those
classes that need this functionality with the module.

regards.

-a

On Thu, 23 Nov 2006 [email protected] wrote:

to clean it up pull ‘init_attr’ into it’s own module and extend only those
classes that need this functionality with the module.

since my first method was slightly flawed, here’s the above plus a fix:

harp:~ > cat a.rb
module InitAttr
def init_attr a, &init
ivar = “@#{ a }”
define_method a do
this = class << self; self; end
begin
instance_variable_set ivar, instance_eval(&init)
ensure
this.module_eval{ define_method(a){ instance_variable_get
ivar } }
end
end
end
end

class C
extend InitAttr
init_attr(:list){ Array.new }
def push(val) list.push val end
end

obj = C.new
p obj.list
obj.push 42
p obj.list
p C.new.list

harp:~ > ruby a.rb
[]
[42]
[]

-a

On Thu, 2006-11-23 at 10:04 +0900, Victor “Zverok” Shepelev wrote:

@something_list << obj

@something_list << obj

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

Daniel S. 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:

On Thu, 23 Nov 2006, Joel VanderWerf wrote:

> > mc = MyClass.new > > mc.push_something 3 > p mc # ==> # > > Getting this to work for subclasses as well is left as an exercise to the > reader ;)

to do exactly this, without any extract class instance vars, plus proper
behaviour in subclasses, and not too many extract singletons, one can
simply
use attributes.rb (from the metakoans.rb rubyquiz):

 harp:~ > cat a.rb
 require 'set'
 require 'rubygems'
 require 'attributes' ### gem install attributes - it's __only__ 42 

lines!

 class C
   attribute('array'){ Array.new }
   attribute('set'){ Set.new }

   def initialize
     self.class.attributes.each{|a| send a} # force init
   end

   def array_push(obj) @array << obj end
   def set_push(obj) @set << obj end
 end

 class B < C; end


 #
 # works for objects
 #
   c = C.new
   c.array_push 42
   c.set_push 42
   p c.array
   p c.set

 #
 # even in subclasses
 #
   b = B.new
   p b.array
   p b.set


 harp:~ > ruby a.rb
 [42]
 #<Set: {42}>
 []
 #<Set: {}>

if you hate installing/depending-on gems then just steal the code: it’s
so
short you can inline it!

regards.

-a

checking is @XXX_list initialized. Then I change it:

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.

On 23.11.2006 02:36, Victor “Zverok” Shepelev wrote:

And in some of those aspects there is the same picture:
end
initialize_without_something(*arg)
end
to hashes, others to arrays, params count differ and so on - so, they

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

From: Robert K. [mailto:[email protected]]
Sent: Friday, November 24, 2006 9:51 PM

source files.
def initialize(a,b,c)
end
end

Then you can safely do in another place:

class Foo
include Mixin
end

Robert, thanks. It’s a REALLY good point.
I’ve completely haven’t think about mixin’s initialize. Would guess it.

V.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs