Associating data with a function

G

Gavin Kistner

Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?
=20
The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 =3D ExpensiveObject.new
tmp2 =3D ExpensiveObject.new
# stuff involving tmp1/tmp2
result =3D tmp1 + tmp2 + c2
end
def c2
tmp1 =3D ExpensiveObject.new
tmp2 =3D ExpensiveObject.new
# stuff involving tmp1/tmp2
result =3D tmp1 + tmp2
end
end


The Solution We'll Ignore
--------------------------
We *could* create a distinct instance variable for each needed variable.
I personally don't like the weird coupling this creates, though. So I'm
ignoring it.

class Foo
def initialize
@tmp1 =3D ExpensiveObject.new
@tmp2 =3D ExpensiveObject.new
@tmp3 =3D ExpensiveObject.new
@tmp4 =3D ExpensiveObject.new
end
def c1
tmp1, tmp2 =3D @tmp1, @tmp2
#...
end
def c2
tmp1, tmp2 =3D @tmp3, @tmp4
#...
end
end


The JavaScript Solution
-----------------------
The solution I used for JavaScript was that functions/methods are
first-class objects that can have arbitrary data assigned, and can get a
reference to themselves during execution. So:

Foo.prototype.c1 =3D function( ) {
var tmp1 =3D arguments.callee.eo1;
var tmp2 =3D arguments.callee.eo2;
//...
}
Foo.prototype.c1.eo1 =3D new ExpensiveObject;
Foo.prototype.c1.eo2 =3D new ExpensiveObject;


There were a few potential solutions for Lua. I'll list the two best,
just to help give you ideas:

Lua Solution #1: Closures (would work for JS, too)
--------------------------------------------------
Foo.c1 =3D ( function( )
local tmp1 =3D ExpensiveObject:new( )
local tmp2 =3D ExpensiveObject:new( )
return function( self )
-- stuff using tmp1 and tmp2
end
end)( )


Lua Solution #2: Changing the global environment for a function
---------------------------------------------------------------
function associateFunctionData( inF, inData )
setfenv(inF, setmetatable(inData,{__index=3Dgetfenv(inF)}))
end

function Foo:c1( )
-- stuff using tmp1 and tmp2
-- as though they were globals
end

associateFunctionData( Foo.c1, {
tmp1 =3D ExpensiveObject:new( ),
tmp2 =3D ExpensiveObject:new( )
} )
 
J

Jan Svitok

Just two ideas, it's too late to actually think ;-)

you can change your 'ignored' vesion to something more acceptable:

...
def c1
@c1_tmp1 ||= ExpensiveObject.new
@c1_tmp2 ||= ExpensiveObject.new
tmp1, tmp2 = @c1_tmp1, @c2.tmp2
...
end

I was thinking of some kind of delegation, but it got too messy so I
abandoned it.
Another possibility is to implement a pool of objects directly into
ExpensiveObject or to create a layer above it.

i.e. something like (not tested, not thread-safe, etc.):

class ExtensiveObjectPool
class << self
def initialize
@idle, @busy = [], []
end

def with(how_many)
objs = allocate(how_many)
yield *objs
ensure
release(objs)
end

def allocate(num)
ret = []
while num > 0 and not @free.empty?
num -= 1
ret << @free.pop
@busy << ret.last
end
while num > 0
num -= 1
ret << ExpensiveObject.new
@busy << ret.last
end
ret
end
def release(objs)
@busy.delete(*objs)
@free += objs
end
end

class Foo
def c1
ExtensiveObjectPool.with(2) do |tmp1, tmp2|
result = tmp1 + tmp2 + c2
end
end

def c2
ExtensiveObjectPool.with(2) do |tmp1, tmp2|
result = tmp1 + tmp2
end
end
end
 
D

dblack

Hi --

Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

Lua Solution #1: Closures (would work for JS, too)
--------------------------------------------------
Foo.c1 = ( function( )
local tmp1 = ExpensiveObject:new( )
local tmp2 = ExpensiveObject:new( )
return function( self )
-- stuff using tmp1 and tmp2
end
end)( )

You could do:

class Foo
def initialize
tmp1, tmp2 = EO.new, EO.new
define_method("c1") { do stuff with tmp1 etc. }
tmp1, tmp2 = EO.new, EO.new
define_method("c2") { do stuff with tmp1 etc. }
end
end

or something like that. I'd be inclined to revisit the instance
variable version, though. I'm at the bottom of the well of mental
energy right now or I'd try to come up with a twist on it that might
seem less coupled....


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
J

Joel VanderWerf

Gavin said:
Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

Use closures and class scopes:

class Foo
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new

define_method :c1 do
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
end

class Foo
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new

define_method :c2 do
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end
 
N

Nobuyoshi Nakada

Hi,
At Thu, 5 Oct 2006 08:18:24 +0900,
Gavin Kistner wrote in [ruby-talk:218056]:
The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

Like this?

$ ruby eo.rb 5
#<Foo::ExpensiveObject: 1>
#<Foo::ExpensiveObject: 2>
#<Foo::ExpensiveObject: 3>
#<Foo::ExpensiveObject: 4>
#<Foo::ExpensiveObject: 5>
[:c1,
#<Foo::ExpensiveObject: 1>,
#<Foo::ExpensiveObject: 2>,
[:c2,
#<Foo::ExpensiveObject: 3>,
#<Foo::ExpensiveObject: 4>,
#<Foo::ExpensiveObject: 5>,
[:c1,
#<Foo::ExpensiveObject: 1>,
#<Foo::ExpensiveObject: 2>,
[:c2,
#<Foo::ExpensiveObject: 3>,
#<Foo::ExpensiveObject: 4>,
#<Foo::ExpensiveObject: 5>,
[:c1, #<Foo::ExpensiveObject: 1>, #<Foo::ExpensiveObject: 2>, []]]]]]

$ cat eo.rb
#!/usr/bin/ruby

module ScratchArgument
def scratch_method(name)
meth = instance_method(name)
define_method(name) do |*args0|
*eo = *yield
imeth = meth.bind(self)
(class << self; self; end).class_eval do
define_method(name) do |*args|
args.unshift(*eo)
imeth.call(*args)
end
end
args0.unshift(*eo)
imeth.call(*args0)
end
end
end

class Foo
extend ScratchArgument

class ExpensiveObject
@@count = 0
def initialize
@id = @@count += 1
p self
end
def inspect
"#<#{self.class.name}: #{@id}>"
end
end

def c1(tmp1, tmp2, i)
if i <= 0
[]
else
[:c1, tmp1, tmp2, c2(i - 1)]
end
end
scratch_method:)c1) do
[ExpensiveObject.new, ExpensiveObject.new]
end

def c2(tmp1, tmp2, tmp3, i)
if i <= 0
[]
else
[:c2, tmp1, tmp2, tmp3, c1(i - 1)]
end
end
scratch_method:)c2) do
[ExpensiveObject.new, ExpensiveObject.new, ExpensiveObject.new]
end
end

result = Foo.new.c1(ARGV.empty? ? 3 : ARGV.first.to_i)
require 'pp'
pp result
 
J

Joel VanderWerf

Joel said:
Use closures and class scopes:

class Foo
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new

Assuming you want to share the tmp1 and tmp2 among all instances of the
class, which probably you don't want. Sorry.
 
A

ara.t.howard

Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object instances
with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods. Each method needs a couple 'scratch'
objects to perform its calculations. The methods call each other; they must
not use the same scratch objects. It is expensive to instantiate a scratch
object. It's not expensive to initialize an existing scratch object with
data. We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end


The Solution We'll Ignore
--------------------------
We *could* create a distinct instance variable for each needed variable.
I personally don't like the weird coupling this creates, though. So I'm
ignoring it.

class Foo
def initialize
@tmp1 = ExpensiveObject.new
@tmp2 = ExpensiveObject.new
@tmp3 = ExpensiveObject.new
@tmp4 = ExpensiveObject.new
end
def c1
tmp1, tmp2 = @tmp1, @tmp2
#...
end
def c2
tmp1, tmp2 = @tmp3, @tmp4
#...
end
end


what's wrong with the simple solution:

class Foo
METHOD_DATA = {
'c1' => {
'tmp1' => ExpensiveObject.new,
'tmp2' => ExpensiveObject.new,
},

'c2' => {
'tmp1' => ExpensiveObject.new,
'tmp2' => ExpensiveObject.new,
},
}

def mdata m, key
METHOD_DATA[m.to_s][key.to_s]
end

def c1
tmp1, tmp2 = mdata('c1', 'tmp1'), mdata('c1', 'tmp2')
result = tmp1 + tmp2 + c2
end

def c2
tmp1, tmp2 = mdata('c2', 'tmp1'), mdata('c2', 'tmp2')
result = tmp1 + tmp2
end
end

you can make it lazy using lambda if you want...

??

cheers.


-a
 
R

Robert Klemme

Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

I can think of several solutions.

1. Use command pattern, i.e., distribute method c1 and c2 to different
classes (i.e. a class per method), make your local variables instance
variables and reuse instances of these command classes. If you have
common behavior or state you can even use a common base class or a Module.

2. Use factory with a pool to create and release objects whose creation
is expensive. The factory can reside at class level. Gotcha: if you do
multithreading you have to make sure the factory deals properly with that.

Note, both approaches work with pretty much every OO language and do not
need any kind of fancy environment changes or closures.

Kind regards

robert
 
D

dblack

Hi --

class Foo
def initialize
tmp1, tmp2 = EO.new, EO.new
define_method("c1") { do stuff with tmp1 etc. }
tmp1, tmp2 = EO.new, EO.new
define_method("c2") { do stuff with tmp1 etc. }
end
end

or something like that. I'd be inclined to revisit the instance
variable version, though. I'm at the bottom of the well of mental
energy right now or I'd try to come up with a twist on it that might
seem less coupled....

See Joel's response for how to do this correctly :) (i.e., calling
define_method in top-level class definition scope, not in an instance
method where it won't have any meaning)


David

--
David A. Black | (e-mail address removed)
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
 
T

Trans

Gavin said:
Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

I look at this and think, "where the persistant locals?" Well, we don't
have em but they can be emulatated via instance vars.

class Foo
def c1
tmp1 = (@_tmp1 ||= ExpensiveObject.new)
tmp2 = (@_tmp2 ||= ExpensiveObject.new)
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = (@_tmp1 ||= ExpensiveObject.new)
tmp2 = (@_tmp2 ||= ExpensiveObject.new)
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

T.
 
P

Phrogz

what's wrong with the simple solution:

class Foo
METHOD_DATA = {
'c1' => {
'tmp1' => ExpensiveObject.new,
'tmp2' => ExpensiveObject.new,
},

'c2' => {
'tmp1' => ExpensiveObject.new,
'tmp2' => ExpensiveObject.new,
},
}

It's certainly effective, and slightly nicer than instance variables
since it clearly describes which methods the variables go with. I'm not
wild about it just because it means that when you edit the method you
need to remember to go edit a data structure somewhere else.

(This is one reason that makes a separately-managed pool much nicer
than closure or other solutions where you must instantiate the objects
outside of the method itself.)

A viable alternative nonetheless.
 
P

Phrogz

Trans said:
I look at this and think, "where the persistant locals?" Well, we don't
have em but they can be emulatated via instance vars.

class Foo
def c1
tmp1 = (@_tmp1 ||= ExpensiveObject.new)
tmp2 = (@_tmp2 ||= ExpensiveObject.new)
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = (@_tmp1 ||= ExpensiveObject.new)
tmp2 = (@_tmp2 ||= ExpensiveObject.new)
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

Persistent locals is an interesting concept. Your specific example
fails, however, if I change c1's last line to:
result = tmp1 * c2 + tmp2
because calculating c2 will change the value in @_tmp2, making the tmp2
in c1 invalid. You'd need to ensure that each instance variable had a
unique name in each function, which is just about the same as declaring
all the instance variables during initialization or class creation.
 
P

Phrogz

Nobuyoshi said:
module ScratchArgument
def scratch_method(name)
meth = instance_method(name)
define_method(name) do |*args0|
*eo = *yield
imeth = meth.bind(self)
(class << self; self; end).class_eval do
define_method(name) do |*args|
args.unshift(*eo)
imeth.call(*args)
end
end
args0.unshift(*eo)
imeth.call(*args0)
end
end
end

Wow, that's pretty crazy, Nobu! Thanks for the brain bending. :)
 
T

Trans

Phrogz said:
Persistent locals is an interesting concept. Your specific example
fails, however, if I change c1's last line to:
result = tmp1 * c2 + tmp2
because calculating c2 will change the value in @_tmp2, making the tmp2
in c1 invalid. You'd need to ensure that each instance variable had a
unique name in each function, which is just about the same as declaring
all the instance variables during initialization or class creation.

Right, they should have had differnt names.

T.
 
T

Trans

Gavin said:
Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Wasteful Example:
class Foo
def c1
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2 + c2
end
def c2
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
# stuff involving tmp1/tmp2
result = tmp1 + tmp2
end
end

Hmm then again....

require 'facet/annotation'

class Foo

ann :c1, :tmp1 => ExpensiveObject.new,
:tmp2 => ExpensiveObject.new

def c1
result = ann.c1.tmp1 + ann.c1.tmp2 + c2
end

ann :c2, :tmp1 => ExpensiveObject.new,
:tmp2 => ExpensiveObject.new

def c2
result = ann.c2.tmp1 + ann.c2.tmp2
end

end

Works as long ExpensiveObject isn't dependent on instance context.
(Note: If anyone actually tries this it may need to use:
'self.class.ann.c1.tmp1' instead, although it ought to work as given.)

T.
 
L

Logan Capaldo

Because I just had to solve this problem in both JavaScript and Lua, and
because I love Ruby more than either, I thought I'd ask people here how
they'd solve it in Ruby.

The general question is: how would do you associate a few object
instances with methods on a per-method basis?

The Situation/Requirements
--------------------------
A class has 2 different methods.
Each method needs a couple 'scratch' objects to perform its
calculations.
The methods call each other; they must not use the same scratch objects.
It is expensive to instantiate a scratch object.
It's not expensive to initialize an existing scratch object with data.
We like OOP coding, and want to call these as methods of a receiver.

Lua Solution #1: Closures (would work for JS, too)
--------------------------------------------------
Foo.c1 = ( function( )
local tmp1 = ExpensiveObject:new( )
local tmp2 = ExpensiveObject:new( )
return function( self )
-- stuff using tmp1 and tmp2
end
end)( )
Can't you do the same here?

class A
tmp1 = ExpensiveObject.new
tmp2 = ExpensiveObject.new
define_method:)c1) do # Look ma! a closure
# stuff using tmp1 and tmp2
end
end
 

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,780
Messages
2,569,608
Members
45,241
Latest member
Lisa1997

Latest Threads

Top